Skip to content

Keep the kani_loop_modifies binding alive through MIR optimization - #5068

Open
kasimte wants to merge 1 commit into
model-checking:mainfrom
kasimte:fix-5053-modifies-mir-opt
Open

kasimte wants to merge 1 commit into
model-checking:mainfrom
kasimte:fix-5053-modifies-mir-opt

Conversation

@kasimte

@kasimte kasimte commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #5053.

#[kani::loop_modifies] is not enforced at -Zmir-opt-level=2 and above. The kani_loop_modifies binding is optimized out before codegen reads it. As suggested in the issue, the fix passes a reference to the binding to an #[inline(never)] function. The binding then survives optimization.

Testing

  • New modifies_mir_opt_level.rs (-Zmir-opt-level=2): an undeclared write fails Check that hidden is assignable. A complete clause verifies.
  • The keep-alive adds two pre-loop locals, which shifts CBMC temp indices. The six loop_assigns_for_*.expected goldens are updated to match (same statuses, var_N only).

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

At -Zmir-opt-level=2 and above, MIR optimizations removed the
kani_loop_modifies binding because nothing read it, so codegen found no
modifies clause and CBMC inferred the write set from the loop body,
admitting writes the clause omitted. Pass a reference to the binding to
an #[inline(never)] function, as model-checking#4946 did for loop_decreases, so the
assignment survives optimization and the declared clause is enforced.

The keep-alive adds two MIR locals before the loop, shifting the CBMC
temporary indices in the existing loop_modifies assigns checks; the six
loop_assigns_for_* expected files are updated to match (behavior
unchanged, only the var_N index in the descriptions).

Resolves model-checking#5053
@kasimte
kasimte requested review from a team as code owners October 26, 2026 15:21
@github-actions github-actions Bot added Z-EndToEndBenchCI Tag a PR to run benchmark CI Z-CompilerBenchCI Tag a PR to run benchmark CI labels Oct 26, 2026
@feliperodri
feliperodri requested a balanced review from Copilot November 6, 2026 19:09
@feliperodri

Copy link
Copy Markdown
Member

@kasimte could you resolve the branch conflicts?

@feliperodri feliperodri added this to the Contracts milestone Nov 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

2 open issues
What changed in this PR

This PR fixes an enforcement gap where #[kani::loop_modifies] stops being honored under higher MIR optimization levels by ensuring the internal kani_loop_modifies binding remains visible through optimization.

Changes:

  • Add a regression test that runs with -Zmir-opt-level=2 and checks both incomplete and complete modifies clauses.
  • Update multiple loop-contract golden outputs due to shifted CBMC temporary indices.
  • Update the loop-contract macro expansion to keep the modifies binding alive via an #[inline(never)] keep-alive call.
File Description
tests/​expected/​loop-contract/​modifies_mir_opt_level.rs New regression test for loop modifies enforcement under MIR opt level 2.
tests/​expected/​loop-contract/​modifies_mir_opt_level.expected New expected output validating the regression failure mode.
tests/​expected/​loop-contract/​loop_assigns_for_ref.expected Update golden to match new CBMC temp indices.
tests/​expected/​loop-contract/​loop_assigns_for_ptr_fail.expected Update golden to match new CBMC temp indices.
tests/​expected/​loop-contract/​loop_assigns_for_ptr.expected Update golden to match new CBMC temp indices.
tests/​expected/​loop-contract/​loop_assigns_for_fat_ptr_fail.expected Update golden to match new CBMC temp indices.
tests/​expected/​loop-contract/​loop_assigns_for_fat_ptr.expected Update golden to match new CBMC temp indices.
tests/​expected/​loop-contract/​loop_assigns_for_empty_slice_fail.expected Update golden to match new CBMC temp indices.
library/​kani_macros/​src/​sysroot/​loop_contracts/​mod.rs Macro change to try to prevent the modifies binding from being optimized out.

Review effort: Lite

let loop_assign_keep_stmts: Vec<Stmt> = parse_quote! {
#[inline(never)]
#[kanitool::fn_marker = "kani_keep_loop_modifies_binding"]
const fn kani_keep_loop_modifies_binding<T>(_binding: &T) {}
Comment on lines +921 to +926
let loop_assign_keep_stmts: Vec<Stmt> = parse_quote! {
#[inline(never)]
#[kanitool::fn_marker = "kani_keep_loop_modifies_binding"]
const fn kani_keep_loop_modifies_binding<T>(_binding: &T) {}
kani_keep_loop_modifies_binding(&#loop_assign_ident);
};

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Z-CompilerBenchCI Tag a PR to run benchmark CI Z-EndToEndBenchCI Tag a PR to run benchmark CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loop contracts: a write missing from loop_modifies is accepted at -Zmir-opt-level=2 (the kani_loop_modifies binding is optimized out)

3 participants