Latest Results
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