Laurel: avoid quantified modifies frames when using array theory#1374
Conversation
## Summary - Periodic merge of `main` into `main2` to keep main2 up to date with core improvements - 46 new commits from main since PR #1346 (mostly repo-removal PRs: #1339, #1351, #1343, #1329, #1334, plus code changes) - All conflicts resolved by keeping main2's versions (sub-repos stay local, no module migration, preserves main2-only features like Procedure.Body sum type, transparent procs, array axiomatization) - Fixed duplicate StrataDDM git entries that auto-merged into `docs/api/lake-manifest.json` and `docs/verso/lake-manifest.json` --------- Co-authored-by: Aaron Tomb <aarotomb@amazon.com> Co-authored-by: Michael Tautschnig <mt@debian.org> Co-authored-by: Kiro <kiro-agent@users.noreply.github.com> Co-authored-by: Juneyoung Lee <136006969+aqjune-aws@users.noreply.github.com> Co-authored-by: keyboardDrummer-bot <keyboardDrummer-bot@users.noreply.github.com> Co-authored-by: Mikaël Mayer <MikaelMayer@users.noreply.github.com> Co-authored-by: thanhnguyen-aws <ntson@amazon.com> Co-authored-by: Fabio Madge <fmadge@amazon.com> Co-authored-by: Joe Hendrix <joehx@amazon.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: June Lee <lebjuney@amazon.com> Co-authored-by: David Deng <daviddenghaotian@gmail.com> Co-authored-by: David Deng <htd@amazon.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Mikael Mayer <mimayere@amazon.com> Co-authored-by: Remy Willems <rwillems@amazon.com> Co-authored-by: keyboardDrummer-bot <keyboarddrummer.bot@gmail.com> Co-authored-by: Sagar Joshi <72283186+sagjoshi@users.noreply.github.com>
|
I found the term "array-theory" in this PR misleading because the PR does not directly use any SMT array theory. Instead, the PR relies on Core Map's which are not necessarily implemented using SMT array theory. In fact, I think Core is or at least was not using array theory for their maps, simply because it is or was not implemented yet. |
Each `LaurelPass.run` now receives `LaurelTranslateOptions`, instead of passes being parameterized at construction. `LaurelTranslateOptions` moves into `LaurelPass`, the pipeline threads the options into every pass, and the `comesBefore` lists no longer carry an arbitrary placeholder argument. No behavioural change: every pass ignores the new argument here.
Under `--use-array-theory` the heap `Map` is an SMT array, so for a `modifies` clause naming only individual references we can emit an enumerated, quantifier-free frame (`buildEnumeratedFrame`: overwrite the named rows) instead of the `∀`-based `buildPointwiseFrame`. The dispatch (`onlyIndividualRefs`) is restricted to bodiless procedures: a body that allocates cannot prove the whole-array equality, so bodies keep the pointwise frame, whose `< nextReference` guard tolerates allocation.
Structural #guards that the enumerated frame is quantifier-free while the pointwise frame is not (ModifiesFrameQuantifierFreeTest), the array-theory path including allocating bodies (T2c), and a perf/completeness regression for a chained spec procedure (T2e).
bf25905 to
d5ece2c
Compare
|
@keyboardDrummer-bot please resolve merge conflicts |
…laurel-modifies-array-theory
|
How come you can't make the enumerated encoding work with procedures that have a body? Suppose I have: then that would translate to something like: which has the curious implication that |
|
Yeah, I had pretty much the same example. The pointwise frame dodges this with the However to get it for bodies too, I split the jobs: callers assume the enumerated frame, and the body is checked separately by asserting the pointwise frame at every exit. what do you think (see next commit) ? @keyboardDrummer |
For an individual-ref modifies clause under array theory, callers now assume the quantifier-free enumerated frame as a free ensures, while the body proves the allocation-tolerant pointwise frame via asserts inserted before every exit, so callers never assume the quantifier. This drops the bodiless-only restriction: allocating bodies and writes-via-calls verify, and illegal writes are still rejected.
Brilliant! |
Merge reviewed-kbd-will-merge-to-main into PR1374-kiro. Resolution kept the reviewed branch's generic LaurelPass/PassMeta pass framework, but threads the LaurelTranslateOptions argument as the first parameter of each pass's run (options-first), per review preference. Preserved the PR's enumeratedModifiesClauses option and its threading through modifiesClausesTransformPass. Dropped the PR's now-superseded translateInvokeOnAxiom (invokeOn-axiom generation is handled by ContractPass on the reviewed branch). Also documents that enumeratedModifiesClauses has no effect when the procedure's modifies clause contains sets.
cc440f0 to
5caabd8
Compare
fabiomadge
left a comment
There was a problem hiding this comment.
Independent soundness pass (built at 5caabd852, suite green). Sound: callers assume the enumerated frame (free) while the body proves only pointwise; enumerated is abstractly stronger (constrains unallocated rows) but the gap is confined to fresh objects the caller never observed (opaque refs + monotone, never-reused nextReference) — imprecision, not a derivable falsehood. Verified: unmodified objects preserved, modified ones not over-preserved, wildcard effects still propagate into a caller's frame.
One non-blocking note — loop + early return rejects legal code. A body writing only its framed object then returning inside a loop fails (modifies clause does not hold); the loopless analog verifies — the loop lacks a heap-frame invariant to discharge the old() assert. Sound, just imprecise.
kadirayk
left a comment
There was a problem hiding this comment.
IIUC body proves the quantified frame, callers assume the enumerated (quantifier-free) frame, which should be faster. The default case is untouched. Tests seem to cover the changes and tricky paths. LGTM!
0a4a29b
Under
--use-array-theory, amodifiesclause naming only individual objectreferences is now encoded as a single quantifier-free heap equation instead of
a
∀-quantified frame, so the solver discharges it by array rewriting instead ofinstantiating a quantifier per object/field.
The
∀frame is unchanged for set-valued/emptymodifiesand when array theory isoff, so default behavior is untouched.