Skip to content

[codex] Add class generic bounds - #3672

Merged
aaronvg merged 9 commits into
canaryfrom
codex/find-functionbound-generics
Jun 4, 2026
Merged

[codex] Add class generic bounds#3672
aaronvg merged 9 commits into
canaryfrom
codex/find-functionbound-generics

Conversation

@aaronvg

@aaronvg aaronvg commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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.


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.

Reviewed by Cursor Bugbot for commit 4c7031a. Bugbot is set up for automated code reviews on this repo. Configure here.

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.

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jun 4, 2026 12:09am
promptfiddle Ready Ready Preview, Comment Jun 4, 2026 12:09am
promptfiddle2 Ready Ready Preview, Comment Jun 4, 2026 12:09am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds BEP-044 per-class generic parameter bounds and enforces them across lowering (CST→AST→HIR), MIR propagation, TIR validation/subtyping, type-inference (GenericEnv), interface-rule matching, and comprehensive tests.

Changes

Generic Parameter Bounds Implementation

Layer / File(s) Summary
AST-level bounds representation
baml_language/crates/baml_compiler2_ast/src/ast.rs
ClassDef gains generic_param_bounds: Vec<Option<TypeExpr>> to store per-generic bounds parallel to generic_params.
CST → AST/HIR lowering with bounds extraction
baml_language/crates/baml_compiler2_ast/src/lower_cst.rs, baml_language/crates/baml_compiler2_hir/src/item_tree.rs
lower_class uses extract_generic_params_with_bounds and ItemTree::alloc_class copies generic_param_bounds into HIR Class.
MIR propagation and binding reconciliation
baml_language/crates/baml_compiler2_mir/src/lower.rs
LoweringContext::new assembles combined generic param lists (implements/class + function) for bound lowering and infer_interface_class_bindings now reconciles associated-type bindings when matching interface/class types.
TIR builder: validation, subtype helpers, and wiring
baml_language/crates/baml_compiler2_tir/src/builder.rs
Make lower_generic_param_bounds pub(crate), add validate_type_generic_bounds_at_span/span-agnostic entrypoints, resolve_interface_loc, an early reflexivity fast-path, structural-subtype helpers (nominal interfaces + function param variance), and invoke validation at multiple lowering/check sites.
Type inference: GenericEnv and span-aware lowering
baml_language/crates/baml_compiler2_tir/src/inference.rs
Introduce GenericEnv, span-aware type-expression lowering/validation helpers, and refactor ScopeKind handling (Function, Lambda, Class, TypeAlias) to apply envs, perform self substitution, and validate declared bounds at spans.
Interface implementation rule bounds
baml_language/crates/baml_compiler2_tir/src/interfaces.rs
Populate InterfaceImplRule.generic_param_bounds for in-body implements by lowering declared bounds; route type_implements through rule-driven checks that validate bounds and adjust compatibility-view population.
Comprehensive tests
baml_language/crates/baml_tests/tests/interfaces_class_generics.rs, baml_language/crates/baml_tests/tests/interfaces_associated_types.rs
New tests covering bound exposure, inferred/explicit violations, compound/union/substituted bounds, function-type bounds, throws/optional/alias/parameter/field/pattern positions, lambda/async contexts, associated-type resolution, and runtime dispatch checks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • BoundaryML/baml#3647: related prior work extending generic-bound lowering/validation and integrating it into interface/method lowering paths.
  • BoundaryML/baml#3555: earlier interface v2 plumbing and ImplementsRegistry changes referenced by this PR.
  • BoundaryML/baml#3652: adjacent BEP-57 work on associated-type wiring that touches MIR/interface binding reconciliation.

Poem

I hop through AST and HIR with glee,
Bounds on generics snug as can be,
MIR and TIR check each span and rule,
Tests run fast — the compiler's cool,
A rabbit cheers: the types agree! 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[codex] Add class generic bounds' directly and clearly describes the main feature added: support for generic bounds on classes throughout the compiler pipeline.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/find-functionbound-generics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread baml_language/crates/baml_compiler2_tir/src/builder.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/crates/baml_compiler2_mir/src/lower.rs (1)

1739-1775: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mirror interface-owned generics in enclosing_generic_params().

