fix(rt): remove mutability guard on local variable assignment#948
Conversation
MIR loop-carried control flow can assign to locals with `mutability: "Not"` (e.g. loop counters). The `#setLocalValue` rule previously required `mutabilityOf(...) ==K mutabilityMut`, causing proof execution to get stuck. Remove the mutability guard so initialized typed locals can be written regardless of their declared mutability.
Proof now reaches the target node instead of getting stuck, reflecting the immutable local reassignment fix.
06cb4be to
1f09bbf
Compare
There was a problem hiding this comment.
The mutability of a Place is not considered when assigning at the MIR level. This is a topic that is currently in some debate amongst the rustc community.
@mariaKt made a post in the stable-mir channel of the Rust zulip where we were informed that mutability is ignored. They directed us to this issue where they discuss if writing to an immutable Local should be considered UB - currently it is not.
Miri evaluates an assignment of a MIR Place by rustc_const_eval interpreter method eval_statement which does not consider mutability at all in the evaluation.
Kani also ignores the mutability of a Local when declaring them for the GOTO C backend as it does not at all read the mutability field of the LocalDecl. Similarly there is no check when for the StatementKind::Assign case for codegen_statement.
- More `BinOp::Offset` tests https://github.com/runtimeverification/mir-semantics/pull/#935 - fix: type of offset for applyBinOp(binOpOffset, ...) https://github.com/runtimeverification/mir-semantics/pull/#936 - Add type-correcting projections on pointer cast and related operations https://github.com/runtimeverification/mir-semantics/pull/#937 - Fix metadata on `PtrToPtr` cast https://github.com/runtimeverification/mir-semantics/pull/#941 - Corrections to Multisig cheatcodes https://github.com/runtimeverification/mir-semantics/pull/#942 - Handled additional Range constructor in `toSigners` side condition. https://github.com/runtimeverification/mir-semantics/pull/#944 - Update dependency: deps/stable-mir-json_release runtimeverification/mir-semantics#938 - Improved `show` printing for leaves runtimeverification/mir-semantics#946 - Fix/cachix pin no response runtimeverification/mir-semantics#950 - Hotfix/cachix pin checks runtimeverification/mir-semantics#951 - fix(rt): remove mutability guard on local variable assignment runtimeverification/mir-semantics#948 - Makefile `stable-mir-json` command to build release also runtimeverification/mir-semantics#963 - Add `rust-toolchain.toml` runtimeverification/mir-semantics#959 - Add cut-point rules for specific functions / intrinsics (via definition) runtimeverification/mir-semantics#960 - Update dependency: deps/stable-mir-json_release runtimeverification/mir-semantics#947 - Updated Solana cheatcodes with `Span` in `Call` `Terminator` (c17566dc)
Summary
Remove the
mutabilityOf(...) ==K mutabilityMutguard from#setLocalValueinrt/data.md. MIR'sLocalDecl::mutabilityis a source-level annotation, not an assignment constraint — the Rust compiler validates legality before emitting MIR and may reuse immutable locals across loop iterations.#setLocalValuerulemutabilityOf(...)) instead of forcingmutabilityMutimmutable-local-reassign.rs(loop variable withmutability: Not)Follow-up: #949 (remove mutability tracking entirely)
Context
A
for i in 0..2loop variable is bound via pattern matching (Some(i)) on each iteration, so rustc marks it asmutability: Not. However, rustc reuses the same MIR local across iterations, producing repeated assignments to an immutable local. This is valid MIR — confirmed viarustc -Z unpretty=mirand theLocalDecldocumentation.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):
With fix (GREEN):
Test plan
immutable-local-reassign.rspasses with fix, fails (stuck) without fixmake test-integration)