Latest Results
Make baml.toml opt-in for the BAML CLI (#3676)
## Why
The most expensive thing an agent can do is try a command and have it
fail fast. Previously every project command bailed with `doesn't look
like a BAML project` unless the exact directory contained a `baml.toml`
— so `baml describe baml.String` couldn't even read the stdlib without a
project, and `baml run`/`test`/`generate`/`pack` refused to run a plain
`baml_src/` directory.
This makes **`baml.toml` opt-in**: it's only needed when you actually
use one of its features (dependencies, version lock, `[scripts]`,
multiple packages). A bare `baml_src/` is a complete project.
## Behavior
| Command | No project at all | `baml_src/` only, no `baml.toml` |
|---|---|---|
| `describe` / `grep` | ✅ stdlib-only default state (walks up for an
ancestor `baml.toml`) | ✅ |
| `fmt` | ✅ no-op success | ✅ |
| `run` / `test` / `generate` / `pack` | ❌ error → hints `baml_src/`,
`baml init`, `--file` | ✅ |
| `run --file` / `pack --file` | ✅ hermetic (unchanged) | ✅ |
Key points:
- **describe / grep** use a lenient loader: walk ancestors for a
`baml.toml`; if none, fall back to a stdlib-only database (the `baml.*`
stdlib loads regardless of user files, so `baml describe baml.String`
works in any directory with zero user files).
- **fmt** returns a no-op success when there's no project marker,
instead of erroring. It deliberately does **not** walk up, since it
mutates files.
- **run / test / generate / pack** now treat a directory as a project if
it has *either* a `baml.toml` *or* a `baml_src/`. A manifest-less
project loads sources **only** from `baml_src/`, so discovery stays
scoped and can never slurp an unmarked tree — the workspace-shaped salsa
hang the old hard requirement guarded against. `run -e` picks up a
`baml_src/`-only project's context too.
- **pack** names the artifact after `[package].name` when a manifest is
present, else the project directory name (Cargo-style); `--file` still
uses the file stem.
- A present `baml.toml` is still validated (`[package].name` required).
The only remaining hard failure is a directory with neither marker.
## Slurp-hang invariant
A whole-root walk only happens for a **declared** project (a `baml.toml`
present with no `baml_src/`) — intentional, matching what `run`/`test`
already did. Every manifest-less path is scoped to `baml_src/` and
provably cannot slurp.
## Testing
`cargo nextest run -p baml_cli --no-default-features --features
ring-crypto` → **195 passing**. Added:
- Unit: loader decision table (all four marker cases), walk-up, stdlib
default state, `resolve_project_name` dir-name fallback.
- E2E: project-less `describe`/`grep`/`fmt`; manifest-less `run <fn>`
(executes), `run -e`, `run --list`, `test` (→ NoTestsRun), `generate`,
and `pack` (builds + runs the binary).
fmt + clippy `--all-targets` clean.
## Open question
A `baml.toml` present but **without** `[package].name` (e.g. a manifest
added *only* for `[scripts]`/`[dependencies]`) is still rejected —
Cargo-consistent, but mildly in tension with "only needed when you use a
feature," since `pack` can now fall back to the directory name. Left
strict for now; easy to relax if we'd prefer.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Projects may be defined by a baml_src/ directory alone (no baml.toml
required).
* CLI read-only commands (describe, grep, fmt) operate in a stdlib-only
default when no project is present.
* **Improvements**
* Formatter is a no-op outside projects and prints a friendly message on
success.
* Packaging: clearer output basename rules and stricter handling of
nameless file paths.
* **Tests**
* New end-to-end and unit tests cover manifest-less and baml_src-only
scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai --> feat: generic instantiation expressions (`let f = foo<int>`) (#3640)
## Summary
Adds **generic instantiation expressions** — referencing a generic
callable with explicit type arguments as a *value* (not a call):
```baml
function foo<T>(x: T) -> T { x }
let f = foo<int> // typed (int) -> int; f("s") is a type error
foo<int>(x) // (call form, already supported)
```
Previously `foo<int>` in value position silently dropped the type args
(parsed as a `<` comparison or lowered to a fully-generic ref), so `let
f = foo<int>; f("s")` was wrongly accepted.
## Behavior
```baml
let f = foo<int> // (int) -> int
let g = foo // still <T>(x: T) -> T (bare ref unchanged)
foo<int> === foo<int> // true (interned, pointer-stable)
foo<int> !== foo<string> // distinct specializations
let f = baml.json.from_string<User>; f(s) // works (T=User reified at runtime)
let g = describe<User>; g() // reflect.type_of<T> -> "User"
function fwd<T>() { let g = describe<T>; g() } // param-dependent: resolves T at runtime
```
Plus enforcement and lowering fixes that fall out of this:
- **BEP-044 generic bounds** are now enforced on instantiation *values*,
not only at call sites.
- Generic calls on **non-`PATH` receivers** (`(b).foo<int>`,
`arr[0].foo<int>`, `self.subs[i].m<T>(...)`) previously lowered to
`<missing>`; now lowered correctly (this fixed two latent std-lib
`<missing>` bugs).
## Implementation
**Front-end**
- Parser: ports typescript-go's `canFollowTypeArgumentsInExpression` to
disambiguate `<…>` in value position vs. a `<` comparison (reuses the
existing balanced-`>` scan).
- AST: new `Expr::GenericApply { base, type_args }`; lowering captures
`GENERIC_ARGS` (with a consumed-args marker so `foo<int>(x)` doesn't
double-count).
- TIR: specializes the signature and **clears `generic_params`**, so a
later call checks args against the concrete params.
**Runtime** — a generic-function value carries its type args so an
indirect call behaves exactly like the direct `foo<int>(...)`:
- New `Object::GenericFunction { function: GlobalIndex, type_args }`.
Stored by global slot (not `HeapPtr`), so it's a compile-time-poolable,
GC-leaf value.
- **Concrete** instantiations → a pooled, interned
`Constant::GenericFunction` (dedup by `(function, type_args)` → one
object → pointer-stable identity).
- **Param-dependent** instantiations (`foo<T>` where `T` is the
enclosing frame's type param) → a new `MakeGenericFunction` opcode that
resolves the type-arg templates against the current frame's `type_args`
at runtime.
- The call path seeds both `frame.type_args` (for `reflect.type_of<T>` /
bytecode bodies) and `pending_call_type_args` (for type-reifying
natives).
## Testing
- Parser disambiguation matrix; TIR snapshot tests (specialization,
`f("s")` rejection, arity, bounds, multi-arg).
- Runtime fixture `ns_instantiation_expr` covering: end-to-end call,
pointer identity, json-native-through-value, reflect-through-value
(concrete + param-dependent), bound violation, deep-copy equality.
- Full workspace test sweep green (4945 tests).
- An adversarial review pass surfaced 3 real issues (equality of
non-pooled copies, a latent entry-point panic, the
param-dependent-reflect hole) — all fixed and regression-tested.
## Notes
This is forward-compatible with function mocking: a generic-callable
value carries its `(function, type_args)` structurally, and the VM seeds
those at the call — exactly the key mock dispatch needs.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Instantiate generic functions as first-class values (e.g., `let f =
identity<int>;`) so specialized callables can be stored, passed, and
invoked later.
* Parser now better distinguishes `<...>` generic-argument lists from
comparison operators in value position.
* **Improvements**
* End-to-end support: type checking, bytecode, runtime representation,
and VM dispatch for generic-function values and reflection.
* **Tests**
* Added extensive compile-time and runtime tests and formatter coverage
for generic instantiation forms.
<!-- end of auto-generated comment: release notes by coderabbit.ai --> [codex] Add class generic bounds (#3672)
## Summary
Adds generic bounds to class generic parameters, including
parsing/lowering support, TIR validation, and runtime lowering support
for class methods that rely on bounded type variables.
## What changed
- Stores class generic bounds in AST/HIR and propagates them into
TIR/MIR.
- Centralizes TIR bounded-generic environment handling through a shared
`GenericEnv` path.
- Validates bounded generic class/interface type arguments in
annotations, constructors, throws clauses, lambda annotations, patterns,
type aliases, interface fields, and required interface method
signatures.
- Extends nominal interface subtyping through structural containers such
as lists, maps, futures, and non-generic function types.
- Adds focused tests for class generic bounds, unions, compound bounds,
function-type bounds, nested nominal bounds, and runtime method access.
## Root cause
Class generic parameters could be parsed as names, but their bounds were
not represented consistently through the AST/HIR/TIR/MIR pipeline.
Several TIR annotation paths also lowered types without validating
class/interface generic argument bounds, and MIR method lowering did not
carry enclosing class bounds when erasing bounded type variables for
runtime field/member access.
## Validation
- `cargo test -p baml_tests --test interfaces_class_generics`
- `cargo test -p baml_tests --test interfaces generic_bound`
- `cargo test -p baml_tests --test interfaces bounded`
- `cargo test -p baml_compiler2_tir`
- `cargo test -p baml_tests --test interfaces`
- `cargo test -p baml_compiler2_mir`
Note: the working tree still has unrelated pre-existing generated Go
changes and `rig_tests/`; they are not included in this PR.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> Large changes to generic bound propagation, subtyping, interface impl
indexing, and MIR method lowering in the core compiler pipeline;
mistakes could cause false positives/negatives in type-checking or
runtime dispatch.
>
> **Overview**
> **Class generic parameters now carry `extends` bounds end-to-end**
(AST/HIR `generic_param_bounds`, class lowering via
`extract_generic_params_with_bounds`), and TIR enforces those bounds
wherever class/interface type arguments appear—not only at explicit
call-site type args.
>
> **TIR** introduces a shared **`GenericEnv`** for merging enclosing
class/interface/`implements` generics with function and lambda scopes,
validates bounds on resolved types (constructors, aliases, patterns,
throws, interface signatures), and tightens **subtyping** (type-var
reflexivity, structural checks through lists/maps/futures/functions,
container `Array`/`Map` args without spurious nominal rules).
**Interface compatibility** now evaluates impl rules against lowered
class bounds and only caches nominal `class_implements` when bounds are
empty.
>
> **MIR** accumulates generic bounds from `implements` blocks, parent
class/interface scopes, and the function when lowering methods;
**interface impl inference** also unifies associated-type bindings on
interface-to-interface matches.
>
> New **`interfaces_class_generics`** tests and extensions to
associated-type / runtime dispatch tests cover bounded class type args
and cross-namespace defaults.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4c7031a5cd50175b7fcf9a8e9c3926d31f246022. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Class generic parameter bounds are supported and flow to methods,
lambdas, throws, object expressions, type aliases, and associated-type
bindings.
* **Behavior Changes / Bug Fixes**
* Generic-bound validation applied more broadly (including
interface/associated-type checks), with tighter
subtype/container/function compatibility and clearer diagnostics at
source spans.
* Runtime dispatch respects substituted associated-type bindings.
* **Tests**
* Large test suite added for bounds enforcement, inference failures,
associated types, runtime dispatch, and many typing contexts.
<!-- end of auto-generated comment: release notes by coderabbit.ai --> Fix interface `Self` bugs (#3647)
- `Self` should behave like a pinned type variable, not an
dyn-interface-type. Makes this (correctly) produce a compiler error:
```baml
interface Equatable {
function eq(self, other: Self) -> bool
}
function cmp<S extends Equatable, U extends Equatable>(x: S, y: U) -> bool {
return x.eq(y)
}
```
- Limits interface implementations to only concrete types. You cannot do
`implement Foo for int | string`. This prohibition applies to all
abstract types: unions, optionals, interfaces, `unknown`
- Fixes to bounds across typevars
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* BEP-044: Self-pinned support for interface default methods;
interface-method values now dispatch to concrete implementors at
runtime.
* **Diagnostics**
* New validation rejecting non-concrete `implements ... for ...` targets
(error E0138).
* **Tests**
* Added compile-time and runtime tests for Self-pinned default methods,
associated-type behavior, generic-bound soundness, and `implements ...
for ...` constraints.
<!-- end of auto-generated comment: release notes by coderabbit.ai --> fix: thread inferred generic type args through interface dispatch (#3667)
## Problem
A generic class instance whose `<T>` is **inferred** (not written
explicitly) must carry the correct runtime `class_type_args`, and a
method dispatched through a multi-implementor interface must receive the
resolved class/interface type args in its frame.
The interface-dispatch switch (`emit_method_candidate_switch`) called
the matched implementor's method with **only the explicit call-site
`<...>` type args** — it never seeded the callee frame. So an interface
**default method** (e.g. a lazy iterator's `map` building `Map<T, U>`),
or any dispatched method that reads its enclosing `T` at runtime
(`reflect.type_of<T>()`, constructing `Other<T>{}`), saw an empty
`frame.type_args`. It then built instances with `class_type_args =
[unknown]`, and the downstream `IsType` dispatch guard fell through to
the **wrong implementor** — a silent wrong answer, or a field-access
panic when the sibling class has a different layout.
This was the core compiler blocker for the `baml.iter` lazy-iterator
protocol.
### Minimal reproduction (before this fix)
```baml
interface Cursor<T> {
function head(self) -> T throws unknown
// default method builds a NEW generic from the interface's T
function take_one(self) -> Cursor<T> throws unknown {
return SingleCursor<T> { value: self.head() }
}
}
class ArrayCursor<T> {
arr T[]
idx int
function of(items: T[]) -> ArrayCursor<T> { return ArrayCursor<T> { arr: items, idx: 0 } }
implements Cursor<T> { function head(self) -> T throws unknown { return self.arr[0] } }
}
class SingleCursor<T> {
value T
implements Cursor<T> { function head(self) -> T throws unknown { return self.value } }
}
function take_one_then_head() -> int throws unknown {
let cursor: Cursor<int> = ArrayCursor.of([10, 20, 30]); // <int> inferred
let single: Cursor<int> = cursor.take_one(); // builds SingleCursor<?>
single.head() // expected 10
}
```
Before: `take_one()` (a default method) had empty `frame.type_args`, so
`SingleCursor<T>` was built with `class_type_args = [unknown]`.
`single.head()` then failed the `SingleCursor<int>` guard and dispatched
to `ArrayCursor.head`, which indexed a non-existent `arr` field. After:
returns `10`.
## Fix (in `baml_compiler2_mir::lower`)
1. **`enclosing_generic_params()`** now includes the **interface's**
generic params for interface default methods. They are lowered as
standalone functions whose bodies reference the interface's `T`; without
this, `TypeVar(T)` lowered to `Concrete(Void)` instead of
`TypeArgRef(N)`.
2. **The interface-dispatch switch** seeds each candidate's callee frame
(De Bruijn order: class/iface params first, then explicit call-site
args):
- a **class-owned** method gets the implementor's class args —
**statically** when the guard fully pins them (common/hot path,
allocation-free, unchanged bytecode), otherwise from the **matched
runtime instance** via a `BoundMethod` (covers `Any`/partial guards,
e.g. a generic class behind a *non-generic* interface);
- an **inherited interface default** gets the resolved interface view's
args (these can differ from the implementor's class args when the
`implements` block renames/reorders params).
3. **`default.<method>()`** (the super-like form that forwards into the
interface default) seeds the default frame with the interface's type
args too.
## Tests
New in-BAML regression namespace
`crates/baml_tests/baml_src/ns_inferred_generic_type_args`:
- `showcase_lazy_cursor_pipeline` — the lazy-cursor example above,
end-to-end.
- `inferred_static_ctor_dispatches_to_correct_implementor` — inferred
static ctor + non-generic interface.
- `inferred_static_ctor_dispatches_through_generic_interface` — inferred
static ctor + generic interface, mismatched field shapes.
- `interface_default_method_sees_interface_type_var` — default method
reads `T` via `reflect.type_of<T>()` at runtime.
- `class_method_builds_generic_via_class_type_var` — class-owned method
builds a generic from its class `T`.
- `default_method_forward_reads_interface_type_var` —
`default.<method>()` forwarding into a generic default.
- `generic_class_behind_nongeneric_interface_reads_type_var` —
`Any`-guard case, seeded from the runtime instance.
Full `baml_tests` suite green. Existing bytecode snapshots unchanged —
only the new namespace's `.snap` changed (no existing namespace
exercised this path; the `BoundMethod` path fires only for
previously-broken `Any`/partial-guard generic arms).
## Known limitation (separate, pre-existing — not addressed here)
A generic class whose **only** field is `T[]`, built via a static
constructor with inferred `T`, gets wrong `class_type_args` from the
*producer* side (a TIR inference issue in the struct literal,
independent of this dispatch fix). Adding any second field (as every
real iterator has, e.g. `idx: int`) avoids it, so it does not affect the
`ArrayIterator`-shaped use case. Worth a separate investigation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed generic type-argument propagation for interface dispatch so
dispatched calls—including default-method forwards and class-owned
implementations—receive correct runtime type arguments.
* **Tests**
* Added end-to-end tests validating inferred generic type args flow
through interface dispatch, default-method forwarding, class-owned vs
instance-seeded cases, and regressions for varied implementor layouts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> [codex] Implement BEP-57 associated types (#3652)
## Summary
Implements BEP-57 associated types for interfaces across parsing,
AST/HIR/TIR/MIR, formatter, LSP diagnostics, and codegen-facing
surfaces.
Key pieces:
- Adds associated type declarations, defaults, bounds, and body-side
witnesses in `implements` blocks.
- Supports associated type bindings on interface value types and generic
bounds.
- Adds associated type projection syntax, including qualified `(Type as
Interface<...>).Member` disambiguation.
- Keeps `.as<T>` as upcast-equivalent while enforcing associated binding
compatibility.
- Rejects target-side witnesses like `implements Iterator<Item = int>`;
witnesses must be declared inside the block (`type Item = int`)
Rust/Swift-style.
- Covers union, match/destructure narrowing, ambiguity, aliases,
diagnostics, formatter, and runtime dispatch cases.
## Root Cause / Design Notes
The parser can recognize `Iterator<Item = int>` as a type expression,
but BEP-57 semantics reserve associated type witnesses for the body of
an implementation. The implementation therefore preserves interface type
bindings for values/bounds/projections while rejecting bindings on
`implements` targets.
## Validation
- `cargo fmt`
- `cargo check -p baml_compiler2_tir -p baml_compiler2_mir -p
baml_lsp2_actions -p baml_compiler2_ast -p baml_compiler_parser -p
baml_fmt`
- `cargo build -p baml_cli`
- `cargo test -p baml_tests --test interfaces_associated_types --
--nocapture` (93 passed)
- `cargo test -p baml_tests --test interfaces -- --nocapture` (379
passed, 2 ignored)
- `cargo test -p baml_tests` (full package passed)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> Large, cross-cutting compiler and runtime changes to interface typing,
dispatch, and reflection; incorrect binding matching could cause subtle
dispatch or pattern-match bugs.
>
> **Overview**
> Adds **BEP-057 associated types** end-to-end: interfaces can declare
associated types (bounds/defaults), and `implements` blocks can witness
them with `type Name = …` instead of putting bindings on the
`implements` target.
>
> The AST/HIR layer gains **`AssociatedTypeBinding`**,
**`associated_type_bindings` on `TypeExpr::Path`**,
**`AssociatedTypeProjection`** (`Base.Item` / `(Base as
Interface).Item`), and matching fields on interfaces, `implements`
blocks, class destructuring, and method-to-interface metadata. Lowering
wires CST `TYPE_ARGS` / associated decls through `lower_type_expr` and
`lower_cst`.
>
> **MIR and emit** stop erasing interfaces to classes: `Ty::Interface`
keeps type args plus associated bindings; interface dispatch, field
access, pattern matching, `is`-type tests, and the runtime **implementor
registry** all compare **generic args and associated bindings** (e.g.
`Source<Item = int>` vs `Item = string`). Builtin codegen treats
projections as unknown types for native extraction.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cecf4f4bfcbebb130d0039392420d76f49091a95. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added associated types on interfaces (BEP-057): syntax, projections,
named bindings; type checking, dispatch, pattern matching, and
exhaustiveness honor associated bindings.
* **Tests**
* Extensive new and updated unit/integration tests covering parsing,
lowering, inference, matching, dispatch, and runtime behavior for
associated types.
* **Tooling**
* Parser, formatter, LSP symbols, diagnostics, snapshots, and
pretty-printers updated to recognize and display associated-type syntax
and errors.
* **Runtime & Serialization**
* Runtime reflection, serialization, FFI wire formats, and program
metadata now include associated-bindings.
<!-- end of auto-generated comment: release notes by coderabbit.ai --> Overhaul BAML language release pipeline (#3639)
## Summary
Overhauls the BAML language release path around the new
wrapper/toolchain split.
- adds the `baml` wrapper crate plus shared `baml_release` and stamped
`baml_version` crates
- replaces the alpha release workflows with a channel-aware release
graph, PyPI publish workflow, dry-run manifests, and wrapper-only
Homebrew/AUR publishing paths
- moves language/toolchain version authority to
`baml_language/release.toml` and `scripts/baml-language-version`
- makes the VSIX platform-neutral, launches `baml lsp` through the
wrapper, and adds LSP/playground compatibility metadata
- adds curl/PowerShell installers, package-manager artifact generation
scripts, and in-repo docs for install/release/toolchain behavior
## Validation
- pre-commit hooks passed, including:
- `cargo fmt`
- `cargo stow`
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
- YAML checks
- `actionlint .github/workflows/release-baml-language.yml
.github/workflows/publish-python-pypi.yml
.github/workflows/build-python-sdk.reusable.yaml`
- `cargo check --manifest-path baml_language/Cargo.toml -p baml -p
baml_release -p baml_cli`
- `cargo test --manifest-path baml_language/Cargo.toml -p baml_release`
- `scripts/baml-language-version check`
- `scripts/baml-wrapper-version check`
- `typescript2/app-vscode-ext`: `./node_modules/.bin/tsc --noEmit`
## Release Follow-ups
Before enabling production publishes:
- update or validate the PyPI trusted publisher binding for
`.github/workflows/publish-python-pypi.yml`
- verify `scripts/install.ps1` on Windows/PowerShell
- confirm production GitHub Actions secrets/OIDC paths for PyPI, AWS,
Homebrew, and AUR
- review whether the included `TASK/*` planning files should remain in
the branch
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* New standalone baml wrapper/launcher with toolchain commands,
self-update, IDE VSIX installer, per-project VS Code clients, and
protocol/handshake compatibility.
* **Installers & Packaging**
* POSIX and Windows installers; Homebrew/AUR formula and packaging
artifact generators; packaging adjusted to ship the wrapper binary.
* **Documentation**
* Expanded install, toolchain, release, maintainer, failure-modes,
command-surface, migration, and merge-to-canary guides.
* **CI / Releases**
* Channel-aware release pipelines, release-plan stamping/manifest
generation, reusable workflow inputs added, legacy alpha workflows
removed; SDKs bumped to 0.11.0.
<!-- end of auto-generated comment: release notes by coderabbit.ai --> Latest Branches
N/A
+1%
0%
© 2026 CodSpeed Technology