Merged
Conversation
Collapses the two parallel Boogie translations for Map/Filter/Count/Find/etc. (inline havoc/assume in statement context vs. axiomatized helper calls in pure/expression context) onto a single helper-function path. Each call site now emits one `$t := $HelperQuantifierHelper_fn(...);` line instead of 5-15 lines of inline assumes, and Z3 reasons over triggered axioms declared once per (fn, type-instantiation) pair rather than re-matching a fresh forall at every call site. Fixes a pre-existing bug in the `find_indices` helper axiom where the predicate was applied to the result index (`FN(res[i])`) instead of the source element (`FN(v[res[i]])`), and rewrites the `filter` helper axiom to tie results to `find_indices` so order and multiplicity are preserved. Adds a dedicated `Count` helper with a bidirectional recursive axiom so Z3 can unfold concrete ranges from either end (forward for literal values, backward for loop invariants that extend the range). Adds incremental step axioms to `map` and `find_indices` for the same loop-invariant reason. Also fixes a latent test-data bug in nested_pure.ok.move (test_find_indices used v=[10,20,30,40] with an is_even predicate, expecting [1,3] — all four values are even, so the correct result is [0,1,2,3]; the weak old axiom masked this). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrites the map/filter/find_indices/find_index/count axioms with explicit recursive incremental clauses (bidirectional for count, end-recursion for the others), lets the 9 previously-disabled assertions in map.ok.move verify, adds a dedicated SumMap helper with its own recursive axiom so sum_map no longer routes through sum(map(...)), and decouples filter from find_indices (filter now recurses directly). Drops the find_indices "completeness" clause the filter axiom used to lean on. Adds three new loop-invariant tests (filter_loop, find_indices_loop, sum_map_loop) and empty-vector edge-case tests across count, find_indices, sum_map, map, and map_range. Note: filter_loop_ok and find_indices_loop_ok are borderline on the 45s per-procedure timeout; they pass in isolation but can flake under heavy parallel load, same as the existing count_loop_ok and vec_map_remove tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends the iterator-helper test coverage: - range_map: add incremental axiom on `end` (same nested-var form as map). - range_map_loop.ok.move: loop extending the integer range step by step. - find_index_loop.ok.move: second variant using find_index_range directly in the loop invariant, exercising the bidirectional find_index axiom. - multi_arg_loop.ok.move: count/filter loops with a captured runtime threshold, stressing the captured_args_tail template path for recursive axioms. - nested_helpers.ok.move: compose two helpers in one expression (count-of-filter, sum_map-of-filter, find_index-of-map, count-of-map). - count_big / filter_big / find_indices_big / sum_map_big: concrete 16-element vectors to stress the recursive axioms and act as regression canaries. All new tests verify individually. The pre-existing borderline-timeout tests (filter_loop, find_indices_loop, count_loop, vec_map_remove) remain flaky under parallel load, which is documented behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A loop with the invariant `c + count(v, i, n) == count(v, 0, n)` — the "unprocessed suffix" pattern. Preserving this across the loop body needs `count(v, i, n) == (if p(v[i]) then 1 else 0) + count(v, i+1, n)`, which is the left-step (start-recursion) clause of the count axiom. Acts as a regression guard: if the start-step axiom is ever dropped or weakened, this test will catch it. Complementary to count_loop.ok.move which uses the prefix-invariant form (end-recursion). Verified: sum_map and count both have bidirectional recursion and handle this pattern. Vector-valued helpers (map, filter, find_indices) don't currently need start-recursion — constructing a Move test that exercises it for vector ops requires vector concat semantics the spec language doesn't expose cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a top-of-macro comment block explaining: - what helpers are axiomatized and why (one uninterpreted function per unique (kind, predicate, type-instantiation) from mono_analysis) - the main/incremental axiom pattern each helper follows and which helpers have which recursion directions (and why — suffix-invariant patterns only exist for scalar-valued helpers in practice) - all template variables (FN/QP/QA/RT/CAT/EAB/EAA) with concrete examples of what each expands to - a step-by-step recipe for adding a new helper kind, pointing to the corresponding Rust code in stackless_bytecode.rs, bytecode_translator, lib.rs, and this file Pure docs; no runtime change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a new `prover::vector_iter::concat<T>(v1, v2): &vector<T>` native,
wired through model.rs / mono_analysis / native.bpl. The Boogie side is a
one-line inline wrapper over the existing `ConcatVec` operation in the
vector theory. This gives Move spec writers a direct way to express
"processed prefix plus unprocessed suffix" invariants over vectors.
Also changes `find_indices` and `find_indices_range` macros to return
`&vector<u64>` instead of `vector<u64>`, aligning them with `filter`,
`filter_range`, `map`, `map_range`, and `range_map`. Updates all call
sites in the test suite to drop the now-unnecessary `&` re-borrow and
add `*` where owned-vector equality is expected.
The concat native is validated by a new concat.ok.move test:
- concrete equality with literal vectors
- identity lemmas (empty left, empty right)
- length additivity
- round-trip with slice (concat of slices recovers the original)
- a loop invariant using concat(processed, slice(v, i, n)) == v to
prove a straightforward copy-loop correct
I attempted to add start-recursion ("left step") axioms for map, filter,
and find_indices to support suffix-invariant loop proofs that use the
new concat natively, but the added axioms caused Z3 to time out on the
existing big-vector stress tests (map_big, filter_big, find_indices_big)
even with tighter triggers and compound-term patterns. Reverted; the
vector-valued helpers stay end-recursion-only. The start-recursion
question for map/filter/find_indices remains open and can be revisited
if a future use case makes it worth the solver cost.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduce the _big tests from 16-20 elements to 8 so the recursive helper
axioms have less to unfold. They still serve as regression canaries for
scaling behavior, but the smaller size gives Z3 a more predictable budget.
Motivated by an attempt to add start-recursion axioms for map/filter/
find_indices (for suffix-invariant loop proofs enabled by the new concat
native). Several approaches were tried:
- {:weight 0} on start-step: worked for filter_suffix_loop, broke map and
find_indices suffix tests
- {:weight 100} on start-step: suffix tests fail (too deprioritized)
- compound triggers on both the current and recursive helper terms: Z3
can't match `start + 1` syntactically against concrete arithmetic, axiom
never fires
- direct ConcatVec form for the start-step: breaks .fail tests because Z3
gets lost unfolding recursively
None produced a state where both the concrete-vector big tests and the
suffix-invariant loop tests passed reliably. Reverted all axiom changes
and deleted the experimental suffix-loop tests. The size-shrink is the
one piece of the experiment that's worth keeping.
See .claude memory note project_start_recursion_attempt.md for detailed
findings on the Z3 quantifier-instantiation interactions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds left-step (start-recursion) axioms for map and filter that enable
suffix-invariant loop proofs of the form
concat(r, map_range(v, i, n, f)) == map(v, f)
expressed via the concat native.
The key trick: a naive `{helper(v, start, end)}` trigger on a recursive
axiom causes matching loops — each instantiation introduces a new helper
term that matches the trigger and fires again. Earlier attempts to use a
compound trigger with `start + 1` failed because Z3's trigger matcher is
syntactic, not arithmetic — it can't unify `start + 1` against a ground
term like `i + 1` at match time.
The fix: use a fresh bound variable `next_start` in the pattern and guard
with `next_start == start + 1` in the body:
axiom (forall QP, next_start ::
{helper(v, start, end), helper(v, next_start, end)}
next_start == start + 1 && start < end ==> body)
Trigger matching is purely syntactic so Z3 can find matches in the
E-graph; the guard filters to consecutive pairs at instantiation time.
The compound trigger requires BOTH helper terms to already be present,
which breaks the matching loop — a single fresh `helper(v, 0, N)` can't
fire the axiom by itself — while still firing when a loop invariant has
both the current and previous iteration's helper terms in context.
New tests:
- map_suffix_loop.ok.move: loop body `r.push_back(f(v[i]))` maintaining
`concat(r, map_range(v, i, n, f)) == map(v, f)`
- filter_suffix_loop.ok.move: similar for filter
find_indices didn't work out — adding the compound-trigger start-step
axiom for find_indices caused find_indices_loop_ok (prefix-invariant) to
deterministically time out, likely because of interaction with the
separate sorted-order axiom. Left as future work.
issue-416.move.snap updated for BPL line-number shifts from the new
axioms (cosmetic, verification result unchanged).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends the fresh-bound-variable compound-trigger pattern from the
previous commit to find_indices: now both end-step and start-step use
the form
axiom (forall ..., prev_end: int ::
{helper(v, start, end), helper(v, start, prev_end)}
prev_end + 1 == end && start < end ==> body)
with a fresh bound `prev_end` (or `next_start`) in the pattern plus a
guard in the body. This lets find_indices participate in suffix-invariant
loop proofs — `find_indices_suffix_loop.ok.move` verifies.
The trade-off: concrete-vector verifications of exact `find_indices(v, p)
== vector[...]` equality can no longer be proved via recursive unfolding,
because a single fresh `find_indices(v, 0, N)` call can't fire the
compound-triggered axioms on its own. Tests that relied on that form
have been weakened to assert main-axiom-provable properties (length
bound, in-range indices, predicate holds at each, strict ordering)
instead of exact-vector equality. Affected tests:
- find_indices_big.ok.move — now asserts length bound + per-element
properties instead of `indices == vector[1,3,5,7]`
- find_indices.fail.move snapshot — error locations shift slightly for
the .fail tests that used `vector::borrow` (now reporting abort check
failure instead of ensures-doesn't-hold; still failure, still correct)
- map.ok.move's test_spec — `find_indices == vector[0,2]` replaced with
length bound; the count/any/all assertions still pin down behavior
- map.ok.move's test_empty — same treatment
- nested_pure.ok.move and nested_pure_params.ok.move — `find_indices ==
vector[1,3]` replaced with length bounds
- find_indices.ok.move — test_find_indices_range_valid_indices now
guards the vector::borrow with `if (len > 0)` since the borrow's
abort check can no longer be discharged via unfolding
I also tried wiring an `ext(axiom)` lemma (e.g. "for this specific 8-
element vector, length is exactly 4") into find_indices_big. It didn't
work: the per-spec scenario analyzer doesn't bring axiom functions into
scope unless the test directly calls them, and an axiom whose body goes
through macro-expanded iterator helpers doesn't plug into the dependency
graph cleanly. Left as future work, documented in the test file comment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
map.ok.move had become a grab bag — earlier in this branch I uncommented assertions for filter/find/find_index/find_indices/count/any/all/sum_map that were sitting commented out in the file, and they stuck around as duplicated coverage. Each of those helpers already has its own *.ok.move test file, so the duplication was pure noise. Strip map.ok.move's test_spec and test_empty back to map-only assertions. Move the any/all empty-vector case that was in map.ok.move's test_empty into any_all.ok.move where it belongs (none of the other helpers needed the move — their .ok.move files already had empty-vector coverage). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three tests previously had concrete `find_indices(...) == vector[...]` equality assertions that I'd weakened to main-axiom-provable length bounds when find_indices moved to compound-trigger recursive axioms (which prevent recursive unfolding from a fresh helper call). They're now restored to the exact-value form, paired with a per-spec `extra_bpl` file that supplies a single-trigger end-step axiom for that spec's specific find_indices helper instance. Tests: - find_indices_big.ok.move — `indices == vector[1, 3, 5, 7]` on [1..8] with `is_even` - nested_pure.ok.move::test_find_indices and ::test_find_indices_range — `vec_find_even_indices == vector[1, 3]` on [11, 20, 31, 40] - nested_pure_params.ok.move::test_find_indices and ::test_find_indices_range — `vec_find_divisible_indices(v, 20) == vector[1, 3]` on [10..40] Each paired with its own `*.ok.bpl` file that declares a single-trigger end-step axiom for the specific helper instance it uses. Using `#[spec(..., extra_bpl = b"...")]` scopes the axiom to exactly the specs that need it — no pollution of funs_abort_check.bpl or other tests, and no interference with the global compound-trigger axioms that keep find_indices_suffix_loop working. The axioms are structurally identical to what the quantifier_helpers_module Tera macro would generate if the helper used a single trigger; they're hand-written here because there's currently no way to parameterize the macro's trigger strategy per-instance. If this pattern becomes common, a next step would be to add a template variable (e.g. `instance.unfold_trigger: "compound" | "single"`) and select per-instance, rather than hand-writing the axiom in each extra_bpl file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
multi_arg_loop.ok.move had two spec(prove) functions — one for count, one for filter — each exercising the captured_args_tail axiom path. Under parallel test load, the combined test sometimes timed out (~28s total, with two ~6s Check tasks that under CPU contention would creep past the 45s per-task budget). Splitting into multi_arg_count_loop.ok.move and multi_arg_filter_loop.ok.move keeps each test's scenario smaller and eliminates the axiom interaction between count and filter in the same Z3 invocation. Total time drops from ~28s to ~17s. Full-suite run now passes with 0 flakes (392/392). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Audit-driven improvements to the quantifier helper axioms: 1. map main axiom: Fix unsound `LenVec(res) == end - start` (gives negative length when start > end). Now uses `(if start <= end then end - start else 0)`. Added `0 <= LenVec(res)` lower bound and `start >= end ==> res == EmptyVec()` base case, matching what filter and find_indices already had. 2. range_map main axiom: Added `0 <= LenVec(res)` lower bound and the empty-range base case `start >= end ==> res == EmptyVec()`. 3. sum_map: Split from one monolithic axiom (base + both recursive directions) into three separate axioms (bounds/base, left-step, right-step), mirroring count's structure. This lets Z3 instantiate only the clause it needs for a given proof, reducing wasted work in proofs that only need one direction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Completes the bidirectional axiom coverage across all seven helpers: - range_map: add compound-trigger start-step axiom (was the only vector-valued helper without one). New range_map_suffix_loop.ok.move verifies `concat(&r, range_map!(i, n, f)) == range_map!(0, n, f)`. - find_index: add find_index_suffix_loop.ok.move exercising the existing start-step axiom in a suffix pattern (find_index_range(v, 0, i, p) == none maintained until the first match). - sum_map: add sum_map_suffix_loop.ok.move exercising the existing start-step axiom (s + sum_map_range(v, i, n, f) == sum_map(v, f)). Every helper now has both prefix-invariant AND suffix-invariant loop test coverage: | Helper | Prefix loop | Suffix loop | |---------------|-------------|-------------| | find_indices | ✓ | ✓ | | filter | ✓ | ✓ | | find_index | ✓ | ✓ (new) | | map | ✓ | ✓ | | range_map | ✓ | ✓ (new) | | count | ✓ | ✓ | | sum_map | ✓ | ✓ (new) | 394 tests pass, 0 failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Re-adds the completeness axiom that was removed during the earlier
axiom restructuring:
forall j :: start <= j < end && FN(v[j]) ==> ContainsVec(res, j)
This says every index in the range where the predicate holds MUST be in
the result. Without it the main axiom only guaranteed soundness (every
element of res is a valid match) but not completeness (a find_indices
returning [] would satisfy the axiom for any input).
Stated as a separate axiom from the main one so the ContainsVec
existential doesn't fire on every main-axiom trigger match.
For filter, the equivalent clause (ContainsVec-based provenance and
completeness) was tried but caused 8x slowdown on filter loop tests
(7s → 51s) due to the nested forall-exists interacting with filter's
single-trigger end-step axiom. Reverted for filter; the recursive step
axioms provide equivalent information when they fire. Left as a future
improvement if a more Z3-friendly formulation is found.
Side effect: find_indices.fail.move now reports all five failures as
clean "ensures does not hold" instead of a mix of "code should not
abort" and "ensures does not hold". The completeness clause lets Z3
prove length >= 1, so vector::borrow abort checks pass and the ensures
error is the only one reported.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switches map, filter, and range_map end-step axioms from single triggers
to compound triggers (fresh-bound-variable pattern), matching find_indices.
All four vector-valued helpers now use the same trigger strategy:
axiom (forall QP, prev_end: int ::
{helper(QA), helper(v, start, prev_end, ...)}
prev_end + 1 == end && start < end ==> body)
for end-step, and the mirror form for start-step.
This eliminates the trigger asymmetry from the previous commits. The
design is now uniform:
- Vector-valued helpers (find_indices, filter, map, range_map): compound
triggers on BOTH end-step and start-step
- Scalar-valued helpers (count, sum_map, find_index): single triggers on
both (matching loops are less of a concern for scalar values)
For map and range_map, the compound end-step trigger is "free" because
their main axioms provide element-wise forall (res[i] == FN(v[i])) via
a single trigger, which is sufficient for concrete tests without
recursive unfolding.
For filter, the main axiom only says "FN(res[i])" — not enough for
concrete tests. Tests that assert filter(...) == vector[...] need a
per-spec extra_bpl file with a single-trigger end-step axiom for the
specific helper instance. New extra_bpl files:
- filter_big.ok.bpl, filter.ok.bpl (filter.ok test)
- filter_x_is_10.bpl, filter_x_is_even.bpl (filter.fail test)
- nested_helpers.ok.bpl (nested composition test)
- nested_pure_filter.bpl (nested_pure.ok filter specs)
- nested_pure_params_filter_divisible.bpl, nested_pure_params_filter_in_range.bpl
The old combined bpl files (nested_pure.ok.bpl, nested_pure_params.ok.bpl)
are split into per-helper-per-predicate files to avoid cross-referencing
undeclared functions when different specs in the same file use different
predicates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Re-adds the ContainsVec-based axioms for filter that were removed earlier
due to 8x slowdown. Now that filter uses compound triggers on both
end-step and start-step, the ContainsVec existentials are affordable —
the step axioms fire only when both consecutive helper terms are in the
E-graph (loop contexts), not on every fresh call.
Two separate axioms added back to filter:
- Provenance: every element of res came from v
(forall i :: ContainsVec(v, res[i]))
- Completeness: every element of v in [start, end) satisfying FN is in res
(forall j :: FN(v[j]) ==> ContainsVec(res, v[j]))
These were the same clauses that caused 51s timeouts when filter had
single-trigger end-step — the change to compound triggers is what made
them tractable. Full suite time actually dropped from ~730s to ~395s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Updates the top-of-macro docstring to accurately describe the current architecture: - Lists exactly 7 helper kinds (was listing QuantifierType variants instead of QuantifierHelperType variants) - Documents the compound-trigger strategy for vector-valued helpers (was describing the old single-trigger + weight-hint approach) - Describes the per-spec extra_bpl pattern for concrete-value tests - Documents the completeness/provenance axioms for find_indices + filter - Updates the "adding a new helper kind" recipe with the extra_bpl step Also removes the `if (len > 0)` guard from find_indices.ok.move's test_find_indices_range_valid_indices — the completeness axiom now proves length >= 1 from the predicate match at v[2], making the guard unnecessary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
msaaltink
reviewed
Apr 13, 2026
msaaltink
reviewed
Apr 13, 2026
Two review-driven improvements:
1. In the false branches of step axioms (where the predicate doesn't
hold), replace the verbose element-wise equality:
LenVec(res) == LenVec(prev) &&
(forall j :: ReadVec(res, j) == ReadVec(prev, j))
with the direct structural equality:
res == prev
This is simpler, gives Z3 more information (array identity, not just
element-wise agreement), and avoids instantiating the inner forall.
Applied to all 8 false branches across find_indices and filter step
axioms.
2. Add explicit $IsValid axioms for vector-valued helpers:
axiom (forall QP :: {helper(QA)}
$IsValid'vec'u64''(helper(QA)))
The reviewer noted that while validity is inferable from the soundness
axiom (all elements are in valid ranges), $IsValid functions are not
inlined in Boogie, so Z3 won't derive it automatically. The explicit
axiom gives Z3 a direct handle on the well-formedness of the result.
Added for find_indices, filter, map, and range_map. A new
`result_is_valid_suffix` field in QuantifierHelperInfo provides the
correct type suffix (e.g. "vec'u64'") to the Tera template.
Full suite: 394 passed, 0 failures, ~371s (down from ~395s — the
simpler equality and explicit validity give Z3 less work).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
msaaltink
reviewed
Apr 13, 2026
Adds congruence axioms stating that extensionally-equal input vectors
produce equal results:
axiom (forall QP, v2 ::
{helper(v, start, end, ...), helper(v2, start, end, ...)}
$IsEqual(v, v2) ==> helper(v, ...) == helper(v2, ...))
Since the helpers are uninterpreted Boogie functions, Z3 only gets
automatic congruence for Boogie `==` (structural equality), not for
the weaker `$IsEqual` (extensional equality: same length + same
elements). In the default non-extensional vector theory, two vectors
with identical elements can be `$IsEqual` without being `==`, so Z3
needs the explicit bridge.
Applied to all 6 vector-taking helpers: find_indices, filter,
find_index, map, count, sum_map. (range_map has no input vector.)
Each axiom uses a compound trigger requiring both `helper(v, ...)`
and `helper(v2, ...)` in the E-graph, so it only fires when Z3 is
actively reasoning about two helper calls with potentially-equal
input vectors.
New `input_vec_is_equal_suffix` and `input_elem_type` fields in
QuantifierHelperInfo provide the type information the Tera template
needs for the v2 parameter declaration and $IsEqual suffix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switches all inner-quantifier guards in the helper axioms from:
(forall i :: 0 <= i && i < LenVec(res) ==> ...)
to:
(forall i :: InRangeVec(res, i) ==> ...)
InRangeVec is deliberately non-inlined in the vector theory — the
comment on its declaration says "it appears important to have this
uninterpreted for quantifier triggering." Using it as the guard gives
Z3 a potential trigger term `{InRangeVec(res, i)}` for inner quantifiers,
which fires in sync with other vector axioms in the prelude that use
the same guard pattern.
Performance: neutral (~365s, same as before). The change is for
consistency with range_map (which already used InRangeVec) and the
broader vector prelude conventions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For find_indices, filter, map, and range_map, merge the separate axioms
that share the same single trigger `{helper(QA)}` into one combined
axiom body per helper. This reduces the number of axiom instances Z3
tracks without changing what fires or when:
- find_indices: $IsValid + main + completeness → 1 axiom
- filter: $IsValid + main + provenance + completeness → 1 axiom
- map: $IsValid + main → 1 axiom
- range_map: $IsValid + main → 1 axiom
count and sum_map are left split (bounds/base, left-step, right-step)
because they use single triggers where Z3 benefits from selective
instantiation — the step axioms chain recursively and Z3 should be
able to skip the bounds axiom when only unfolding.
Axioms with DIFFERENT triggers (congruence, sorted-order, end-step,
start-step) remain separate — they must be, since Boogie doesn't
support multiple trigger sets on a single axiom.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Trim verbose multi-line comments down to single-line summaries and switch inline comments to lowercase per project convention. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5c54681 to
06ebfc7
Compare
CAT (trailing captured args for recursive helper calls) was redundant —
it's derivable from EAB and EAA:
if EAB == "": CAT = EAA
else: CAT = ", " ~ EAB ~ EAA
Remove the `captured_args_tail` field from QuantifierHelperInfo and
compute CAT in the Tera template instead.
Also changes EAB format from trailing-comma ("$t0, ") to plain
comma-separated ("$t0"), and introduces EABC (EAB-with-comma) as a
template variable for clean predicate calls: `{{FN}}({{EABC}}expr{{EAA}})`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FindIndices needs U64 because its result *elements* are indices. Count needs U64 because its *scalar result* is a count. Same type, different semantic reason — split the match arm and document each. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename dst_elem_boogie_type → vec_elem_type and make it clear it's only meaningful for vector-valued helpers (find_indices, filter, map, range_map). For scalar helpers (count, sum_map, find_index) the result type and $IsValid suffix are now set to empty strings since the template never uses them — their return types are hardcoded as `int` in the template. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No dead code path for scalar helpers — vec_elem_type is now computed only when it's actually used. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove stray blank lines after comments, fix broken "main axiomfrom range" comment, and add consistent "// end-step" / "// start-step" labels to find_index's step axioms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EAB gets the trailing ", " added in the template (from the raw instance.extra_args_before), and CAT is derived alongside it. Both are computed once at the top of quantifier_helpers_module instead of per-helper. Removes the intermediate EABC variable and the per-helper CAT set blocks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <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.
No description provided.