Skip to content

Fix loop contract attributes on loops in match arms - #5134

Open
v3risec wants to merge 6 commits into
model-checking:mainfrom
v3risec:fix/match-arm-loop-attributes
Open

v3risec wants to merge 6 commits into
model-checking:mainfrom
v3risec:fix/match-arm-loop-attributes

Conversation

@v3risec

@v3risec v3risec commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix loop contract attribute parsing for loop expressions used directly as match arms.

Previously, the token stream passed to #[kani::loop_invariant], #[kani::loop_modifies], or #[kani::loop_decreases] could include the match arm's trailing comma. Parsing it as a syn::Stmt failed, and the macro could panic instead of expanding the loop or reporting a useful diagnostic.

Changes

  • Introduce a shared LoopInput parser for loop, while, and for expressions, supporting an optional statement semicolon or match-arm trailing comma.
  • Use the parser consistently in all three loop contract attributes.
  • Return normal compile errors for malformed input instead of panicking.
  • Add regression tests for match-arm loops, different attribute orders, and invalid attribute placement.
  • Add a negative termination-check regression to ensure a nondecreasing measure is actually rejected.

Testing

  • Targeted loop-contract expected tests pass, including positive and negative decreases checks.
  • Loop attribute UI tests and kani_macros unit tests pass.
  • The standalone branch builds successfully; Clippy and formatting checks pass.
  • Full repository CI has not been run locally.

Related work

This fixes match-arm attribute parsing, independently of the loop transformation and modifies-clause fixes in #4930, #4947, #5068, and #5071.

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

Use the same loop parser for invariants, modifies, and decreases. Consume a
match-arm comma without retaining it in the generated statement, reject mixed
terminators and non-loop inputs, and report malformed predicates without panic.

Cover loop/while/for syntax, attribute ordering, a failing decreases check,
and invalid-input diagnostics.
@v3risec
v3risec requested review from a team as code owners November 7, 2026 16:16
@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 Nov 7, 2026
@v3risec v3risec changed the title Parse loop contract attributes in match arms Fix loop contract attributes on loops in match arms Nov 7, 2026
@feliperodri feliperodri added this to the Contracts milestone Nov 7, 2026
@feliperodri
feliperodri requested a balanced review from Copilot November 7, 2026 16:38

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.

🟢 Approval recommended

No active review issues remain for the loop-contract parsing and diagnostic changes.

No open issues 🎉

What changed in this PR

Fixes Kani’s loop contract macros when a loop appears directly in a match arm, without changing loop transformation or modifies-clause behavior.

Changes:

  • Adds a shared parser for loop expressions with an optional semicolon or trailing comma, used by all three loop contract attributes.
  • Replaces parsing panics with normal compile errors.
  • Adds parser, match-arm, attribute-order, diagnostic, and negative termination-check regressions.
File Description
tests/​ui/​loop-contract-attributes/​main.rs Tests invalid attribute placement and malformed invariant input.
tests/​ui/​loop-contract-attributes/​expected Records expected compile diagnostics.
tests/​expected/​loop-contract/​match_decreases_stale.rs Tests rejection of a nondecreasing termination measure.
tests/​expected/​loop-contract/​match_decreases_stale.expected Requires the termination check to fail.
tests/​expected/​loop-contract/​loop_match_expression.rs Covers match-arm loop forms and different attribute orders.
tests/​expected/​loop-contract/​loop_match_expression.expected Requires all four harnesses to verify successfully.
library/​kani_macros/​src/​sysroot/​loop_contracts/​mod.rs Introduces the shared parser, integrates diagnostics, and adds parser unit tests.

Review effort: Balanced

@feliperodri feliperodri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, this is a clean fix: one parser for all three attributes, compile errors instead of panics, and a negative decreases test showing the check still fires inside a match arm. I added [C] Bug, [F] Crash and Z-Contracts.

Err(error) => return error.to_compile_error().into(),
};
// Validate that the attribute is applied to a loop statement.
match &loop_stmt {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: LoopInput already rejects non-loops, so this check can't fire any more. Please remove it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed the redundant check. Thanks!

// Check that the targets parse.
let _ = parse_macro_input!(attr with Punctuated::<Expr, Token![,]>::parse_terminated);
let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap();
let LoopInput(mut loop_stmt) = match syn::parse(item.clone()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: loop_invariant uses parse_macro_input!(item as LoopInput). Please use the same form here and in loop_decreases (line 1107).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated both macros to use parse_macro_input! consistently.

false => (),
}
assert_eq!(count, 3);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: please add one labeled loop ('outer: loop) in a match arm, since labels go through the same parser.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a match-arm regression test with 'outer: loop and break 'outer.

@feliperodri feliperodri added [C] Bug This is a bug. Something isn't working. [F] Crash Kani crashed Z-Contracts Issue related to code contracts labels Nov 8, 2026
Use parse_macro_input consistently for loop contract attributes, preserving
original tokens for the loop_modifies warning path. Remove the redundant loop
type check already enforced by LoopInput.

Add a labeled match-arm loop with a labeled break, and update the expected
harness count while retaining existing diagnostics and negative checks.

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

[C] Bug This is a bug. Something isn't working. [F] Crash Kani crashed Z-CompilerBenchCI Tag a PR to run benchmark CI Z-Contracts Issue related to code contracts Z-EndToEndBenchCI Tag a PR to run benchmark CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants