Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
76d464e
baml_language: add throws field to function type representations
rossirpaulo Apr 8, 2026
a0af77c
baml_language: wire throws into function signature construction
rossirpaulo Apr 8, 2026
1f64000
baml_language: finalize typed rethrows for higher-order functions
rossirpaulo Apr 8, 2026
9608710
baml_language: add stored callback enforcement for typed rethrows
rossirpaulo Apr 8, 2026
4282b74
baml_language: add stdlib migration and throws regression coverage
rossirpaulo Apr 8, 2026
84d09cb
Refine typed throws semantics for higher-order functions
rossirpaulo Apr 8, 2026
bab3306
Fix function-type throws validation and optional-call propagation
rossirpaulo Apr 8, 2026
e45b28d
Fix lambda generic scope and optional function-shape matching
rossirpaulo Apr 8, 2026
32fac73
Clean up lambda throws diagnostics and parenthesize optional function…
rossirpaulo Apr 8, 2026
cef2fc8
Update MIR and codegen snapshots for function throws display
rossirpaulo Apr 8, 2026
be22d2d
Update test expectations for typed rethrows changes
rossirpaulo Apr 8, 2026
a5d14b8
Extract callable_boundary module and tighten effect-polymorphic rethrows
rossirpaulo Apr 9, 2026
8845cd3
Complete named callable throws boundary cleanup
rossirpaulo Apr 9, 2026
370e2d1
Merge branch 'canary' into rossirpaulo/typed-rethrows-hof
rossirpaulo Apr 9, 2026
73cdd0f
Refresh function_type_throws MIR and codegen snapshots
rossirpaulo Apr 9, 2026
b640a40
Add UnusedCallbackEffectVar diagnostic for unused callback effect vars
rossirpaulo Apr 9, 2026
aec818c
Align TIR display with actual throws behavior
rossirpaulo Apr 9, 2026
58cbcbe
Add regression tests for generic stored callbacks and heterogeneous c…
rossirpaulo Apr 9, 2026
5333f74
Add generic nominal instantiation soundness and cross-package resolution
rossirpaulo Apr 9, 2026
53b47b8
Propagate named-function throws through typed member field calls
rossirpaulo Apr 9, 2026
1bb6e8b
Render generic args in LSP hover and type display
rossirpaulo Apr 9, 2026
bc521f0
Refresh TIR snapshots for StructuralTy type_args shape change
rossirpaulo Apr 9, 2026
16f2a56
Show declared and inferred throws in LSP function hover
rossirpaulo Apr 9, 2026
0795fdb
Refactor throws propagation and callable resolution
rossirpaulo Apr 9, 2026
7be9e57
Refine package resolution context and throws recovery
rossirpaulo Apr 10, 2026
7327986
Expand throws regression coverage
rossirpaulo Apr 10, 2026
2d58aee
Refine typed throws inference for higher-order callbacks
rossirpaulo Apr 16, 2026
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
12 changes: 10 additions & 2 deletions baml_language/crates/baml_builtins2/baml_std/baml/containers.baml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,18 @@ class Map<K, V> {
}

function map_keys<U>(self, f: (K) -> U) -> U[] {
self.keys().map(f)
let result: U[] = []
for (let key in self.keys()) {
result.push(f(key))
}
result
}

function map_values<U>(self, f: (V) -> U) -> U[] {
self.values().map(f)
let result: U[] = []
for (let value in self.values()) {
result.push(f(value))
}
result
}
}
24 changes: 11 additions & 13 deletions baml_language/crates/baml_builtins2/baml_std/testing/registry.baml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

class TestRegistration {
name string
body () -> null
body () -> null throws unknown
runner TestRunner?
}

class TestSetRegistration {
name string
collector (TestCollector) -> null
collector (TestCollector) -> null throws unknown
runner TestSetRunner?
}

Expand All @@ -23,7 +23,7 @@ class TestCollector {
TestCollector { prefix: prefix, tests: [], testsets: [] }
}

