Skip to content

Promote shifts across blocks in reconstruct_fallible_operations#1328

Open
MavenRain wants to merge 2 commits into
AeneasVerif:mainfrom
MavenRain:1041-promote-shifts-across-blocks
Open

Promote shifts across blocks in reconstruct_fallible_operations#1328
MavenRain wants to merge 2 commits into
AeneasVerif:mainfrom
MavenRain:1041-promote-shifts-across-blocks

Conversation

@MavenRain

@MavenRain MavenRain commented Jul 16, 2026

Copy link
Copy Markdown

Fixes #1041.

Rustc evaluates a shift's operands, and emits its overflow check, before computing the destination place. When that computation needs a call, as with the overloaded index_mut in the issue, the call ends the block and the shift lands in the block the call returns to. reconstruct_fallible_operations only matches within one block, so it never pairs the check with the shift and the shift stays wrapping.

We now record these checks during the per-block pass and resolve them afterwards over the whole body. For each one we find the wrapping shift by its amount, drop the check, and compute the shift early into a fresh local where the check used to be, promoted to panic on overflow. A move then carries the result to the destination back at the original site.

Doing the computation there is sound. By the time control reaches the check, rustc has already evaluated both operands, and the check itself already reads them and panics on overflow, so the early assignment introduces no new effect and the panic stays in its original position before the call.

The new UI test covers the issue's example and a variable shift amount, which now reconstruct to panic.>> and panic.<<. The rest of the suite is unchanged and there is no AST change, so charon-ml is unaffected.

The shift in `out[i] = x >> y` with an overloaded `IndexMut` ends up in
the block the `index_mut` call returns to, so
`reconstruct_fallible_operations` does not find it, leaving the shift
as `wrap.>>` behind a leftover overflow check. See issue AeneasVerif#1041.

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@Nadrieril

Nadrieril commented Jul 17, 2026

Copy link
Copy Markdown
Member

Hi! Thank you for your contribution. Would you mind re-writing your PR description yourself? It is currently decent but still full of unnecessary details that LLMs love to add.

Then, instead of leaving the check, I would like to remove the check and do the arithmetic computation early instead (and store it in a fresh variable). That's always correct since the only side-effects are the panic and the memory accesses, all of which are already done by the overflow check.

Also your solution is doing too much work to be correct compared to what we need: we don't need to follow the graph structure since these asserts are only ever emitted by the compiler: let's simply look for all the uses the given local in the body.

Also I'd like to handle the shift checks in a single pass: iterate over the whole body looking for shifts that use the operand in question, and replace them all. To know where to insert the original computation, record the StmtLoc for the original assert. I expect this will generalize better, since it's likely we'll need something like this for other fallible operations in the future.

Rustc evaluates a shift's operands, and emits its overflow check, before
computing the destination place.  When that computation needs a call,
e.g. `out[i] = x >> y` through an overloaded `IndexMut`, the call ends
the block and the shift lands in the block the call returns to, so the
per-block pattern match never pairs the check with the shift and the
shift stays wrapping.

Record such checks during the per-block pass, then resolve them over the
whole body: find the wrapping shift by its amount, remove the check, and
compute the shift early, promoted to panic-on-overflow, into a fresh
local where the check used to be.  A move forwards the result to the
destination at the original site.

This is correct because rustc has already evaluated both operands by the
time control reaches the check, and the check already reads them and
panics on overflow, so the early computation adds no new effect and the
panic keeps its original position before the call.

Fixes AeneasVerif#1041

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@MavenRain
MavenRain force-pushed the 1041-promote-shifts-across-blocks branch from b54ee5b to 165682f Compare July 23, 2026 05:06
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.

Bug: reconstruct_fallible_operations fails to promote shifts assigned through dereferenced pointers

2 participants