Skip to content

Add EliminateReturnStatements and ContractPass#34

Draft
keyboardDrummer-bot wants to merge 205 commits intomainfrom
contract-pass-v2
Draft

Add EliminateReturnStatements and ContractPass#34
keyboardDrummer-bot wants to merge 205 commits intomainfrom
contract-pass-v2

Conversation

@keyboardDrummer-bot
Copy link
Copy Markdown
Collaborator

Summary

Adds two new Laurel-to-Laurel passes, extracted from #28 with modified contract pass semantics:

  • EliminateReturnStatements: rewrites return to exit statements by wrapping the body in a labelled block. This ensures code placed after the body (e.g., postcondition assertions) is always reached.

  • ContractPass: translates away pre and postconditions entirely by introducing assertions and assumptions at call sites and at procedure starts and ends.

Contract Pass Design

Key differences from the version in #28:

  1. $post procedures are functional (isFunctional := true)
  2. $post takes all inputs AND all outputs of the original procedure as parameters
  3. $post does not call the original procedure — it simply returns the conjunction of postconditions over the combined input+output parameters
  4. At call sites, before the call, all input arguments are assigned to temporary variables. Those temps are then passed to both the actual call and the assumed $post call (along with the output variables after the call)

Pipeline Integration

Both passes are integrated into LaurelCompilationPipeline.lean, running after the existing Laurel-to-Laurel passes and before the final translation to Core.

keyboardDrummer-bot and others added 30 commits April 19, 2026 10:28
- Add OpaqueClause category and optional opaque parameter to procedure/function
  grammar ops, placed between invokeOn and ensures clauses
- Update ConcreteToAbstractTreeTranslator to parse the new opaque argument and
  use it to determine Body.Opaque vs Body.Transparent
- Update LaurelFormat to output 'opaque' for Opaque bodies
- Update all Laurel test files: procedures with ensures clauses now use explicit
  opaque keyword; procedures that only had 'ensures true' now use just 'opaque'
- Remove comments about considering more explicit opaque syntax
- Update PythonRuntimeLaurelPart Laurel source strings with opaque keyword

Closes #9
Add a resolution check that emits a diagnostic when a non-functional
procedure (declared with 'procedure', not 'function') has a transparent
body (no 'ensures' clause). The diagnostic message advises adding
'ensures true' to make the procedure opaque.

Changes:
- Resolution.lean: Add transparent body check in resolveProcedure and
  resolveInstanceProcedure
- LaurelToCoreTranslator.lean: Add checkTransparentBodies option to
  LaurelTranslateOptions, filter diagnostics when disabled
- PySpecPipeline.lean: Disable transparent body check for Python pipeline
  (Python-generated procedures will be updated separately)
- T20_TransparentBodyError.lean: New Laurel test expecting the diagnostic
- T2_ModifiesClauses.lean: Updated as specified in issue #10
- All other Laurel test files: Add 'ensures true' (and 'modifies' clauses
  where needed) to procedures with transparent bodies
- T8c_BodilessInlining.lean: Update expected assertion label offset
- AnalyzeLaurelTest.lean: Filter transparent body errors in resolution test

Closes #10
Merge origin/add-opaque-keyword into disallow-transparent-statement-bodies.
Resolve conflicts in T8c_BodilessInlining.lean and T2_ModifiesClauses.lean.
Replace all 'ensures true' with 'opaque' in Laurel test files.
Update diagnostic message to reference 'opaque' keyword.
… bodies

- Remove checkTransparentBodies from LaurelTranslateOptions and its
  filtering logic in translateWithLaurel
- Remove checkTransparentBodies := false from PySpecPipeline
- Change Python-generated procedure bodies from Transparent to Opaque
  in PythonToLaurel (method bodies and __main__)
- Convert spec preconditions from body assertions to requires clauses
  (buildSpecBody → buildSpecPreconditions) with property summaries
- Include procedures with preconditions in inlinableProcedures so the
  Python-to-Laurel translator generates calls for them
- Pass userSourcePaths to splitProcNames in AnalyzeLaurelTest so spec
  procedures are classified as prelude (not inlined away)
- Remove transparent body error filtering from AnalyzeLaurelTest
- Broaden precondition violation test checks (no longer filter by
  servicelib_Storage_ prefix since labels now use callElimAssert_)
Resolve merge conflicts:
- LaurelGrammar.lean: keep opaque keyword grammar change comment
- PythonToLaurel.lean: keep Opaque bodies, remove non-existent md field
- ToLaurel.lean: take main's buildSpecBody refactoring (SpecAssertMsg,
  SpecExprContext, type-checked preconditions) but return .Opaque instead
  of .Transparent
