Skip to content

fix(compiler): make narrowing safe across spawn - #4128

Merged
codeshaunted merged 6 commits into
canaryfrom
avery/uncaptured-local-narrowing
Jul 22, 2026
Merged

fix(compiler): make narrowing safe across spawn#4128
codeshaunted merged 6 commits into
canaryfrom
avery/uncaptured-local-narrowing

Conversation

@codeshaunted

@codeshaunted codeshaunted commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Issue Reference

B-335

Changes

  • Restrict flow narrowing to uncaptured locals; fields and captured locals keep their declared types.
  • Add an atomic narrow_bind instruction across MIR, bytecode, and the VM.
  • Bind typed patterns from the exact tested value, including when spawn mutates the original binding.
  • Add compiler, bytecode, VM, and runtime regressions.

Testing

  • cargo test -p baml_tests --lib --quiet
  • cargo test -p baml_tests --test narrow_bind
  • cargo test -p bex_vm
  • cargo test -p bex_vm_types
  • cargo check -p baml_compiler2_tir -p baml_compiler2_mir -p baml_compiler2_emit -p bex_vm_types -p bex_vm -p baml_tests
  • cargo fmt --all -- --check

Summary by CodeRabbit

  • New Features
    • Added atomic typed-pattern matching that performs a type test and binds the matched value in one step.
    • Typed matches now retain the bound value reliably for subsequent loads/stores and branch logic.
    • Improved runtime support for matching type templates, including parameterized class types.
  • Bug Fixes
    • Prevented captured locals from being narrowed incorrectly; improved handling of narrowing across spawn/match contexts.
    • Enhanced bytecode disassembly and diagnostics for narrowing and binding operations.
  • Tests
    • Added integration coverage to verify the new atomic narrow-bind behavior and correct runtime results.

@vercel

vercel Bot commented Jul 22, 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 Jul 22, 2026 11:12pm
promptfiddle Ready Ready Preview, Comment Jul 22, 2026 11:12pm
promptfiddle2 Ready Ready Preview, Comment Jul 22, 2026 11:12pm

Request Review

@vercel
vercel Bot temporarily deployed to Preview – beps July 22, 2026 21:40 Inactive
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 595ef359-c148-4f7a-9f8b-fa3e0f561a68

📥 Commits

Reviewing files that changed from the base of the PR and between 791de24 and 7ace6c6.

📒 Files selected for processing (1)
  • baml_language/crates/baml_tests/src/compiler2_tir/phase7.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • baml_language/crates/baml_tests/src/compiler2_tir/phase7.rs

📝 Walkthrough

Walkthrough

The compiler now lowers atomic typed-pattern tests into NarrowBind, preserves bindings through MIR and bytecode, executes shared VM type matching, and validates captured-local narrowing and runtime behavior.

Changes

Atomic narrow-bind pipeline

Layer / File(s) Summary
Stable TIR narrowing
baml_language/crates/baml_compiler2_tir/src/{builder.rs,narrowing.rs}, baml_language/crates/baml_tests/src/compiler2_tir/phase7.rs
Narrowing records subject expressions and applies refinements only to uncaptured locals.
MIR atomic pattern lowering
baml_language/crates/baml_compiler2_mir/src/{ir.rs,builder.rs,lower.rs,optimize.rs,pretty.rs}
MIR adds NarrowBind, snapshots pattern scrutinees, disables incompatible switch lowering, and updates control-flow and local analysis.
MIR-to-bytecode emission
baml_language/crates/baml_compiler2_emit/src/{analysis.rs,stack_carry.rs,emit.rs}
Emission creates NarrowBind instructions with conditional branches while preserving destination locals and stack safety.
Bytecode and VM execution
baml_language/crates/{bex_vm_types/src/{bytecode.rs,relink.rs},bex_vm/src/{vm.rs,debug.rs}}
The instruction and compact opcode are encoded, displayed, relinked, and executed through shared type matching.
Validation
baml_language/crates/baml_tests/src/{compiler2_emit/mod.rs,compiler2_tir/phase7.rs}, baml_language/crates/baml_tests/tests/narrow_bind.rs
Tests cover emission, captured versus uncaptured narrowing, snapshots, and runtime binding.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PatternLowering
  participant MIR
  participant BytecodeEmitter
  participant BexVM
  PatternLowering->>MIR: emit NarrowBind with type template and branches
  MIR->>BytecodeEmitter: lower NarrowBind terminator
  BytecodeEmitter->>BexVM: execute NarrowBind opcode
  BexVM->>BexVM: match type constant and conditionally bind destination
  BexVM-->>PatternLowering: push match result and continue branch
Loading

Possibly related PRs

Poem

