Repository navigation
verify: Make chooses with one condition one choice, kept across writes elsewhere - #44
Merged
Merged
Conversation
…s different values.
…ule in ARCHITECTURE.
This was referenced Oct 20, 2026
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.
Two chooses with the same condition were two unrelated choices, so this fails with a counterexample on 0.1.1 and main:
and so does a choice over memory across a write to another object:
DESIGN says a choose "is a function of the values its body mentions, so it is the same for the same values". Neither failure is a counterexample under that meaning. Three things broke it:
convertChooselifted every occurrence to a function of its own (__cppverify_choose_0,__cppverify_choose_1), so the two sides ofsamewere different functions.*bfreed the choice overa.The change:
choose_mapinair/src/closure.rs). A condition written differently is another choice even when it is equivalent (k > nandn < k), as in Verus.addFrameInstancesrelates each application of a choice that reads memory across every step back from its heap:agree -> c(after) == c(before). The antecedentagree(choiceReadsAgree) states that every load of the condition, at every binder value where it is evaluated, reads the same value in both memories, with its address read in each memory, and the same for every application in it that reads memory. That is the documented meaning stated as a fact, so the array theory does the address reasoning. It covers a store, a call's frame, a store of the value already there, and indirect addresses such asc[a[k]].Evaluator::checkChoicesaccepts two values of one choice with the same other arguments only when some value its condition reads differs between the two memories.readsApartbuilds a formula that follows the condition's evaluation order: a read differs if its address does or its value does, the right operand of&&counts only where the left one held, and a quantifier gives anexists. The certifier's quantifier evaluation decides it. Otherwise the counterexample is unchecked.Docs: ch13 explains what "the same" means, with checked examples (written alike, a write elsewhere, a write inside, written differently). DESIGN and ARCHITECTURE describe the sharing, the frame, and the certifier's rule. The release notes get an entry under "Failures that were not real".
Testing:
Verify/suite/choose_identity.cppcovers:CertifyTest.ChoicesAreFunctionsOfTheValuesTheyReadgives the certifier two memories that agree on the cells a choice reads (undetermined), and two that differ in one (certified). The lit test fails on main. Each of four mutations fails a test:same,renamed,write_other, andcall_writes_other;same_inner.Checked: Verify suite 424 passed, 1 unsupported; VerifyTests 61 passed, 1 skipped (encoding parity, which needs archives). Together with #43, in a build with assertions, the Verify suite passes (425, 1 unsupported). A verdict differential against main over 431 Verify sources (the ULEB128 exhaustive decoder left out: it has no choose, and every change runs only for choices) changes
choose_identity.cppandllvm_uleb128.cpp. In the latter,decode_uleb128_canonical(androundtrip_uleb128, which relies on it) timed out in main's run under the differential's load; main's binary run alone on that file verifies both, in 60 s.choose_axioms.cpptook 9.7 s instead of 35.4 s, with the same verdicts.