- AbstractToConcreteTreeTranslator.lean: add opaqueClause argument to
  procedureToOp to match the grammar definition
- Resolution.lean: fix proc.md -> proc.name.md (Procedure has no md field)
- LaurelFormat.lean: accept deletion (replaced by DDM grammar formatter)
- LiftHolesTest.lean: take main's DDM-based formatting
- ConstrainedTypeElimTest.lean: use main's DDM format with opaque keyword
- AnalyzeLaurelTest.lean: take main's entry-point/inlining refactoring,
  re-apply PR's foundAlwaysFalse simplification
- PreludeVerifyTest.lean: take main's expected output
Resolve conflicts:
- LaurelGrammar.lean: keep opaque keyword comment
- LaurelFormat.lean: accept upstream deletion (replaced by AbstractToConcreteTreeTranslator)
- AbstractToConcreteTreeTranslator.lean: add opaque argument to procedureToOp
- LaurelGrammar.st: fix opaqueClause format string to use newline+indent prefix
- LiftHolesTest.lean: add opaque to hole function expected outputs
- ConstrainedTypeElimTest.lean: add opaque to test procedure expected output
- AbstractToConcreteTreeTranslatorTest.lean: add opaque to roundtrip test inputs/outputs
- T8d_HeapMutatingValueReturn.lean: add opaque to procedures with ensures clauses
…que in tests

- Fix opaqueClause grammar to include newline prefix ("\n  opaque")
- Fix parser to still create Opaque body when postconditions are present
  even without explicit opaque keyword
