Denotational semantics for Laurel IR with concrete evaluator and transform preservation tests#631
Open
olivier-aws wants to merge 14 commits intomainfrom
Open
Denotational semantics for Laurel IR with concrete evaluator and transform preservation tests#631olivier-aws wants to merge 14 commits intomainfrom
olivier-aws wants to merge 14 commits intomainfrom
Conversation
…prehensive test suite Semantics (Strata/Languages/Laurel/): - LaurelSemantics: Shared type definitions (values, stores, heaps, outcomes) and helper functions (evalPrimOp, bindParams, store/heap operations) - LaurelDenote: Fuel-based denotational interpreter - LaurelDenoteMono: Fuel monotonicity proof for the denotational interpreter Concrete evaluator: - LaurelConcreteEval: Concrete evaluator for Laurel programs via denotational semantics Test suite (StrataTest/Languages/Laurel/): - LaurelDenoteUnitTest: Unit tests for denotational interpreter - LaurelDenoteIntegrationTest: Integration scenario tests - LaurelDenotePropertyTest: Plausible property-based tests - LaurelConcreteEvalTest: Concrete evaluator tests using Laurel parser - ConcreteEval/ module hierarchy with shared TestHelper: Primitives, Arithmetic, BooleanOps, ControlFlow, SideEffects, Procedures, Aliasing, Variables, HeapObjects, Recursion, TypeOps, Verification, EdgeCases Also fixes LiftImperativeExpressions refactoring and minor test updates.
…onstructs Remove the wildcard catch-all from evalPrimOp and replace it with explicit per-operation cases so that adding a new Operation constructor forces a build error. This prevents new operations from silently returning none. Add short-circuit handling for AndThen, OrElse, and Implies in denoteStmt instead of evalPrimOp, since these operators must not eagerly evaluate their second argument. This enables proper side-effect semantics where the second operand is only evaluated when needed. Add DivT (truncation division) and ModT (truncation modulus) cases to evalPrimOp using Int.tdiv and Int.tmod respectively. Update the fuel monotonicity proof (LaurelDenoteMono) to handle the new short-circuit cases in denoteStmt by case-splitting on the operation and argument list structure. Fix pre-existing test failures: - BooleanOps tests now pass (AndThen/OrElse have semantics) - LaurelConcreteEvalTest short-circuit tests now pass - LaurelDenoteUnitTest short-circuit tests use correct operations - DivT test updated from stuck to returning correct result Add new tests: - DivT/ModT with positive and negative operands - DivT/ModT division by zero (stuck) - evalPrimOp unit tests for DivT, ModT, AndThen, OrElse, Implies - Truncation division edge cases (negative dividend/divisor)
…exhaustive over all Laurel constructs - Add DivT/ModT to arithTotalProp property test (bug fix) - Add Implies to short-circuit ops return none section in unit tests - Add TODO for extracting shared tactic in LaurelDenoteMono.lean - Document And/Or (eager) vs AndThen/OrElse/Implies (short-circuit) distinction in evalPrimOp - Fix stale comment in BooleanOps.lean referencing And/Or instead of AndThen/OrElse for short-circuit semantics
…osition
Test 3 expected `returned: 42` but got `returned: 0` because the
block expression `{x := 42; x}` was wrapped in a procedure call
`id(...)`. The lift pass correctly lifts the call to a temporary
before the block's side effects execute, so `id(x)` was called with
x still equal to 0.
Rewrite Test 3 to place the block expression directly in return
position (`return {x := 42; x}`) where the lift pass produces the
correct order: assign x := 42, then return x. The expected output
`returned: 42` remains correct.
The denotational interpreter handles blocks in expression/argument position natively, making the liftExpressionAssignments pass unnecessary for concrete evaluator tests. Remove the applyLift parameter, its conditional logic, and the LiftImperativeExpressions import. Update all 58 call sites and related doc comments.
Delete three no-op `simp only [denoteStmt] at heval ⊢` lines in the AndThen, OrElse, and Implies cases of denoteStmt_fuel_mono. The match on the following line already unfolds denoteStmt, making these simp calls redundant and triggering unused-argument warnings during build. Remove unused simp warnings in LaurelDenoteMono.lean
Add infrastructure to run every string-based ConcreteEval test through the full Laurel→Laurel lowering pipeline, exposing which tests break after the lowering passes. Changes: - Add lowerLaurelToLaurel helper to LaurelToCoreTranslator.lean that extracts the Laurel→Laurel pass sequence (stops before Laurel→Core) - Add parseLaurelTransformed to TestHelper.lean using the new helper - Create TransformPreservation.lean with 94 tests mirroring all string-based ConcreteEval tests - Update ConcreteEval.lean barrel file Results: 77/94 tests pass (output matches direct mode). 17 tests fail due to two known categories: - heapParameterization (13 tests): composite types/heap objects - liftExpressionAssignments (4 tests): nested calls and eval order Failing tests document actual (wrong) output with explanatory comments.
- Fix failure count breakdown: 12 heapParameterization / 5 liftExpressionAssignments (was incorrectly 13/4) - Fix SideEffects Test 1 comment: the lifting pass traverses arguments right-to-left creating snapshot variables, so both block expressions independently see the original x=0, yielding add(0,0)=0 - Remove duplicate resolve call in lowerLaurelToLaurel (no-op second call after modifiesClausesTransform, mirrored from translate) - Add TODO to refactor translate to call lowerLaurelToLaurel internally to avoid duplicated Laurel→Laurel pass pipeline
Two bugs in the StaticCall case of transformExpr:
1. Nested call ordering: when an imperative call had arguments that were
themselves imperative calls (e.g. add(mul(2,3), mul(4,5))), the outer
call's temp variable was declared before the inner calls' temps.
Fix: snapshot arg prepends before adding the call's own prepends,
then restore them so inner calls are declared first.
2. Side-effect evaluation order: when arguments contained blocks with
side effects (e.g. add({x:=1;x}, {x:=x+10;x})), the lifted code
didn't preserve left-to-right evaluation order. The right arg's
side effects could clobber variables read by the left arg's result.
Fix: isolate each arg's prepends, capture side-effectful args in
temporaries, and emit prepend groups in left-to-right order using
the cons-based stack (right-to-left groups pushed first so left
groups end up on top).
All 5 liftExpressionAssignments failures in TransformPreservation
tests now pass (82/94 total, remaining 12 are heapParameterization).
Should not have been committed
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.
Denotational semantics for Laurel IR with concrete evaluator and transform preservation tests
Summary
This PR adds a fuel-based denotational interpreter for Laurel IR, a concrete program evaluator, a comprehensive test suite (~130 tests), and transform-preservation infrastructure that validates the Laurel→Laurel lowering pipeline preserves semantics. It also fixes a bug in
liftExpressionAssignmentsthat broke evaluation order.Changes
Denotational interpreter (
Strata/Languages/Laurel/)evalPrimOp,bindParams,getBody, etc.) used by the interpreter and evaluator.denoteStmt,denoteBlock,denoteArgs) — three mutually recursive functions covering allStmtExprconstructors exhaustively. Short-circuit operators (AndThen,OrElse,Implies) are handled as special cases before the generalPrimitiveOppath.evalPrimOpuses explicit per-operation fallthrough (no wildcard) so adding a newOperationvariant forces a build error.n, it succeeds with anym ≥ ngiving the same result.denoteStmttoLaurel.Programby buildingProcEnvfrom static + instance procedures, constructing the initial store from static fields, and runningmain.Test suite (
StrataTest/Languages/Laurel/)TestHelper.leanwithparseLaurel(parse + resolve) and programmatic AST helpers.Transform preservation (
ConcreteEval/TransformPreservation.lean)Runs all 94 string-based ConcreteEval tests after the full Laurel→Laurel lowering pipeline (the exact pass sequence from
LaurelToCoreTranslator.translate, stopping before Laurel→Core translation). 82/94 pass — the remaining 12 failures are all due toheapParameterizationchanging the calling convention for composite types.Bug fix:
liftExpressionAssignmentsFixed two bugs in the
StaticCallcase oftransformExpr:Nested call ordering:
add(mul(2,3), mul(4,5))— the outer call's temp variable was declared before inner calls' temps, causing the evaluator to reference undeclared variables.Side-effect evaluation order:
add({x:=1;x}, {x:=x+10;x})— the lifted code didn't preserve left-to-right evaluation order. Fixed by isolating each arg's prepends, capturing side-effectful args in temporaries, and emitting prepend groups in the correct order.Other improvements
LaurelToCoreTranslator.lean: ExtractedlowerLaurelToLaurelfunction that runs all Laurel→Laurel passes, reusable by bothtranslateand the test infrastructure.applyLiftparameter fromparseLaurel— tests validate raw Laurel semantics directly.simpwarnings inLaurelDenoteMono.lean.Testing
lake build— no warnings from new fileslake test— all tests passBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.