Skip to content

Track drop points by MIR Place, splitting partially-moved locals (fix #121, #122)#155

Draft
coord-e wants to merge 2 commits into
mainfrom
claude/pr-154-mergeable-vt1l0f
Draft

Track drop points by MIR Place, splitting partially-moved locals (fix #121, #122)#155
coord-e wants to merge 2 commits into
mainfrom
claude/pr-154-mergeable-vt1l0f

Conversation

@coord-e

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

Copy link
Copy Markdown
Owner

Supersedes #154 and #124. Fixes #121 and #122.

Motivation

Approach

Fix it in the drop-point production, keeping whole-local drops intact and dropping the still-owned siblings of a partially-moved local (sound and complete), without a hand-rolled type walk:

  • drop_point.rsMoves::collect gathers all non-reference moved operands in one body traversal: whole-local moves (keyed by location, where the local also dies) and, keyed by the parent local, the partial field moves. DropSet { drops, except } carries, per drop point, the places to drop plus the moved-out sub-places to skip. A dying local always goes in drops; its partial-move sub-places go in except.
  • env.rsdropping_assumption threads the except set and returns early on any subtree whose path matches an excepted place, so the drop resolves the still-owned siblings while skipping the moved-out ones (which are resolved at the move destination). Path::same_place performs the match and peels Deref: the drop walk inserts a Deref for every own-box the type elaboration introduces (mut/reborrowed locals, and every tuple field), whereas the moved-out places come straight from MIR without them. Moved-out places never contain a real deref (reference moves are excluded upstream), so peeling cannot conflate distinct owned places. This is what makes the skip fire for partially-moved locals that get box-elaborated (e.g. when a sibling is mutated through).
  • Drop points are collected into sets rather than Vec, dropping the manual dedup bookkeeping; the analyzer drops each point through a single drop_places helper.

Testing

New pass/fail regression tests:

Each false-assertion variant reports Unsat (rejected); the true companions verify. Full UI suite: 294 passed, 0 failed (pcsat via Docker, z3 4.15.4), plus doc-tests. cargo fmt --all -- --check, cargo clippy -- -D warnings, and git diff --check are clean.

🤖 Generated with Claude Code

@coord-e coord-e force-pushed the claude/pr-154-mergeable-vt1l0f branch 4 times, most recently from 915afc5 to 12b6c5f Compare June 29, 2026 05:35
coord-e and others added 2 commits July 1, 2026 15:42
Replace the Local-only drop-point representation with MIR `Place` so
implicit drops are tracked at the right granularity. Drop points are stored
as `HashSet<Place>` (a whole-local drop is just a place with an empty
projection), and the analyzer drops them through a single `drop_places`
helper.
A local with a partial field move (e.g. `move (_2.0)`) was still dropped
wholesale, so dropping it walked into the moved-out sub-place and resolved
the `&mut` prophecy it owns a second time. The two resolutions contradict,
making the clause body unsatisfiable, after which any assertion -- including
false ones -- "verifies" (#121, #122).

`Moves::collect` gathers all non-reference `move`d operands in one body
traversal: whole-local moves (keyed by location, where the local also dies)
and, keyed by parent local, the partial field moves. A dying local is dropped
whole, but its partial-move sub-places are passed to the drop as `except`: the
drop walk skips those subtrees (they are resolved at the move destination) and
resolves the still-owned siblings, so the fix is both sound and complete.

`Env::dropping_assumption` gains the `except` set and `Path::same_place`. The
comparison peels `Deref`, because the drop walk inserts a `Deref` for every
`own`-box the type elaboration introduces (mut locals, and every tuple field)
while the moved-out places come straight from MIR without them; moved-out
places never contain a real deref, so peeling cannot conflate distinct places.

Adds regression tests: the two original shapes (#121, #122), plus a
partially-moved local with a still-owned sibling (completeness) and a soundness
guard where the moved part is used and the sibling is reborrowed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NU1g7aHMxcSEZgeKProDr1
@coord-e coord-e force-pushed the claude/pr-154-mergeable-vt1l0f branch from 12b6c5f to 43aaebb Compare July 2, 2026 04:54
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.

Unsoundness: partially-moved locals are still implicitly dropped, resolving prophecies of moved-out &mut borrows

2 participants