I hop through types, both wide and small,
Bind the matched value, never fall.
MIR paths bloom, bytecode sings,
The VM checks what each type brings.
Captured locals stay safely still.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main goal: making narrowing safe around spawn by restricting unsafe narrowing behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ 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 avery/uncaptured-local-narrowing

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.

@github-actions

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.

@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 22, 2026 21:47 Inactive

@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: 3

🧹 Nitpick comments (1)
baml_language/crates/baml_tests/tests/narrow_bind.rs (1)

4-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the spawned-mutation binding case.

This only tests a direct match. Add a regression where a captured source is mutated by spawn and the typed binding is consumed after synchronization; otherwise a reload-after-test regression can still pass.

Also verify the Rust changes with cargo test --lib.

🤖 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_tests/tests/narrow_bind.rs` around lines 4 - 29,
Extend typed-pattern coverage in typed_pattern_tests_and_binds_the_same_value
with a regression where a captured source is mutated inside spawn,
synchronization completes, and the typed binding is then consumed afterward.
Assert the expected result and narrow_bind behavior so reload-after-test
regressions are detected, then verify the Rust changes with cargo test --lib.

Source: Coding guidelines

🤖 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_mir/src/lower.rs`:
- Around line 12224-12228: Update the stale comment in the pattern-lowering
check around pattern_narrow_type to state that narrow-bind patterns are
deliberately excluded from switch/jump-table lowering because Switch cannot
preserve bind-on-match semantics. Remove the outdated wording about recovering
the narrow annotation and treating it as a TypeTag arm; leave the return false
behavior unchanged.

In `@baml_language/crates/baml_tests/src/compiler2_emit/mod.rs`:
- Around line 62-86: Update the test around the NarrowBind instruction to record
its index alongside the destination slot, then restrict the destination-use
search to instructions after that index, specifically the succeeding then-arm
instructions. Preserve the existing slot matching for LoadVar, StoreVarLoadVar,
and LoadVar2 so the assertion verifies the bound local is used only after
NarrowBind rather than matching an earlier scrutinee use.

In `@baml_language/crates/bex_vm/src/debug.rs`:
- Around line 168-174: Update the Instruction::NarrowBind formatting branch to
resolve ty through operand_meta before constructing the output, matching the
textual renderer’s metadata lookup. Render the resolved type metadata instead of
the raw constant index while preserving the existing destination-name handling.

---

Nitpick comments:
In `@baml_language/crates/baml_tests/tests/narrow_bind.rs`:
- Around line 4-29: Extend typed-pattern coverage in
typed_pattern_tests_and_binds_the_same_value with a regression where a captured
source is mutated inside spawn, synchronization completes, and the typed binding
is then consumed afterward. Assert the expected result and narrow_bind behavior
so reload-after-test regressions are detected, then verify the Rust changes with
cargo test --lib.
🪄 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: 1c87df28-cdaa-4e7b-90c6-76cbdad61716

📥 Commits

Reviewing files that changed from the base of the PR and between f1c20aa and 832e457.

