Skip to content

Commit 7355946

Browse files
authored
fix(rt): remove mutability guard on local variable assignment (#948)
## Summary Remove the `mutabilityOf(...) ==K mutabilityMut` guard from `#setLocalValue` in `rt/data.md`. MIR's `LocalDecl::mutability` is a source-level annotation, not an assignment constraint — the Rust compiler validates legality before emitting MIR and may reuse immutable locals across loop iterations. - Remove mutability guard from the initialized-local `#setLocalValue` rule - Preserve original mutability on write (`mutabilityOf(...)`) instead of forcing `mutabilityMut` - Add regression test `immutable-local-reassign.rs` (loop variable with `mutability: Not`) Follow-up: #949 (remove mutability tracking entirely) ## Context A `for i in 0..2` loop variable is bound via pattern matching (`Some(i)`) on each iteration, so rustc marks it as `mutability: Not`. However, rustc reuses the same MIR local across iterations, producing repeated assignments to an immutable local. This is valid MIR — confirmed via `rustc -Z unpretty=mir` and the [`LocalDecl` documentation](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/struct.LocalDecl.html). Without this fix, proof execution gets stuck at step 693 on `#setLocalValue(place(local(8), .ProjectionElems), Integer(1, 64, false))` — the loop counter assignment that no rule can handle. ### Proof evidence **Without fix (RED):** ``` APRProof: immutable-local-reassign.main status: ProofStatus.FAILED stuck: 1, failing: 1 Leaf <k>: #setLocalValue(place(local(8), .ProjectionElems), Integer(1, 64, false)) function: repro ``` **With fix (GREEN):** ``` test_prove_rs[immutable-local-reassign] PASSED (53.31s) ``` ## Test plan - [x] `immutable-local-reassign.rs` passes with fix, fails (stuck) without fix - [x] Full integration test suite (`make test-integration`)
1 parent e23e477 commit 7355946

3 files changed

Lines changed: 25 additions & 11 deletions

File tree

kmir/src/kmir/kdist/mir-semantics/rt/data.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,21 +177,23 @@ In contrast to regular write operations, the value does not have to be _mutable_
177177
### Setting Local Variables
178178

179179
The `#setLocalValue` operation writes a `Value` value to a given `Place` within the `List` of local variables currently on top of the stack.
180-
This may fail because a local may not be accessible or not mutable.
181180
If we are setting a value at a `Place` which has `Projection`s in it, then we must first traverse the projections before setting the value.
182181
A variant `#forceSetLocal` is provided for setting the local value without checking the mutability of the location.
183182

183+
**Note on mutability:** The Rust compiler validates assignment legality and may reuse immutable locals in MIR (e.g., loop variables), so `#setLocalValue` does not guard on mutability.
184+
185+
TODO: `#forceSetLocal` is now functionally identical to `#setLocalValue` and may be removed.
186+
184187
```k
185188
syntax KItem ::= #setLocalValue( Place, Evaluation ) [strict(2)]
186189
| #forceSetLocal ( Local , Evaluation ) [strict(2)]
187190
188191
rule <k> #setLocalValue(place(local(I), .ProjectionElems), VAL) => .K ... </k>
189192
<locals>
190-
LOCALS => LOCALS[I <- typedValue(VAL, tyOfLocal(getLocal(LOCALS, I)), mutabilityMut)]
193+
LOCALS => LOCALS[I <- typedValue(VAL, tyOfLocal(getLocal(LOCALS, I)), mutabilityOf(getLocal(LOCALS, I)))]
191194
</locals>
192195
requires 0 <=Int I andBool I <Int size(LOCALS)
193196
andBool isTypedValue(LOCALS[I])
194-
andBool mutabilityOf(getLocal(LOCALS, I)) ==K mutabilityMut
195197
[preserves-definedness] // valid list indexing checked
196198
197199
rule <k> #setLocalValue(place(local(I), .ProjectionElems), VAL) => .K ... </k>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn main() {
2+
let _ = repro();
3+
}
4+
5+
fn repro() -> usize {
6+
let mut out = [0usize; 2];
7+
for i in 0usize..2usize {
8+
out[i] = i;
9+
}
10+
out[1]
11+
}

kmir/src/tests/integration/data/prove-rs/show/interior-mut3-fail.main.expected

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@
4949
│ #execBlockIdx ( basicBlockIdx ( 7 ) ) ~> .K
5050
│ function: main
5151
52-
│ (17 steps)
53-
└─ 11 (stuck, leaf)
54-
#setLocalValue ( place ( ... local: local ( 1 ) , projection: .ProjectionElems )
55-
function: main
56-
span: 79
57-
52+
│ (66 steps)
53+
├─ 11 (terminal)
54+
│ #EndProgram ~> .K
55+
│ function: main
56+
57+
┊ constraint: true
58+
┊ subst: ...
59+
└─ 2 (leaf, target, terminal)
60+
#EndProgram ~> .K
5861

59-
┌─ 2 (root, leaf, target, terminal)
60-
│ #EndProgram ~> .K
6162

6263

0 commit comments

Comments
 (0)