- Only emit opaqueClause for Opaque bodies with postconditions, impl, or modifies
  (empty Opaque bodies like hole functions don't need it)
- Add opaque to remaining test procedures with transparent bodies:
  T19_BitvectorTypes, T20_InferTypeError, T21_ExitMultiPathAssert,
  T8d_HeapMutatingValueReturn, DuplicateNameTests
- Update expected output in AbstractToConcreteTreeTranslatorTest
The change from Transparent to Opaque bodies for spec procedures caused
them to be excluded from inlinableProcedures, which meant the Python-to-
Laurel translator generated Holes instead of direct calls for spec
procedure invocations. This broke the AnalyzeLaurelTest precondition
violation tests (no assertions were generated in the inlined body).

Fix by matching on body variants: Transparent and Opaque-with-implementation
are both inlinable. Also update VerifyPythonTest to expect opaque bodies
for Python-generated methods.
The opaque body changes cause additional postcondition VCs to appear
in test_method_call_with_kwargs and test_method_kwargs_no_hierarchy.
Procedures are now opaque, so they generate an additional postcondition
verification condition. Update the 12 affected expected files.
…opaqueSpec

- Changed procedureToOp to produce opaqueSpec op with ensures and modifies
  as nested args (matching the grammar), instead of separate top-level args
- Added missing opaque keyword in MapStmtExprTest and T2_ModifiesClauses tests
- Grammar: Add `modifiesWildcard` op for `modifies *` syntax
- Parser: Handle `modifiesWildcard` by producing `StmtExpr.All` in modifies list
- Printer: Emit `modifiesWildcard` when modifies list contains `All`
- ModifiesClauses: Skip frame condition generation for wildcard modifies
- FilterNonCompositeModifies: Preserve `All` entries (don't filter as non-composite)
- HeapParameterization: Don't treat wildcard `All` as evidence of heap access
- PythonToLaurel: Use `modifies *` for all opaque procedures
- Specs/ToLaurel: Use `modifies *` for spec procedures
- T2_ModifiesClauses: Uncomment wildcard test case
Refactor StmtExpr to introduce a Variable inductive type:
- Add Variable with Local (name) and Field (target, fieldName) constructors
- Replace StmtExpr.Identifier with StmtExpr.Var (.Local name)
- Replace StmtExpr.FieldSelect with StmtExpr.Var (.Field target fieldName)
- Change Assign targets from List (AstNode StmtExpr) to List (AstNode Variable)
- Add multi-target assignment (multiAssign) to the Laurel grammar
- Update all pattern matches and constructors across the codebase

Closes #21
The multiAssign rule (CommaSepBy Ident := StmtExpr) conflicts with
call argument parsing (CommaSepBy StmtExpr), causing parse failures
in HeapParameterizationConstants and PythonRuntimeLaurelPart.

Multi-target assignment from Python is handled by PythonToLaurel,
not the Laurel grammar parser.
Remove StmtExpr.LocalVariable and add Variable.Declare constructor.

- var x: int := 3 is now Assign [Declare {x, int}] 3
- var x: int (no init) is now Var (Declare {x, int})

Update all consumers across the codebase: grammar translators,
resolution, heap parameterization, type hierarchy, Laurel-to-Core
translator, lift imperative expressions, map/traverse utilities,
Python-to-Laurel translator, type alias/constrained type elimination,
filter prelude, infer hole types, and affected test comments.
- Add multiAssign grammar rule: var x: int, y, var z: int := call()
- Parse multiAssign in ConcreteToAbstract translator
- Emit multiAssign in AbstractToConcrete translator for multi-target assigns
- Handle Declare targets in multi-target assign in LaurelToCore translator
- Add T22_MultipleReturns test verifying the feature end-to-end
Changed the grammar so multi-target assignments use 'assign' keyword:
  assign var x: int, y, var z: int := multipleReturns();
  assign a, var b: int, var c: int := multipleReturns();

This removes the ambiguity since the first target no longer needs to
be a 'var' declaration. Single assignments still work without 'assign':
  var m: int := 3;
  n := 4;

Updated test to cover both forms.
…p recursion, update docs, remove partial

- ConcreteToAbstractTreeTranslator: replace silent fallback with TransM.error
  for non-Var assign targets
- HeapParameterization: restore recursion into Field targets and value for
  multi-target assigns
- CoreGroupingAndOrdering: clarify comment about field-target assigns being
  eliminated before this pass
- Laurel.lean: update Variable doc to cover all three constructors
- TypeHierarchy: remove partial from validateDiamondFieldAccessesForStmtExpr
  and provide termination proof
- Add dedicated handler for Assign (targetHead::targetTail) (StaticCall | InstanceCall)
  that adds heap variable to front of targets when callee writes heap
- Add heap variable to call arguments when callee reads/writes heap
- Extract single field-write case as separate pattern for clarity
- Add test case for heap-modifying procedure with multiple returns
…fying multiple return

- Add repeatedAssignTarget test to T22_MultipleReturns (passes)
- Add fieldAssignsFromHeapModifyingMultipleReturnCaller test to M1_MutableFields
- Add assignTargetField grammar rule for field access targets in multiAssign
- Implement processFieldAssignments in HeapParameterization: replaces Field
  targets with fresh local variables and generates suffix heap update statements
- Update ConcreteToAbstract/AbstractToConcrete for field assign targets
…e unused mkVarDecl

- Replace empty string with $BUG_invalid_var in stmtExprToVar fallback
- Remove unused mkVarDecl function
- Remove unused variable names (h1, h2, hmem, hval) in termination proofs
- Remove unused simp argument AstNode.val
- Remove unused simp argument List.cons
@keyboardDrummer keyboardDrummer changed the base branch from issue-21-assign-variable-type to main May 5, 2026 13:26
keyboardDrummer and others added 6 commits May 5, 2026 15:09
*Description of changes:*

Always transform asserts in LiftImperativeExpressions to hoist
imperative procedure calls out of expressions. Previously, asserts
containing imperative calls were skipped, leaving procedure calls inside
pure expressions that Core rejects with 'calls to procedures are not
supported in functions or contracts'.

Added test which produces an internal assert of the form
`!Any..isexception(PSub(base, timedelta_func(...)))`.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

---------

Co-authored-by: Michael Tautschnig <tautschn@amazon.com>
Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Co-authored-by: keyboardDrummer-bot <keyboardDrummer-bot@users.noreply.github.com>
Repository owner deleted a comment from keyboardDrummer-bot May 5, 2026
@keyboardDrummer
Copy link
Copy Markdown
Owner

@keyboardDrummer-bot can you debug the failure in CI ? if you don't have a good fix, just tell me the analysis

Two bugs in the ContractPass:

1. In functional procedures, precondition asserts at call sites caused
   'asserts are not supported in functions' errors during Laurel-to-Core
   translation. Fix: use assume instead of assert for precondition checks
   inside functional procedures (RemoveFunctionAssumptions will strip them).

2. For bare StaticCall (no assignment), the postcondition  function
   was called without output arguments, causing a type error (partial
   application returned arrow type instead of bool). Fix: capture outputs
   in temporary variables and pass them to the  function.

Also moves 4 tests from pending/ to tests/ that now pass due to the
ContractPass handling preconditions at call sites.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants