Support 4-6 argument function applications in GOTO backend - #1430
Closed
keyboardDrummer-bot wants to merge 119 commits into
Closed
Support 4-6 argument function applications in GOTO backend#1430keyboardDrummer-bot wants to merge 119 commits into
keyboardDrummer-bot wants to merge 119 commits into
Conversation
…g#1361) ## Summary Loop-invariant verification diagnostics (a failing `invariant(...)` in a `while`/`for` loop) previously pointed at the **whole loop** instead of the specific invariant that failed. This change threads each invariant's source range through the loop's metadata so loop elimination can attribute each invariant's verification condition to that invariant's own source location. This support is required for [JVerify#437](strata-org/jverify#437) ## Problem In Strata, loop-invariant proof obligations are synthesized by `LoopElim` and were tagged with the loop-wide metadata `md`. The per-invariant source range was lost earlier in the pipeline: Core loop invariants are bare `(label, expr)` pairs and Core expressions are `Unit`-annotated, so they carry no source range. A front-end-only change does not help: the diagnostic location is driven by the synthesized assert's metadata, not by anything attached to the invariant expression. The fix therefore has to live in Strata. ## Solution Thread each invariant's source range through the loop's existing `MetaData` array and use it per-invariant in `LoopElim`. When a per-invariant provenance is present, each invariant's generated assert/assume is attributed to that invariant's own source location; otherwise we fall back to the loop metadata `md`, so loops not originating from Laurel (Core `.st`, C_Simp) are unchanged. ## Testing `StrataTest/.../Fundamentals/T13_WhileLoopsError.lean` adds two caret-annotated regression tests using the existing diagnostic harness (`TestDiagnostics`, `matchesDiagnostic`), which checks the exact start/end line and column of each diagnostic: 1. **`badInitialInvariant`** — a single `invariant i >= 0` that fails on entry. The caret asserts the diagnostic lands on the invariant expression, not the `while` loop. 2. **`secondInvariantFails`** — two invariants where the first holds on entry but the second (`invariant j >= 0`) does not. The caret asserts the diagnostic points specifically at the failing second invariant. This is the case that distinguishes per-invariant attribution from loop-wide attribution: before the fix the range would resolve to the loop, so the carets would not match. Verification: - `lake build Strata` — compiles, no proof breakage. - `lake build StrataTest` — all `#guard_msgs` tests pass, including the new ones. - The fallback to the loop `md` keeps existing Core `.st` and C_Simp loop diagnostics unchanged.
… into issue-924-contract-and-proof-pass
… lowering to Core (strata-org#1328) ## Summary This PR addresses the problem that Laurel composite types could *declare* instance procedures inside `composite { ... }` blocks, but they couldn't be compiled or called. The Laurel→Core translator unconditionally rejected every instance procedure with a `NotYetImplemented` diagnostic, and even without that block they would have been silently dropped: the SCC ordering in [`CoreGroupingAndOrdering.lean`](Strata/Languages/Laurel/CoreGroupingAndOrdering.lean) only enumerates `program.staticProcedures`, so anything on `CompositeType.instanceProcedures` never reached Strata Core. ### 1. Surface syntax of instance procedure calls: `obj#method(args)` - **Parser** ([`ConcreteToAbstractTreeTranslator.lean`](Strata/Languages/Laurel/Grammar/ConcreteToAbstractTreeTranslator.lean)): when the callee of `call(...)` is a `fieldAccess` node, emit `InstanceCall target method args` instead of dropping the receiver into an empty-string static call. - **Grammar** ([`LaurelGrammar.st`](Strata/Languages/Laurel/Grammar/LaurelGrammar.st)): adjust `call`'s precedence so `c#m(args)` parses as `call(fieldAccess(c, m), args)`. - **Resolution** ([`Resolution.lean`](Strata/Languages/Laurel/Resolution.lean)): pre-register instance procedures in the global scope under their lifted key (`<CompositeName>$<methodName>`). Two composites can now share a method name without colliding (Note: this change seems to resolve strata-org#1321 but the the issue of the Resolver still exists - variable scope should be carefully refactored for the resolution pass). `InstanceCall` resolution looks up the receiver's composite type, builds the lifted key, and stamps the resolved `uniqueId` on the original callee identifier. ### 2. Laurel-to-Laurel pass: `LiftInstanceProcedures` A new pass under [`Strata/Languages/Laurel/LiftInstanceProcedures.lean`](Strata/Languages/Laurel/LiftInstanceProcedures.lean), wired into [`LaurelCompilationPipeline.lean`](Strata/Languages/Laurel/LaurelCompilationPipeline.lean) with `needsResolves := true` between `EliminateValueReturns` and `HeapParameterization`. The pass: - Walks every `.Composite ct` in `program.types` and clones each `proc ∈ ct.instanceProcedures` into a fresh top-level static procedure named `<CompositeName>$<methodName>`. Body, parameters, contracts, and `invokeOn` are copied verbatim. - Walks the entire program and rewrites every `InstanceCall` whose callee so it points at the lifted name (with the receiver prepended as the first argument to match the lifted procedure's `self : <CompositeName>` parameter). - Clears `ct.instanceProcedures := []` on every composite and appends the lifted procedures to `program.staticProcedures`. Downstream simplifications: [`HeapParameterization.lean`](Strata/Languages/Laurel/HeapParameterization.lean) drops its secondary traversal of `instanceProcedures` (now always empty), and the `NotYetImplemented` block in [`LaurelToCoreTranslator.lean`](Strata/Languages/Laurel/LaurelToCoreTranslator.lean) is replaced by a defensive `StrataBug` assertion that fires only on a pass-ordering regression (all instance procedures should already be lifted and rewritten at this point). --------- Co-authored-by: olivier-aws <obouisso@amazon.com>
… into issue-924-contract-and-proof-pass
…n heap-writing procedures (strata-org#1349) ## Summary `HeapParameterization` rewrites `==`/`!=` on heap references into a `Composite..ref!` reference comparison, gated on the operand type being `.UserDefined _`. That pattern matches **both** composites (heap references, where `ref!` is correct) **and** datatypes (values, where `ref!` is wrong — it unifies a datatype value against `Composite`, which is an `int` synonym). ## Symptom The bug only surfaces inside a procedure that **writes the heap**, because only then does the heap-rewriting pass descend into the body and reach the equality arm. A datatype comparison sitting next to any heap write (e.g. a `new C` allocation) fails Core type checking with: ``` Impossible to unify (arrow Composite int) with (arrow <Datatype> ...) ``` This is a latent, general correctness bug — it affects **any** datatype `==`/`!=` in a heap-writing procedure, including a plain `datatype Pair { MkPair(a: int, b: int) }`. It is independent of any particular field type. ## Fix Guard the `ref!` rewrite on `!isDatatype` (using the existing `isDatatype` helper) in both the `.Eq` and `.Neq` arms, so datatype equality falls through to structural comparison. Composite reference-equality semantics are unchanged. ## Tests `StrataTest/Languages/Laurel/DatatypeEqHeapProcTest.lean` covers both `==` and `!=` on a datatype inside a heap-writing procedure. Verified both arms **fail Core type checking without the guard** and **verify cleanly with it** (non-vacuous regression guard). Full `Strata` + `StrataTest` build passes (554 jobs), no other regressions. ## Notes Found while investigating `Array<T>` in datatype constructor arguments (the Seq/Array PR strata-org#1073): allocating an `Array<T>` forces `writesHeap`, which made this pre-existing bug reachable. This PR fixes the root cause independently; the array-facing follow-up (lifting the validator gate, flipping that test to positive) is left to strata-org#1073. Co-authored-by: Siva Somayyajula <somayyas@amazon.com>
## Summary Adds type checking to Laurel's `Resolution.lean` as requested in strata-org#1120. ## Changes - **`resolveStmtExpr` now returns `ResolveM (StmtExprMd × HighTypeMd)`** — both the resolved expression and its synthesized type. - **Type checks added:** - Boolean conditions in `if`/`while`/`assert`/`assume` must be `TBool` - Arithmetic/comparison operands must be numeric (`TInt`, `TReal`, `TFloat64`) - Logical operands (`And`, `Or`, `Not`, `Implies`, etc.) must be `TBool` - Static call argument types must match parameter types - Instance call argument types must match parameter types (skipping `self`) - Assignment value type must match target type (single-target only) - Functional procedure body type must match declared output type (transparent bodies only) - **Diagnostics, not hard failures** — type mismatches are reported via `ResolveState.errors` and compilation continues. - **Cascading error prevention:** - `Unknown` types are compatible with everything - `UserDefined` types skip strict assignability checks (subtype/inheritance relationships are not tracked during resolution) - `TVoid` types skip assignment/output checks (statements like `return`/`while` don't produce values in the expression sense) - `MultiValuedExpr` types skip assignability checks (arity mismatch already reported separately) - Kind-mismatched type references (e.g., using a variable name as a type) produce `Unknown` to avoid cascading - **`computeExprType` in `LaurelTypes.lean` is unchanged** — it continues to work alongside the new type checking. - **Callers updated** to use the returned type from `resolveStmtExpr` (e.g., `resolveBody`, `resolveProcedure`, `resolveInstanceProcedure`, `resolveConstant`, `resolveTypeDefinition`). ## Testing All existing tests pass (`lake build StrataTest` — 592 jobs successful). Closes strata-org#1120" --------- Co-authored-by: keyboardDrummer-bot <keyboardDrummer-bot@users.noreply.github.com> Co-authored-by: Léo LEESCO <leo.leesco@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Léo Leesco <109468520+leo-leesco@users.noreply.github.com> Co-authored-by: Shilpi Goel <shigoel@gmail.com> 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: 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: 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: Sagar Joshi <72283186+sagjoshi@users.noreply.github.com>
…theory modifies tests The alwaysCallCoreFunctions optimization redirects procedure calls to their pure $asFunction versions. This produces a more complex encoding that can cause the SMT solver to return 'could not be proved' instead of 'does not hold' for modifies-clause violations and frame-related assertions, particularly on slower CI hardware. Disable the optimization for these tests (matching T2e_ModifiesArrayTheoryPerf). Also weaken T2d section 5's annotation from 'does not hold' to 'could not be proved' since the fresh-object-pinning counterexample is inherently harder to find with the new procedure-based encoding.
…r array-theory modifies tests" This reverts commit 0221896.
…direction Two issues caused test failures: 1. The alwaysCallCoreFunctions optimization was redirecting calls to procedures whose sole output is $heap. This makes the heap encoding opaque to the array theory solver, defeating the quantifier-free modifies frame. Fix: exclude procedures with a single $heap output from redirection. 2. Three test annotations used 'does not hold' for assertions where the solver cannot reliably find a counterexample with the procedure-based encoding (wildcard modifies and fresh-object-pinning cases). These matched the base branch annotations of 'could not be proved' and are restored.
…ction redirection" This reverts commit 2bcd810.
This reverts commit d3e7594.
The function→procedure changes renumber assertion/assume IDs in the generated Core programs. Update all expected_interpret/*.expected files that had hardcoded assertion numbers to use [0-9]+ patterns instead, matching the approach already used by test_missing_models.expected.
The GOTO backend's toGotoExprCtx and toGotoExpr functions only handled up to ternary (3-argument) function applications. With the removal of Laurel functions in favor of procedures, the contract pass now generates pre/postcondition checks with 4+ arguments (e.g., procedure params + return value + exception variable). Add explicit pattern matching for quaternary (4), quinary (5), and senary (6) argument applications in both LExprT.toGotoExpr and LExpr.toGotoExprCtx. This fixes pyAnalyzeLaurelToGoto failures for tests that call procedures with contracts (e.g., test_helper.procedure).
keyboardDrummer-bot
changed the base branch from
reviewed-kbd-will-merge-to-main
to
removeFunctions
June 29, 2026 20:55
Draft
keyboardDrummer
marked this pull request as ready for review
June 29, 2026 20:59
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.
Problem
The GOTO backend's
toGotoExprCtxandtoGotoExprfunctions only handled up to ternary (3-argument) function applications. With the removal of Laurel functions in favor of procedures (PR #1408), the contract pass now generates pre/postcondition checks with 4+ arguments (e.g., the procedure's parameters + return value + exception variable).This caused
pyAnalyzeLaurelToGototo fail with:Fix
Add explicit pattern matching for quaternary (4), quinary (5), and senary (6) argument applications in both
LExprT.toGotoExprandLExpr.toGotoExprCtx.Testing
pyAnalyzeLaurelToGoto:test_function_def_calls,test_class_methods,test_class_with_methods,test_datetime,test_datetime_now_tz,test_missing_models,test_multi_function,test_precondition_verification,test_timedelta_exprlake build StrataTestpasses (all Lean tests including CBMC GOTO tests)Note
The
cbmc_expected.txtfile may also need updating — many tests currently markedSKIPnow pass thepyAnalyzeLaurelToGotostage. Their final CBMC verification outcomes need to be determined in a CI environment withsymtab2gbandcbmcavailable.