This constructor now pulls interface generic params/bounds into generic_param_bounds, but enclosing_generic_params() at Lines 5530-5550 still only returns implements_for or class + function generics. In a generic interface default method, any interface type var that later flows through emit_interface_class_guard_branch, reflect.type_of<T>(), or other TyTemplate lowering still resolves with the wrong param list and degrades to Void instead of a TypeArgRef, so the runtime dispatch/materialization path stays incorrect.

Suggested fix
 fn enclosing_generic_params(&self) -> Vec<baml_base::Name> {
     let Some(fl) = self.func_loc else {
         return Vec::new();
     };
     let item_tree = file_item_tree(self.db, fl.file(self.db));
     let func_id = fl.id(self.db);
     if let Some(imp) = item_tree
         .implements_for
         .iter()
         .find(|imp| imp.methods.contains(&func_id))
     {
         return imp.generic_params.clone();
     }
+    if let Some(iface_data) = item_tree
+        .interfaces
+        .values()
+        .find(|iface_data| iface_data.default_methods.contains(&func_id))
+    {
+        let mut params = iface_data.generic_params.clone();
+        params.extend(item_tree[func_id].generic_params.iter().cloned());
+        return params;
+    }
     let mut params: Vec<baml_base::Name> = item_tree
         .classes
         .values()
         .find(|class_data| class_data.methods.contains(&func_id))
         .map(|class_data| class_data.generic_params.clone())
         .unwrap_or_default();
     params.extend(item_tree[func_id].generic_params.iter().cloned());
     params
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs` around lines 1739 -
1775, enclosing_generic_params() currently returns only implements_for or
class+function generics, causing interface-owned generic params/bounds added
earlier (in the bound_param_names/generic_param_bounds construction) to be
omitted; update enclosing_generic_params() to mirror the same logic used when
collecting bound_param_names—i.e., if the function scope's parent is a
ScopeKind::Class or an Interface, include that parent type's generic_params (and
their bounds) when building the returned Name list so interface type variables
are visible to TyTemplate lowering and
emit_interface_class_guard_branch/reflect.type_of<T>() paths; locate
enclosing_generic_params(), extend it to check the parent scope for interface
entries (similar to the bound_param_names/ bound_exprs collection) and append
interface.generic_params before appending the function's generic_params so
runtime materialization uses TypeArgRef instead of collapsing to Void.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@baml_language/crates/baml_compiler2_tir/src/builder.rs`:
- Around line 11204-11215: The List and Map branches in is_subtype currently
treat containers as covariant by checking only one direction; change them to
enforce invariance by requiring mutual subtyping (i.e., both
self.is_subtype(sub_inner, sup_inner) && self.is_subtype(sup_inner, sub_inner))
for Ty::List/Ty::EvolvingList and similarly require mutual checks for both key
and value in Ty::Map/Ty::EvolvingMap so that List<T> and Map<K,V> are only
considered subtypes when their element/key/value types are equivalent under
mutual subtyping; update those match arms in builder.rs (the match handling
Ty::List/Ty::EvolvingList and Ty::Map/Ty::EvolvingMap) to use the bidirectional
checks instead of a single is_subtype call.

In `@baml_language/crates/baml_compiler2_tir/src/inference.rs`:
- Around line 1323-1330: The generic-bound diagnostics are currently anchored to
the enclosing scope span (func_data.span / ancestor_scope.range) when calling
apply_generic_env after extend_env_with_lambda_generics; change those calls to
pass the lambda's span instead—use the span returned or provided by
extend_env_with_lambda_generics (the lambda_span/local lambda_site span) as the
last argument to apply_generic_env so diagnostics originate at the lambda site;
update both occurrences (the call around apply_generic_env at the shown location
and the similar call around lines 1391-1398) to use that lambda span rather than
func_data.span/ancestor_scope.range.
- Around line 243-245: The ancestor traversal currently stops on the first
non-Let scope (the `break` in inference.rs when matching `ScopeKind::Let` vs
`_`), which prevents finding an enclosing `Function` if an intermediate `Block`
exists; update the loop so that instead of breaking on `_` you continue climbing
(e.g., set `current = scope.parent` and keep looping) until you either hit a
`ScopeKind::Function` or `None`, ensuring the enclosing function's
generics/bounds are discovered; adjust any handling around `current` and `scope`
so the loop exits only on `Function` or end-of-chain rather than any non-Let.

In `@baml_language/crates/baml_compiler2_tir/src/interfaces.rs`:
- Around line 1024-1035: The compatibility path currently bypasses generic bound
checks: update derive_compatibility_views and the compatibility recording so
that class-shaped rules are not blindly recorded in class_implements by
QualifiedTypeName alone; instead ensure ImplementsRegistry::type_implements
consults type_implements_interface_via_rule (or make the compatibility-recording
logic type-arg aware) so that queries for Class<BadArg> are validated against
the InterfaceImplRule.generic_param_bounds created earlier (reference
derive_compatibility_views, class_implements,
ImplementsRegistry::type_implements, QualifiedTypeName, and
type_implements_interface_via_rule).

---

Outside diff comments:
In `@baml_language/crates/baml_compiler2_mir/src/lower.rs`:
- Around line 1739-1775: enclosing_generic_params() currently returns only
implements_for or class+function generics, causing interface-owned generic
params/bounds added earlier (in the bound_param_names/generic_param_bounds
construction) to be omitted; update enclosing_generic_params() to mirror the
same logic used when collecting bound_param_names—i.e., if the function scope's
parent is a ScopeKind::Class or an Interface, include that parent type's
generic_params (and their bounds) when building the returned Name list so
interface type variables are visible to TyTemplate lowering and
emit_interface_class_guard_branch/reflect.type_of<T>() paths; locate
enclosing_generic_params(), extend it to check the parent scope for interface
entries (similar to the bound_param_names/ bound_exprs collection) and append
interface.generic_params before appending the function's generic_params so
runtime materialization uses TypeArgRef instead of collapsing to Void.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d1160320-790a-4385-971e-6de7941286a1

📥 Commits

Reviewing files that changed from the base of the PR and between 5557a52 and fd20b61.

📒 Files selected for processing (8)
  • baml_language/crates/baml_compiler2_ast/src/ast.rs
  • baml_language/crates/baml_compiler2_ast/src/lower_cst.rs
  • baml_language/crates/baml_compiler2_hir/src/item_tree.rs
  • baml_language/crates/baml_compiler2_mir/src/lower.rs
  • baml_language/crates/baml_compiler2_tir/src/builder.rs
  • baml_language/crates/baml_compiler2_tir/src/inference.rs
  • baml_language/crates/baml_compiler2_tir/src/interfaces.rs
  • baml_language/crates/baml_tests/tests/interfaces_class_generics.rs

Comment thread baml_language/crates/baml_compiler2_tir/src/builder.rs
Comment thread baml_language/crates/baml_compiler2_tir/src/inference.rs
Comment thread baml_language/crates/baml_compiler2_tir/src/inference.rs
Comment thread baml_language/crates/baml_compiler2_tir/src/interfaces.rs
…bound-generics

# Conflicts:
#	baml_language/crates/baml_compiler2_tir/src/inference.rs
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

⏭️ Performance benchmarks were skipped

Perf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to canary/main.

To run them on this PR, do any of the following, then push a commit (or re-run CI):

  • Add RUN_CODSPEED=1 to the PR description, or
  • Include run-perf or /perf in the PR title or any commit message.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Binary size checks passed

7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
baml-cli Linux 🔒 17.7 MB 7.5 MB file 24.1 MB -6.4 MB (-26.5%) OK
packed-program Linux 🔒 12.9 MB 5.4 MB file 15.1 MB -2.2 MB (-14.7%) OK
baml-cli macOS 🔒 13.5 MB 6.5 MB file 18.2 MB -4.7 MB (-25.8%) OK
packed-program macOS 🔒 9.8 MB 4.7 MB file 11.5 MB -1.7 MB (-14.6%) OK
baml-cli Windows 🔒 14.5 MB 6.7 MB file 19.6 MB -5.1 MB (-25.9%) OK
packed-program Windows 🔒 10.4 MB 4.8 MB file 12.2 MB -1.9 MB (-15.2%) OK
bridge_wasm WASM 11.9 MB 🔒 3.4 MB gzip 3.9 MB -511.7 KB (-13.2%) OK

🔒 = the size this artifact is GATED on (ceiling + delta). Binaries gate on file size (installed binary); WASM gates on gzip (download size). The other size is shown for information only.


Generated by cargo size-gate · workflow run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/crates/baml_compiler2_tir/src/interfaces.rs (1)

567-593: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exclude bounded blanket rules from the compatibility view.

The new guard only protects class_implements/implements_type_args. This branch still records bounded blanket rules in blanket_class_implements, but blanket_class_implements_interface() only checks class QTN + arity, so a rule like implements<T extends Named> Printable for Box<T> can still make Box<int> look compatible through the legacy fast path.

Suggested fix
         match &rule.for_ty_pattern {
             Ty::Class(class_qtn, class_args, _)
                 if matches!(rule.origin, InterfaceImplOrigin::OutOfBody)
                     && class_args.iter().any(|arg| matches!(arg, Ty::TypeVar(..))) =>
             {
-                views.blanket_class_implements.push(BlanketClassImpl {
-                    class_qtn: class_qtn.clone(),
-                    generic_params: rule.generic_params.clone(),
-                    generic_param_bounds: rule.generic_param_bounds.clone(),
-                    interface_qtn: iface_qtn.clone(),
-                    interface_type_args: interface_type_args.clone(),
-                    for_target_ty: rule.for_ty_pattern.clone(),
-                });
+                if rule.generic_param_bounds.iter().all(Option::is_none) {
+                    views.blanket_class_implements.push(BlanketClassImpl {
+                        class_qtn: class_qtn.clone(),
+                        generic_params: rule.generic_params.clone(),
+                        generic_param_bounds: rule.generic_param_bounds.clone(),
+                        interface_qtn: iface_qtn.clone(),
+                        interface_type_args: interface_type_args.clone(),
+                        for_target_ty: rule.for_ty_pattern.clone(),
+                    });
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/baml_compiler2_tir/src/interfaces.rs` around lines 567 -
593, The branch that pushes into views.blanket_class_implements should skip
rules with bounded generic params: when matching Ty::Class(...) in the
rule.for_ty_pattern arm, check rule.generic_param_bounds (same condition used
for class_implements) and only push the BlanketClassImpl into
views.blanket_class_implements if all generic_param_bounds are None; this
prevents bounded blanket rules (e.g., implements<T extends Named> ...) from
being recorded and subsequently considered by
blanket_class_implements_interface(), so update the logic around where
BlanketClassImpl is created/inserted to include that guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@baml_language/crates/baml_compiler2_tir/src/interfaces.rs`:
- Around line 567-593: The branch that pushes into
views.blanket_class_implements should skip rules with bounded generic params:
when matching Ty::Class(...) in the rule.for_ty_pattern arm, check
rule.generic_param_bounds (same condition used for class_implements) and only
push the BlanketClassImpl into views.blanket_class_implements if all
generic_param_bounds are None; this prevents bounded blanket rules (e.g.,
implements<T extends Named> ...) from being recorded and subsequently considered
by blanket_class_implements_interface(), so update the logic around where
BlanketClassImpl is created/inserted to include that guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7bcf8930-835a-4a29-8b6d-f7e32f52575f

📥 Commits

Reviewing files that changed from the base of the PR and between 25a2bac and f9d67a7.

📒 Files selected for processing (4)
  • baml_language/crates/baml_compiler2_tir/src/builder.rs
  • baml_language/crates/baml_compiler2_tir/src/inference.rs
  • baml_language/crates/baml_compiler2_tir/src/interfaces.rs
  • baml_language/crates/baml_tests/tests/interfaces_class_generics.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • baml_language/crates/baml_compiler2_tir/src/inference.rs
  • baml_language/crates/baml_compiler2_tir/src/builder.rs

Comment thread baml_language/crates/baml_compiler2_mir/src/lower.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
baml_language/crates/baml_compiler2_mir/src/lower.rs (2)

7017-7032: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Propagate interface type args for non-class implementor defaults.

This path still hard-codes frame_seed: CalleeFrameSeed::Static(Vec::new()), so an inherited generic interface default dispatched to a primitive / other non-class implementor still enters with an empty frame.type_args. A default body that reads its enclosing interface generics (reflect.type_of<T>(), bounded member access on T, etc.) will still resolve them to unknown here. Please seed this from implementor.iface_args when they are concrete, mirroring the class-implementor default path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs` around lines 7017 -
7032, The branch that builds InterfaceMethodCandidate for non-class implementors
currently uses CalleeFrameSeed::Static(Vec::new()), which drops interface type
args and causes interface-default bodies to see unknown generics; update the
code in the block that calls resolve_type_implementor_method and constructs
InterfaceMethodCandidate so that frame_seed is seeded from
implementor.iface_args when those args are concrete (mirror the
class-implementor default path), e.g. construct CalleeFrameSeed::Static with
implementor.iface_args.clone() (or an empty Vec only if iface_args are not
present), keeping guard as
InterfaceDispatchGuard::Type(implementor.runtime_ty.clone()) and preserving
item_ref.

7649-7708: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Blanket override methods still drop implements_for generic args.

The new frame_seed plumbing stops at inherited defaults; dispatched out-of-body overrides still use Static(Vec::new()). Those bodies lower against enclosing_generic_params() == imp.generic_params, so any override that references its rule generics or their bounds still runs with no seeded frame.type_args and loses that runtime contract. This needs a seed in imp.generic_params order rather than the class/interface order.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs` around lines 7649 -
7708, The blanket-override path is leaving CalleeFrameSeed::Static(Vec::new())
causing out-of-body overrides to miss impl-level generic args; update the branch
that constructs InterfaceMethodCandidate (both the earlier override case and
this inherited default case) to compute frame_seed using the implementing impl's
generic-parameter order (imp.generic_params / enclosing_generic_params() for the
impl) instead of class/interface order: gather the resolved frame type args
(currently in current_iface_args or the resolved closure view args) and
reorder/map them into the impl's generic parameter order (use the impl/type-name
info available via impl_tn or the impl's definition lookup) and pass
CalleeFrameSeed::Static(mapped_args) when fully concrete, otherwise Vec::new();
keep using InterfaceMethodCandidate, InterfaceDispatchGuard::Class, and
frame_seed = CalleeFrameSeed::Static(...) as the insertion point.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@baml_language/crates/baml_compiler2_mir/src/lower.rs`:
- Around line 7017-7032: The branch that builds InterfaceMethodCandidate for
non-class implementors currently uses CalleeFrameSeed::Static(Vec::new()), which
drops interface type args and causes interface-default bodies to see unknown
generics; update the code in the block that calls
resolve_type_implementor_method and constructs InterfaceMethodCandidate so that
frame_seed is seeded from implementor.iface_args when those args are concrete
(mirror the class-implementor default path), e.g. construct
CalleeFrameSeed::Static with implementor.iface_args.clone() (or an empty Vec
only if iface_args are not present), keeping guard as
InterfaceDispatchGuard::Type(implementor.runtime_ty.clone()) and preserving
item_ref.
- Around line 7649-7708: The blanket-override path is leaving
CalleeFrameSeed::Static(Vec::new()) causing out-of-body overrides to miss
impl-level generic args; update the branch that constructs
InterfaceMethodCandidate (both the earlier override case and this inherited
default case) to compute frame_seed using the implementing impl's
generic-parameter order (imp.generic_params / enclosing_generic_params() for the
impl) instead of class/interface order: gather the resolved frame type args
(currently in current_iface_args or the resolved closure view args) and
reorder/map them into the impl's generic parameter order (use the impl/type-name
info available via impl_tn or the impl's definition lookup) and pass
CalleeFrameSeed::Static(mapped_args) when fully concrete, otherwise Vec::new();
keep using InterfaceMethodCandidate, InterfaceDispatchGuard::Class, and
frame_seed = CalleeFrameSeed::Static(...) as the insertion point.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 546ba8d6-8f9d-4598-87d4-0ddea5f40514

📥 Commits

Reviewing files that changed from the base of the PR and between f9d67a7 and d73c4ec.

📒 Files selected for processing (1)
  • baml_language/crates/baml_compiler2_mir/src/lower.rs

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 196e71a. Configure here.

Comment thread baml_language/crates/baml_compiler2_tir/src/builder.rs
…bound-generics

# Conflicts:
#	baml_language/crates/baml_compiler2_tir/src/builder.rs
#	baml_language/crates/baml_compiler2_tir/src/inference.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant