Bind the structure of the value a mutable borrow prophesies - #213
Draft
coord-e wants to merge 1 commit into
Draft
Conversation
A mutable borrow rebinds the borrowed variable to the borrow's prophecy variable, which stands for the value the place takes once the borrow ends. That variable is opaque: it is bound as a value, not with the `FlowBinding` that `locate_place` and `borrow_var` walk to reach through a reference, a box, a tuple, or an enum. A place whose value has structure therefore lost that structure as soon as it was borrowed. Writing to a `mut` local is elaborated into a borrow of the local's slot, so a straight-line reassignment of a `&mut` local followed by a write through it hit exactly that: the reassignment left the local's content standing for the prophecy, and elaborating the write into a reborrow of `*r` panicked with `borrowing unbound var`. The same reassignment of a tuple local panicked with `deref unbound var` when projecting a field afterwards. Reading through the reassigned local was fine, since reads go through `place_type`, which needs no flow binding. Bind a fresh variable equal to the prophecy through `bind_impl` instead, so that the prophesied value carries the structure of the value it replaces. A value bound without a flow binding keeps standing for the prophecy variable directly, leaving the constraints generated for a borrow of a scalar unchanged. Fixes #176 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gbkD8SESxdRSj7taHpFJs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #176.
Cause
Env::borrow_varrebinds the borrowed variable to the borrow's prophecy variable, which stands for the value the place takes once the borrow ends. That variable comes frompush_temp_var, so it is bound as a value (TempVarBinding::Type), not with theFlowBindingthatlocate_placeandborrow_varwalk to reach through a reference, a box, a tuple, or an enum. A place whose value has structure therefore lost that structure as soon as it was borrowed.Writing to a
mutlocal is elaborated into a borrow of the local's slot (ReborrowVisitor::visit_assign), which is why the issue's straight-line reassignment hit exactly that:r = &mut bleftr's content standing for the prophecy, and elaborating*r = 20into a reborrow of*rreachedborrow_varwith no binding —borrowing unbound var. The same reassignment of a tuple local panics withderef unbound varwhen a field is projected afterwards. Reading through the reassigned local was fine, since reads go throughplace_type, which needs no flow binding; and the cross-basic-block form was fine too, since a block entry re-binds its locals from the block type.Fix
bind_prophesied_valuebinds a fresh variable refined to equal the prophecy throughbind_impl, so the prophesied value carries the structure of the value it replaces. A value that had no flow binding to begin with keeps standing for the prophecy variable directly, so the constraints generated for a borrow of a scalar are unchanged.Tests
tests/ui/{pass,fail}/reassign_mut_ref.rswrite through a&mutlocal both before and after reassigning it, and check both referents afterwards; the failing side breaks the assertion on the reassigned referent.Also checked by hand, all verifying as expected after the fix and all but the last panicking before it:
r = &mut b; *r = 20; assert!(b == 20)borrowing unbound varr = &mut b; *r = 20; assert!(b == 21)borrowing unbound varUnsatr = &mut b; *r = 20; assert!(a == 20)borrowing unbound varUnsatr = &mut a; *r = 5; assert!(a == 5)(same referent)borrowing unbound vart = (3, 4); t.0 = 5; assert!(t.0 == 5 && t.1 == 4)deref unbound varx = Box::new(2); *x = 3; assert!(*x == 3)Generated by Claude Code