Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@
"outFiles": [
"${workspaceFolder}/typescript/apps/vscode-ext/dist/__test__/**/*.js"
]
},
{
"name": "Debug baml-cli --check (direct)",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/engine/target/debug/baml-cli",
"args": ["check", "--from", "../integ-tests/"],
"cwd": "${workspaceFolder}/engine",
"env": { "RUST_LOG": "baml_cli=debug" },
"preLaunchTask": "build baml-cli",
"sourceLanguages": ["rust"]
}
],
"compounds": [
Expand Down
8 changes: 8 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@
"endsPattern": "."
}
}
},
{
"label": "build baml-cli",
"type": "shell",
"command": "cargo",
"args": ["build", "--bin", "baml-cli"],
"options": { "cwd": "${workspaceFolder}/engine" },
"problemMatcher": ["$rustc"]
}
],
"inputs": [
Expand Down
13 changes: 10 additions & 3 deletions engine/baml-compiler/src/thir/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
///
/// However, the current implementation is simple and ad-hoc, likely wrong
/// in several places. Bidirectional typing is the target.
use std::{borrow::Cow, sync::Arc};
use std::{borrow::Cow, str::FromStr, sync::Arc};

use baml_types::{
ir_type::{ArrowGeneric, TypeIR},
BamlMap, BamlMediaType, BamlValueWithMeta, TypeValue,
};
use baml_vm::types::Type;
use internal_baml_ast::ast::WithSpan;
use internal_baml_diagnostics::{DatamodelError, Diagnostics, Span};

Expand Down Expand Up @@ -1371,6 +1372,7 @@ pub fn typecheck_expression(
"image" | "audio" | "video" | "pdf" | "baml" => {}

cls if context.classes.contains_key(cls) => {}
p if TypeValue::from_str(p).is_ok() => {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correctness: TypeValue::from_str(p).is_ok() accepts any string that parses as a primitive or enum, but does not check if the type is actually defined in the current context, potentially allowing invalid types to pass silently.

🤖 AI Agent Prompt for Cursor/Windsurf

📋 Copy this prompt to your AI coding assistant (Cursor, Windsurf, etc.) to get help fixing this issue

In engine/baml-compiler/src/thir/typecheck.rs, line 1360, the current check `TypeValue::from_str(p).is_ok()` allows any string that parses as a primitive or enum, even if the type is not defined in the current context. Update this line to only allow primitive types or enums that are actually defined in `context.enums` or are valid primitives. Replace the line with: `p if TypeValue::from_str(p).is_ok() && (context.enums.contains_key(p) || matches!(TypeValue::from_str(p), Ok(TypeValue::Primitive(_)))) => {}`.
📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
p if TypeValue::from_str(p).is_ok() => {}
p if TypeValue::from_str(p).is_ok() && (context.enums.contains_key(p) || matches!(TypeValue::from_str(p), Ok(TypeValue::Primitive(_)))) => {}


_ => {
diagnostics.push_error(DatamodelError::new_validation_error(
Expand Down Expand Up @@ -2487,17 +2489,22 @@ pub fn typecheck_expression(
thir::Expr::Var(name, _) => {
if context.classes.get(name).is_some() {
Some(TypeIR::bool())
} else if context.enums.get(name).is_some() {
Some(TypeIR::bool())
} else if TypeValue::from_str(name).is_ok() {
Some(TypeIR::bool())
} else {
// TODO: Check type aliases (may be recursive)
diagnostics.push_error(DatamodelError::new_validation_error(
&format!("Class {name} not found"),
&format!("Type {name} not found"),
span.clone(),
));
None
}
}
_ => {
diagnostics.push_error(DatamodelError::new_validation_error(
"Invalid binary operation (instanceof): right operand must be a class",
"Invalid binary operation (instanceof): right operand must be a type",
span.clone(),
));
None
Expand Down
18 changes: 18 additions & 0 deletions integ-tests/baml_src/test-files/vm/expr_funcs.baml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ class DummyJsonTodo {
userId int
}

enum DummyEnum {
POSITIVE
NEGATIVE
NEUTRAL
}

function ExecFetchAs(url: string) -> DummyJsonTodo {
let todo = baml.fetch_as<DummyJsonTodo>(url);

Expand All @@ -165,3 +171,15 @@ test Fib5() {
args { n 5 }
@@assert( {{ this == 5 }} )
}

function InstanceofWithPrimitives(n: int) -> bool {
n instanceof int
}

function InstanceofWithClasses(dummy: DummyJsonTodo) -> bool {
dummy instanceof DummyJsonTodo
}

function InstanceofWithEnums(e: DummyEnum) -> bool {
e instanceof DummyEnum
}