Skip to content

Bind aggregate params containing &mut with flow bindings#127

Draft
coord-e wants to merge 5 commits into
mainfrom
claude/sleepy-archimedes-iu2cyw
Draft

Bind aggregate params containing &mut with flow bindings#127
coord-e wants to merge 5 commits into
mainfrom
claude/sleepy-archimedes-iu2cyw

Conversation

@coord-e

@coord-e coord-e commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #125.

Reborrowing a &mut-typed field out of an aggregate (tuple/struct) parameter panicked with deref unbound var in Env::locate_place.

Root cause

An aggregate parameter like w: (&mut i64,) is an immutable local whose type is a Tuple, not directly is_mut(). The binding logic only routes a value through the flow-decomposing mut_bind path when is_mut_local(local) || ty.is_mut(), so the tuple param took the immut_bindbind_var path and was stored without flow bindings. When ReborrowVisitor later turned the copy of the &mut field into a reborrow of _1.0, locate_place started at _1, found no Tuple flow binding, and the .expect("deref unbound var") fired.

A top-level &mut parameter already works because is_mut() is true and it takes the mut_bind path — this change generalizes that to aggregates that contain a reachable &mut.

Changes

  • src/rty.rs: new Type::contains_mut() — true if the type is, or transitively contains through tuple/struct and pointer projections, a mutable reference.
  • src/analyze/basic_block.rs: both binding sites (bind_local and the basic-block parameter loop) use contains_mut() instead of is_mut(), so aggregates holding a &mut get flow-decomposed bindings. Blast radius is minimal — tuples without any &mut still take the immut_bind path.
  • tests/ui/{pass,fail}/reborrow_mut_field_of_aggregate_param.rs: paired regression test (per repo convention). The pass asserts the value flows back correctly after reborrowing the aggregate's &mut field; the fail asserts the negated condition and expects Unsat.

Testing

Ran the full suite locally with z3 4.13.0 (matching .github/actions/setup-z3) and the COAR docker image: 234 passed, 0 failed, including both new tests.

Note

contains_mut does not recurse into enum variants, since enum field types come from the EnumDefProvider rather than the Type itself. The reported cases (tuple and struct params — structs are represented as tuples here) are covered; an enum carrying a &mut field would be a separate follow-up.

https://claude.ai/code/session_01MQbByXbDZ8FxkhRra4nPfN


Generated by Claude Code

claude added 5 commits June 14, 2026 12:34
Reborrowing a `&mut`-typed field out of an aggregate (tuple/struct)
parameter panicked with "deref unbound var" in `Env::locate_place`.
Such an aggregate parameter is an immutable local whose type is a tuple,
so it took the `immut_bind` path and was stored without flow bindings.
The reborrow elaboration then failed to resolve the field projection to
the inner `&mut`.

Detect mutable references reachable through aggregate and pointer
projections via a new `Type::contains_mut` and route such parameters
through the flow-decomposing `mut_bind` path, matching how a top-level
`&mut` parameter is already handled.

Fixes #125

https://claude.ai/code/session_01MQbByXbDZ8FxkhRra4nPfN
Follow the repo convention of pairing each ui test in pass/ and fail/.
Drive the test from main so verification has a concrete assertion to
check, and add the fail counterpart asserting the negated condition.
Make the 'needs decomposition' decision a separate, type-driven notion
from local mutability. mut_local (reassignable/mut-borrowed) remains the
box-elaboration trigger per standard Rust semantics; whether to bind with
flow decomposition is now derived independently from the type.

Replace Type::contains_mut with Env::type_contains_mut, which resolves
enum variants through the enum definitions (with a guard against recursive
enum types) so a &mut reachable through an enum is detected too, rather
than relying on mut_locals' Move rule to mark the enum local mutable.

Add pass/fail ui tests for reborrowing a &mut field out of an enum param.

https://claude.ai/code/session_01MQbByXbDZ8FxkhRra4nPfN
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.

Panic: "deref unbound var" when reborrowing a &mut field out of an aggregate parameter

2 participants