Unelaborate the Box deref temps GVN rewrites into copies - #245
Draft
coord-e wants to merge 1 commit into
Draft
Conversation
`ElaborateBoxDerefs` lowers `*boxed` into a `Unique`/`NonNull`/`Transmute` chain, and `Derefer` hoists the base of that chain into a deref temp. That temp is what `unelaborate_derefs` recognizes by its `CopyForDeref` rvalue, so that the deref below it is analyzed on the place the box lives in rather than on the temp. From `-C opt-level=1` upward, GVN rewrites `_t = deref_copy P` into `_t = copy P`. The temp then survived unelaboration and the chain was resolved against it instead of against `P`. A `Box` is modeled by value, so the temp took a copy of the box's contents: the write landed on the copy while `P` kept its pre-write value, and every later read of `P` saw the stale one. A `Box` behind one more projection -- a struct field, a tuple element, an enum payload, a nested `Box` -- was thus verified against the wrong value, silently in both directions: a program whose assertion is false at run time verified as `safe`, and one whose assertion holds was rejected with `Unsat`. Match the rewritten shape too, for the locals an `ElaborateBoxDerefs` chain dereferences as a whole. Those are exactly the deref temps: a chain reads its base through `Unique`/`NonNull` field projections, which no local other than a hoisted temp stands in for. Fixes #244 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YXdyimRB3Fpje8fbfmqhiB
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.
ElaborateBoxDerefslowers*boxedinto aUnique/NonNull/Transmutechain, andDereferhoists the base of that chain into a deref temp. That temp is whatunelaborate_derefsrecognizes by itsCopyForDerefrvalue, so that the deref below it is analyzed on the place the box lives in rather than on the temp.From
-C opt-level=1upward, GVN rewrites_t = deref_copy Pinto_t = copy P. The temp then survived unelaboration and the chain was resolved against it instead of againstP. ABoxis modeled by value, so the temp took a copy of the box's contents: the write landed on the copy whilePkept its pre-write value, and every later read ofPsaw the stale one. ABoxbehind one more projection — a struct field, a tuple element, an enum payload, a nestedBox— was thus verified against the wrong value, silently in both directions: a program whose assertion is false at run time verified assafe, and one whose assertion holds was rejected withUnsat.Match the rewritten shape too, for the locals an
ElaborateBoxDerefschain dereferences as a whole. Those are exactly the deref temps: a chain reads its base throughUnique/NonNullfield projections, which no local other than a hoisted temp stands in for.Operand::Movestays untouched — that is a real ownership transfer — and so does a box copy that no chain dereferences.Fixes #244
Verification
All with
-Adead_code -C debug-assertions=false, at every level0,1,2,3,s,z:assert!(*t.0 == 1)— false0, safe at1..zassert!(*s.b == 1)— false0, safe at1..zassert!(**x == 1)— false0, safe at1..z0, Unsat at1..ztests/ui/pass/box_nested.rs0, Unsat at1..zBoxlocaltests/ui/{pass,fail}/box_field_optimized.rspin the struct-field shape at-C opt-level=1; both fail onmainand pass here. The full suite is green (328 tests).A sweep of all 166
tests/ui/passfiles at-C opt-level=1differs in exactly two lines,box_field_optimized.rsandbox_nested.rs, bothUnsat→ok. The 44 files that already fail at that level onmain— 42 ICEs, plus theghost_field.rsregression the issue notes — behave identically.Left alone
&mut Box<i64>parameter shape is fixed at1,sandz, but at2/3it still reaches the pre-existingsrc/refine/env.rs:399unwrap the issue mentions.&mut ((_1 as A).0), GVN having removed the reference indirection, andEnv::borrow_varhas no flow binding for that place:borrowing unbound varatsrc/refine/env.rs:1014. That is a separate gap in borrowing an enum payload place, and perCLAUDE.mdthe ICE path is left as it is rather than worked around.🤖 Generated with Claude Code
https://claude.ai/code/session_01YXdyimRB3Fpje8fbfmqhiB
Generated by Claude Code