function register_test(self, name: string, body: () -> null, runner: TestRunner?) -> null {
function register_test(self, name: string, body: () -> null throws unknown, runner: TestRunner?) -> null {
let full_name = if (self.prefix == "") { name } else { self.prefix + "/" + name }
let count = 0
let hash_prefix = full_name + "#"
Expand All @@ -35,7 +35,7 @@ class TestCollector {
null
}

function register_test_set(self, name: string, collector: (TestCollector) -> null, runner: TestSetRunner?) -> null {
function register_test_set(self, name: string, collector: (TestCollector) -> null throws unknown, runner: TestSetRunner?) -> null {
let full_name = if (self.prefix == "") { name } else { self.prefix + "/" + name }
let count = 0
let hash_prefix = full_name + "#"
Expand Down Expand Up @@ -171,7 +171,7 @@ class TestRegistry {
}

// Run a single test with error handling and optional runner middleware.
function run_test(body: () -> null, runner: TestRunner?) -> TestReport {
function run_test(body: () -> null throws unknown, runner: TestRunner?) -> TestReport {
let base_run = () -> TestReport {
let start = baml.sys.now_ms()
let result = {
Expand All @@ -187,18 +187,16 @@ function run_test(body: () -> null, runner: TestRunner?) -> TestReport {
runs: [RunReport { outcome: result.outcome, duration_ms: baml.sys.now_ms() - start }],
}
}
let effective_run = match (runner) {
null => base_run,
r: TestRunner => r(base_run)
match (runner) {
null => base_run(),
r: TestRunner => r(base_run)()
}
effective_run()
}

// Run all children of a testset with optional runner middleware.
function run_testset(run_children: () -> TestSetReport, runner: TestSetRunner?) -> TestSetReport {
let effective_run = match (runner) {
null => run_children,
r: TestSetRunner => r(run_children)
match (runner) {
null => run_children(),
r: TestSetRunner => r(run_children)()
}
effective_run()
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type ChildReport = TestReport | TestSetReport

// Runner type aliases — lambda transformers.
// TestRunner wraps "run test once" into a new execution lambda.
type TestRunner = (() -> TestReport) -> () -> TestReport
type TestRunner = (() -> TestReport throws unknown) -> (() -> TestReport throws unknown)

// TestSetRunner wraps "run all children" into a new execution lambda.
type TestSetRunner = (() -> TestSetReport) -> () -> TestSetReport
type TestSetRunner = (() -> TestSetReport throws unknown) -> (() -> TestSetReport throws unknown)
5 changes: 4 additions & 1 deletion baml_language/crates/baml_compiler2_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum TypeExpr {
/// Named type path: `User`, `baml.http.Request`
Path {
segments: Vec<Name>,
type_args: Vec<TypeExpr>,
attrs: Vec<RawAttribute>,
},
/// Primitive types
Expand Down Expand Up @@ -97,10 +98,11 @@ pub enum TypeExpr {
value: baml_base::Literal,
attrs: Vec<RawAttribute>,
},
/// Function type: (params) -> return
/// Function type: (params) -> return [throws E]
Function {
params: Vec<FunctionTypeParam>,
ret: Box<TypeExpr>,
throws: Option<Box<TypeExpr>>,
attrs: Vec<RawAttribute>,
},
/// The `unknown` keyword type
Expand Down Expand Up @@ -445,6 +447,7 @@ pub enum Expr {
},
Object {
type_name: Option<Name>,
type_args: Vec<TypeExpr>,
fields: Vec<(Name, ExprId)>,
spreads: Vec<SpreadField>,
},
Expand Down
1 change: 1 addition & 0 deletions baml_language/crates/baml_compiler2_ast/src/companions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ fn make_llm_companion(
let return_type = SpannedTypeExpr {
expr: TypeExpr::Path {
segments: return_type_path.iter().map(Name::new).collect(),
type_args: vec![],
attrs: vec![],
},
span: parent.span,
Expand Down
10 changes: 9 additions & 1 deletion baml_language/crates/baml_compiler2_ast/src/disambiguate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,19 @@ fn validate_type_expr_tree(expr: &TypeExpr, diagnostics: &mut Vec<(String, text_
validate_type_expr_tree(v, diagnostics);
}
}
TypeExpr::Function { params, ret, .. } => {
TypeExpr::Function {
params,
ret,
throws,
..
} => {
for p in params {
validate_type_expr_tree(&p.ty, diagnostics);
}
validate_type_expr_tree(ret, diagnostics);
if let Some(throws) = throws {
validate_type_expr_tree(throws, diagnostics);
}
}
_ => {}
}
Expand Down
18 changes: 16 additions & 2 deletions baml_language/crates/baml_compiler2_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ mod tests {
(Path($name:expr $(, Attr($a:expr))*)) => {
TypeExpr::Path {
segments: vec![baml_base::Name::new($name)],
type_args: vec![],
attrs: type_expr!(@attrs $(, Attr($a))*),
}
};
Expand Down Expand Up @@ -153,8 +154,13 @@ mod tests {
TypeExpr::Rust { attrs } => TypeExpr::Rust {
attrs: strip_attrs(attrs),
},
TypeExpr::Path { segments, attrs } => TypeExpr::Path {
TypeExpr::Path {
segments,
type_args,
attrs,
} => TypeExpr::Path {
segments: segments.clone(),
type_args: type_args.iter().map(strip_spans).collect(),
attrs: strip_attrs(attrs),
},
TypeExpr::Optional { inner, attrs } => TypeExpr::Optional {
Expand All @@ -178,7 +184,12 @@ mod tests {
value: value.clone(),
attrs: strip_attrs(attrs),
},
TypeExpr::Function { params, ret, attrs } => TypeExpr::Function {
TypeExpr::Function {
params,
ret,
throws,
attrs,
} => TypeExpr::Function {
params: params
.iter()
.map(|p| crate::ast::FunctionTypeParam {
Expand All @@ -187,6 +198,7 @@ mod tests {
})
.collect(),
ret: Box::new(strip_spans(ret)),
throws: throws.as_ref().map(|t| Box::new(strip_spans(t))),
attrs: strip_attrs(attrs),
},
TypeExpr::Media { kind, attrs } => TypeExpr::Media {
Expand Down Expand Up @@ -286,6 +298,7 @@ class Response {
baml_base::Name::new("errors"),
baml_base::Name::new("Io"),
],
type_args: vec![],
attrs: vec![]
}
);
Expand Down Expand Up @@ -966,6 +979,7 @@ retry_policy MyRetry {
let (type_name, fields, _) = match root_expr {
Expr::Object {
type_name,
type_args: _,
fields,
spreads,
} => (type_name, fields, spreads),
Expand Down
8 changes: 8 additions & 0 deletions baml_language/crates/baml_compiler2_ast/src/lower_cst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ pub(crate) fn synthesize_llm_builtin_call(
let counter = alloc(Expr::Literal(Literal::Int(0)));
alloc(Expr::Object {
type_name: Some(Name::new("baml.llm.Client")),
type_args: vec![],
fields: vec![
(Name::new("name"), name_lit),
(Name::new("client_type"), ct_variant),
Expand Down Expand Up @@ -953,6 +954,7 @@ fn synthesize_init_test_function(
type_expr: Some(SpannedTypeExpr {
expr: crate::ast::TypeExpr::Path {
segments: vec![Name::new("testing"), Name::new("TestCollector")],
type_args: vec![],
attrs: vec![],
},
span,
Expand Down Expand Up @@ -1053,6 +1055,7 @@ fn synthesize_register_call(
type_expr: Some(SpannedTypeExpr {
expr: crate::ast::TypeExpr::Path {
segments: vec![Name::new("testing"), Name::new("TestCollector")],
type_args: vec![],
attrs: vec![],
},
span,
Expand Down Expand Up @@ -1229,6 +1232,7 @@ fn synthesize_retry_policy_let(

let root = alloc(Expr::Object {
type_name: Some(Name::new("RetryPolicy")),
type_args: vec![],
fields,
spreads: vec![],
});
Expand Down Expand Up @@ -1466,6 +1470,7 @@ fn synthesize_client_let(
// Client { name, client_type, sub_clients, retry, counter }
let root = alloc(Expr::Object {
type_name: Some(Name::new("Client")),
type_args: vec![],
fields: vec![
(Name::new("name"), name_expr),
(Name::new("client_type"), client_type_expr),
Expand Down Expand Up @@ -1677,6 +1682,7 @@ fn synthesize_client_new_companion(
if !provider_fields_set.is_empty() {
alloc(Expr::Object {
type_name: Some(Name::new(type_name)),
type_args: vec![],
fields: prov_fields,
spreads: vec![],
})
Expand Down Expand Up @@ -1716,6 +1722,7 @@ fn synthesize_client_new_companion(

let options_expr = alloc(Expr::Object {
type_name: Some(Name::new("baml.llm.PrimitiveClientOptions")),
type_args: vec![],
fields: options_fields,
spreads: vec![],
});
Expand All @@ -1727,6 +1734,7 @@ fn synthesize_client_new_companion(
)));
let root = alloc(Expr::Object {
type_name: Some(Name::new("baml.llm.PrimitiveClient")),
type_args: vec![],
fields: vec![
(Name::new("name"), name_lit),
(Name::new("provider"), provider_lit),
Expand Down
17 changes: 17 additions & 0 deletions baml_language/crates/baml_compiler2_ast/src/lower_expr_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,7 @@ impl LoweringContext {
name,
ty: crate::ast::TypeExpr::Path {
segments: vec![Name::new(&text)],
type_args: vec![],
attrs: vec![],
},
};
Expand Down Expand Up @@ -1990,6 +1991,7 @@ impl LoweringContext {
let mut spreads = Vec::new();
let mut position = 0;
let mut type_name = None;
let mut type_args = Vec::new();

// Look for the optional type name (first WORD or path before the brace).
// The type name may be:
Expand Down Expand Up @@ -2021,6 +2023,19 @@ impl LoweringContext {
if let Some(name) = last_word {
type_name = Some(name);
}
type_args = child_node
.children()
.find(|n| n.kind() == SyntaxKind::GENERIC_ARGS)
.into_iter()
.flat_map(|args_node| {
args_node
.children()
.filter(|n| n.kind() == SyntaxKind::TYPE_EXPR)
.filter_map(baml_compiler_syntax::ast::TypeExpr::cast)
.map(|arg| crate::lower_type_expr::lower_type_expr_node(&arg))
.collect::<Vec<_>>()
})
.collect();
// After handling the path node, stop scanning for more pre-brace items.
break 'outer;
}
Expand Down Expand Up @@ -2086,6 +2101,7 @@ impl LoweringContext {
self.alloc_expr(
Expr::Object {
type_name,
type_args,
fields,
spreads,
},
Expand Down Expand Up @@ -2840,6 +2856,7 @@ impl LoweringContext {
type_expr: Some(SpannedTypeExpr {
expr: TypeExpr::Path {
segments: vec![Name::new("testing"), Name::new("TestCollector")],
type_args: vec![],
attrs: vec![],
},
span,
Expand Down
Loading