Repository navigation
DFCC: scope skipped inner-loop locals to their enclosing scope - #9164
Merged
feliperodri merged 1 commit intoSep 16, 2026
Merged
Conversation
When applying loop contracts, a loop without a contract is "skipped" (not abstracted) and shares its enclosing scope's write set. get_tracked_set and get_local_set resolved the active scope from the raw loop id, while get_write_set resolved outward through skipped loops via get_first_id_not_skipped_or_top_level_id. Inside a skipped loop the three disagreed about the active scope, so writes to a skipped loop's locals were checked against the enclosing loop's assigns clause and failed spuriously. This surfaced for callees that contain a contract-less loop (for example the built-in memcmp model, whose loop is unwound at solve time) invoked from within a loop that has a contract: the callee's loop locals were reported as not assignable. See the discussion in model-checking/kani#4790. Resolve the active scope through skipped loops in get_tracked_set and get_local_set, keep skipped inner loops' locals in gen_dfcc_loop_info, and collect DECLs declared in skipped loops as top-level locals.
feliperodri
requested review from
kroening,
peterschrammel,
remi-delmas-3000 and
tautschnig
as code owners
September 14, 2026 18:19
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #9164 +/- ##
========================================
Coverage 80.83% 80.83%
========================================
Files 1717 1717
Lines 190069 190074 +5
Branches 73 73
========================================
+ Hits 153647 153653 +6
+ Misses 36422 36421 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
kroening
approved these changes
Sep 16, 2026
hung1962003
added a commit
to hung1962003/verify-rust-std
that referenced
this pull request
Oct 28, 2026
check_from_ptr_contract reaches CharSearcher::next_match only through
panic formatting, where it never runs. Its new loop contract made Kani
apply loop contracts to this harness, and CBMC 6.11 then rejects the
contract-less loop of its built-in strlen ("Check that len is
assignable", fixed by diffblue/cbmc#9164). Stub next_match in this
harness so the loop contract is not compiled into it; in the
instrumented program next_match still has no body, so the harness
proves the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When applying loop contracts, a loop without a contract is skipped (not abstracted) and shares its enclosing scope's write set.
dfcc_cfg_infot::get_tracked_setandget_local_setresolved the active scope from the raw loop id, whileget_write_setresolved outward through skipped loops viaget_first_id_not_skipped_or_top_level_id. Inside a skipped loop these disagreed about which scope was active, so writes to a skipped loop's locals were checked against the enclosing loop's assigns clause and failed spuriously.This surfaces for a callee that contains a contract-less loop — for example CBMC's built-in
memcmpmodel, whose loop is unwound at solve time — invoked from within a loop that has a contract. The callee's loop locals (res,sc1,sc2, ...) are reported as not assignable even though they are genuinely local. Originally reported against Kani, whereslice == slicelowers to thememcmpmodel: model-checking/kani#4790.Fix
get_tracked_set/get_local_set: resolve the active scope through skipped loops (get_first_id_not_skipped_or_top_level_id), matchingget_write_set.gen_dfcc_loop_info: keep skipped inner loops' locals instead of excluding them (they are not abstracted, so they belong to the enclosing scope).Testing
New regression test
regression/contracts-dfcc/loop_contracts_callee_with_loop: an outer loop with a contract calls a helper containing a contract-less loop. The helper's loop locals must be scoped to the callee. The test fails without the fix (4 spuriouscmp.assigns ... : FAILURE,VERIFICATION FAILED) and passes with it, and its "forbidden patterns" block asserts nocmp.assigns ... is assignable: FAILUREremains.