Skip to content
Merged
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
3 changes: 3 additions & 0 deletions baml_language/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ This changelog covers the independent `baml_language` release line. It does not

### Fixes

- Extended the inline `unreflect(value)` escape rule (E0168) to the two published types it did not read: the error a callee can throw — whether the `throws` clause was written or inferred — and an optional-chained call, where `?.` republishes the call's result as a nullable. `risky<unreflect(t)>(v)` on a callee that can throw `Boom<T>`, and `x?.m<unreflect(t)>()` on a nullable receiver whose callee returns `T`, are refused with the same suggestion to name the type first (`type Out = unreflect(value)`), and a slot that escapes through more than one published type is reported once. Both arms read the published type, so an error typed by the parameter itself (`throws T`), a chained call whose result never mentions it (`-> bool`, `-> Wrapper<unknown>`), a call in argument position inside a chain, and a `?.` link whose receiver is not nullable are all unaffected, as is every shape #4518 accepted. ([#4530](https://github.com/BoundaryML/baml/pull/4530)) - Antonio Sarosi
- Fixed a compiler abort (`type variable not found in type args`) on the rewrite E0168 suggests: a `type Out = unreflect(value)` binding used on a callee that can throw a type mentioning its parameter left that block-scoped name in the caller's inferred `throws`, which reached lowering with nothing to bind it to. The effect a block publishes is now erased like its value, so such a caller compiles and publishes `Boom<unknown>`, and E0096 quotes that instead of the internal name. ([#4530](https://github.com/BoundaryML/baml/pull/4530)) - Antonio Sarosi
- Extended the inline `unreflect(value)` escape rule (E0168) to the two published types it did not read: the error a callee can throw — whether the `throws` clause was written or inferred — and an optional-chained call, where `?.` republishes the result as a nullable. `risky<unreflect(t)>(v)` on a callee that can throw `Boom<T>`, and `x?.m<unreflect(t)>()` on a nullable receiver, are refused with the same suggestion to name the type first (`type Out = unreflect(value)`), and a slot that escapes through more than one published type is reported once. An error typed by the parameter itself (`throws T`), a call in argument position inside a chain, and a `?.` link whose receiver is not nullable are unaffected, as is every shape #4518 accepted. ([#4530](https://github.com/BoundaryML/baml/pull/4530)) - Antonio Sarosi
- Made an assignment inside a `reflect.Session` type-check against its binding, the way ordinary code does: `s.eval("let n = 5")` followed by `s.eval("n = \"seven\"")` is now the same compile error BAML gives anywhere else, instead of rebinding at another type and failing later in the VM with an uncatchable internal error. Compound assignments are checked through the ordinary road too. Re-declaring with `let` at a new type stays legal — it is a new binding, exactly as shadowing works in ordinary code — and assignments at the binding's own type are unchanged. One previously accidental case changes with it: `n += 1.5` on an `int` binding used to commit the float and read back `6.5`, and now compiles and fails at runtime exactly as the same line does in ordinary BAML. ([#4531](https://github.com/BoundaryML/baml/pull/4531)) - Antonio Sarosi
- Made a `let` binding that lives in a global — a Session's top-level bindings and every `client` declaration — behave like an ordinary binding. A method call on one now reaches its receiver instead of failing with an internal `got any` error: `MyClient.id()` in ordinary BAML, and `n.abs()`, `text.to_upper_case()`, `v.length()`, `m.keys()`, a session class's own method and a reflection handle's `fields()` inside a Session. Field access and indexing on such a binding are unchanged. Separately, a Session's `let` now widens a fresh literal initializer to its base type, so `s.eval("let n = 5")` binds `int` rather than the literal type `5`. Two consequences of that are deliberate and visible: a match covering only the literal is now non-exhaustive (and a complete `true`/`false` match on a bool binding is now legal), and an eval contract naming the literal type (`s.eval<5>("n")`) no longer accepts the widened binding. Interface-dispatched methods on such a binding (`n.compare(m)`) reach a separate pre-existing defect and fail with an internal error where they previously failed to compile. ([#4529](https://github.com/BoundaryML/baml/pull/4529)) - Antonio Sarosi
- Carried a constructed type's identity through interface dispatch: `type.of<T>()` inside an implements-block or inherited default method is now the exact `type` value the caller minted for a `reflect.class.new` or `reflect.enum.new` declaration, instead of an `==`-distinct re-derivation of the same type. Identity-keyed patterns — a registry keyed by type, a comparison against a stored type — match inside an impl body instead of silently missing. Statically instantiated type parameters, derived types (`t.array()`, `type.meta(…)`), and declarations from a compiled `reflect.Package` keep re-deriving their value, so they are unaffected. ([#4516](https://github.com/BoundaryML/baml/pull/4516)) - Antonio Sarosi
Expand Down
16 changes: 9 additions & 7 deletions baml_language/crates/baml_compiler2_hir_ty/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::fmt;

use baml_base::{FileId, Name, SourceFile};
use baml_compiler_diagnostics::runtime_type::{
RuntimeTypeNameRewrite, runtime_type_must_be_named_help,
RuntimeTypeEscape, RuntimeTypeNameRewrite, runtime_type_must_be_named_help,
};
use baml_compiler2_ast::{AstSourceMap, ExprId, StmtId, TypeAnnotId};
use baml_compiler2_hir::{
Expand Down Expand Up @@ -137,11 +137,13 @@ pub enum TirTypeError {
/// slots require the whole-slot `unreflect(value)` marker.
ComputedGenericArgumentRequiresUnreflect { name: Name },
/// An inline `unreflect(value)` type argument would escape its call: the
/// runtime parameter is rigid for that one call, but the callee's result
/// type still mentions it, so the value the call produces would carry a
/// name that no longer means anything. The lexical `type T = unreflect(v)`
/// binding is the spelling that outlives a call.
RuntimeTypeMustBeNamed,
/// runtime parameter is rigid for that one call, but a type the call
/// publishes still mentions it, so the value or error that comes back
/// would carry a name that no longer means anything. The lexical
/// `type T = unreflect(v)` binding is the spelling that outlives a call.
/// `escape` picks the note; the headline and the fix are the same either
/// way.
RuntimeTypeMustBeNamed { escape: RuntimeTypeEscape },
/// A mounted callable whose implementation is compiler-owned and has no
/// location-free link ABI was invoked from a source-less consumer.
MountedPackageCallUnsupported { path: Name },
Expand Down Expand Up @@ -892,7 +894,7 @@ impl fmt::Display for TirTypeError {
);
f.write_str(diagnostic.message.as_str())
}
TirTypeError::RuntimeTypeMustBeNamed => {
TirTypeError::RuntimeTypeMustBeNamed { .. } => {
let diagnostic =
baml_compiler_diagnostics::runtime_type::runtime_type_must_be_named();
f.write_str(diagnostic.message.as_str())
Expand Down
Loading
Loading