⛔ Files ignored due to path filters (12)
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__testing_std__/baml_tests__compiles____testing_std____04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__testing_std__/baml_tests__compiles____testing_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/generic_match_typevar_arm/baml_tests__compiles__generic_match_typevar_arm__04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/generic_match_typevar_arm/baml_tests__compiles__generic_match_typevar_arm__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/json_alias_basic/baml_tests__compiles__json_alias_basic__04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/json_alias_basic/baml_tests__compiles__json_alias_basic__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/json_parse_stringify_intrinsics/baml_tests__compiles__json_parse_stringify_intrinsics__04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/json_parse_stringify_intrinsics/baml_tests__compiles__json_parse_stringify_intrinsics__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/patterns_new/baml_tests__compiles__patterns_new__04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/patterns_new/baml_tests__compiles__patterns_new__06_codegen.snap is excluded by !**/*.snap
📒 Files selected for processing (18)
  • baml_language/crates/baml_compiler2_emit/src/analysis.rs
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/baml_compiler2_emit/src/stack_carry.rs
  • baml_language/crates/baml_compiler2_mir/src/builder.rs
  • baml_language/crates/baml_compiler2_mir/src/ir.rs
  • baml_language/crates/baml_compiler2_mir/src/lower.rs
  • baml_language/crates/baml_compiler2_mir/src/optimize.rs
  • baml_language/crates/baml_compiler2_mir/src/pretty.rs
  • baml_language/crates/baml_compiler2_tir/src/builder.rs
  • baml_language/crates/baml_compiler2_tir/src/narrowing.rs
  • baml_language/crates/baml_tests/src/compiler2_emit/mod.rs
  • baml_language/crates/baml_tests/src/compiler2_mir/mod.rs
  • baml_language/crates/baml_tests/src/compiler2_tir/phase7.rs
  • baml_language/crates/baml_tests/tests/narrow_bind.rs
  • baml_language/crates/bex_vm/src/debug.rs
  • baml_language/crates/bex_vm/src/vm.rs
  • baml_language/crates/bex_vm_types/src/bytecode.rs
  • baml_language/crates/bex_vm_types/src/relink.rs

Comment thread baml_language/crates/baml_compiler2_mir/src/lower.rs Outdated
Comment thread baml_language/crates/baml_tests/src/compiler2_emit/mod.rs Outdated
Comment thread baml_language/crates/bex_vm/src/debug.rs
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Binary size checks passed

7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
baml-cli Linux 🔒 25.6 MB 10.9 MB file 25.3 MB +321.1 KB (+1.3%) OK
packed-program Linux 🔒 17.2 MB 7.1 MB file 17.0 MB +164.3 KB (+1.0%) OK
baml-cli macOS 🔒 19.8 MB 9.5 MB file 19.6 MB +264.7 KB (+1.4%) OK
packed-program macOS 🔒 13.4 MB 6.2 MB file 13.2 MB +198.4 KB (+1.5%) OK
baml-cli Windows 🔒 21.4 MB 9.7 MB file 21.1 MB +274.9 KB (+1.3%) OK
packed-program Windows 🔒 14.3 MB 6.3 MB file 14.2 MB +158.2 KB (+1.1%) OK
bridge_wasm WASM 16.2 MB 🔒 4.4 MB gzip 4.4 MB +12.6 KB (+0.3%) 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

@vercel
vercel Bot temporarily deployed to Preview – beps July 22, 2026 21:58 Inactive
…cal-narrowing

# Conflicts:
#	baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____04_5_mir.snap
#	baml_language/crates/baml_tests/snapshots/compiles/__testing_std__/baml_tests__compiles____testing_std____04_5_mir.snap
#	baml_language/crates/baml_tests/snapshots/compiles/patterns_new/baml_tests__compiles__patterns_new__04_5_mir.snap
#	baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap
#	baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap
@vercel
vercel Bot temporarily deployed to Preview – beps July 22, 2026 22:06 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 22, 2026 22:14 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 22, 2026 22:27 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 22, 2026 22:31 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 22, 2026 22:38 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 22, 2026 22:42 Inactive

@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.

🧹 Nitpick comments (1)
baml_language/crates/baml_compiler2_tir/src/builder.rs (1)

1356-1398: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider documenting the new capture-narrowing helpers.

local_is_uncaptured, narrow_uncaptured_local, and uncaptured_condition_narrowings implement the core invariant this PR introduces (narrowing only applies to uncaptured locals), but unlike nearly every other method in this file they carry no doc comment explaining the captured-bindings semantics or the fail-closed behavior (missing source map / unresolved binding → treated as captured). A short doc note would help future maintainers understand why narrowing silently no-ops in these cases.

🤖 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/builder.rs` around lines 1356 -
1398, Add concise doc comments to local_is_uncaptured, narrow_uncaptured_local,
and uncaptured_condition_narrowings describing that narrowing applies only to
uncaptured locals, and that missing source maps or unresolved bindings fail
closed by treating the local as captured. Keep the existing behavior unchanged.
🤖 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.

Nitpick comments:
In `@baml_language/crates/baml_compiler2_tir/src/builder.rs`:
- Around line 1356-1398: Add concise doc comments to local_is_uncaptured,
narrow_uncaptured_local, and uncaptured_condition_narrowings describing that
narrowing applies only to uncaptured locals, and that missing source maps or
unresolved bindings fail closed by treating the local as captured. Keep the
existing behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fb7ffca8-a8a1-47ba-9921-560fc8f8f94d

📥 Commits

Reviewing files that changed from the base of the PR and between 4156ccf and 791de24.

📒 Files selected for processing (5)
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/baml_compiler2_mir/src/optimize.rs
  • baml_language/crates/baml_compiler2_tir/src/builder.rs
  • baml_language/crates/bex_vm/src/vm.rs
  • baml_language/crates/bex_vm_types/src/bytecode.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/bex_vm/src/vm.rs
  • baml_language/crates/baml_compiler2_mir/src/optimize.rs
  • baml_language/crates/bex_vm_types/src/bytecode.rs

@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 22, 2026 22:49 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 22, 2026 22:51 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 22, 2026 22:58 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 22, 2026 23:12 Inactive
@codeshaunted

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codeshaunted
codeshaunted added this pull request to the merge queue Jul 22, 2026
Merged via the queue into canary with commit 872ede1 Jul 22, 2026
67 checks passed
@codeshaunted
codeshaunted deleted the avery/uncaptured-local-narrowing branch July 22, 2026 23:35
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