Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
205 commits
Select commit Hold shift + click to select a range
1fb0a12
Add opaque keyword to Laurel grammar
keyboardDrummer-bot Apr 19, 2026
3e8d949
Disallow transparent statement bodies on non-functional procedures
keyboardDrummer-bot Apr 19, 2026
c7ad4a9
Merge add-opaque-keyword branch and use opaque instead of ensures true
keyboardDrummer-bot Apr 19, 2026
2664cc5
Remove checkTransparentBodies option; make Python pipeline use opaque…
keyboardDrummer-bot Apr 19, 2026
63d884d
Merge main into disallow-transparent-statement-bodies
keyboardDrummer-bot Apr 19, 2026
2ac415f
Merge upstream/main into add-opaque-keyword
keyboardDrummer-bot Apr 19, 2026
037dd83
Fix CI failures: grammar formatting, parser fallback, and missing opa…
keyboardDrummer-bot Apr 19, 2026
7265e2f
Fix CI: include opaque-with-body procedures in inlinableProcedures
keyboardDrummer-bot Apr 19, 2026
976eb51
Update golden-file expected outputs for kwargs tests
keyboardDrummer-bot Apr 19, 2026
9d4c6ee
Update golden-file expected outputs for class tests
keyboardDrummer-bot Apr 19, 2026
405fd0a
Update LaurelGrammar
keyboardDrummer Apr 20, 2026
2387080
Grammar fix so ensures clauses without opaque are not dropped
keyboardDrummer Apr 20, 2026
edf5a72
Fix AbstractToConcreteTreeTranslator to nest ensures/modifies inside …
keyboardDrummer-bot Apr 20, 2026
7dbd727
Merge remote-tracking branch 'fork/add-opaque-keyword' into disallow-…
keyboardDrummer Apr 21, 2026
862d882
Undo expect file changes
keyboardDrummer Apr 21, 2026
107afed
Update comment
keyboardDrummer Apr 21, 2026
b531ee5
Add wildcard modifies clause support (`modifies *`)
keyboardDrummer-bot Apr 21, 2026
e3fe027
Update test
keyboardDrummer Apr 21, 2026
2da0f74
Merge branch 'main' into add-opaque-keyword
tautschnig Apr 22, 2026
89c6836
Add opaque keyword to StatisticsTest Laurel snippets
keyboardDrummer-bot Apr 22, 2026
cf00cb0
Make type of Assign more specific
keyboardDrummer-bot Apr 22, 2026
67e75cd
Remove multiAssign grammar rule that causes parsing ambiguity
keyboardDrummer-bot Apr 22, 2026
89e1d37
Replace LocalVariable with Variable.Declare
keyboardDrummer-bot Apr 22, 2026
27f11e4
Add multi-target assignment syntax and test for multiple returns
keyboardDrummer-bot Apr 22, 2026
76a487b
Use 'assign' keyword prefix for multi-target assignments
keyboardDrummer-bot Apr 23, 2026
4a0d5cc
Address review feedback: error on invalid assign targets, restore hea…
keyboardDrummer-bot Apr 23, 2026
f7784c1
Add heap parameterization for multi-target assign calls
keyboardDrummer-bot Apr 23, 2026
dfd8c58
Add tests for repeated assign target and field assigns from heap-modi…
keyboardDrummer-bot Apr 23, 2026
151f955
Address review: use bug-obvious name in stmtExprToVar fallback, remov…
keyboardDrummer-bot Apr 23, 2026
578d40c
Fix lint warnings in HeapParameterization termination proofs
keyboardDrummer-bot Apr 23, 2026
cf43d15
Update golden files for assertion numbering changes
keyboardDrummer-bot Apr 23, 2026
b87f0cf
start of refactoring
keyboardDrummer Apr 23, 2026
348fa33
Fix bugs
keyboardDrummer Apr 23, 2026
98fe891
Simplify LaurelToCore assign translation
keyboardDrummer Apr 23, 2026
9aa12e0
Resolve sorrys in HeapParameterization proof
keyboardDrummer-bot Apr 23, 2026
2a3da0e
Merge main into issue-21-assign-variable-type
keyboardDrummer-bot Apr 23, 2026
674fcfa
Fix HeapParameterization and LaurelToCoreTranslator for new Variable-…
keyboardDrummer-bot Apr 23, 2026
66bc82b
Fix pre-existing test failures in DictNoneTest and AnalyzeLaurelTest
keyboardDrummer-bot Apr 23, 2026
7f8ad0e
Fix CI: skip Java TestGen test 12 gracefully when javac or jar is mis…
keyboardDrummer-bot Apr 23, 2026
0feec54
Add Resolution check for assign target count, remove LaurelToCore pad…
keyboardDrummer-bot Apr 23, 2026
6f1a531
Revert "Fix Python->Laurel: Havoc callee or Heap after unmodeled Inst…
keyboardDrummer Apr 23, 2026
c253ba7
Merge branch 'issue-21-assign-variable-type' of github.com:keyboardDr…
keyboardDrummer Apr 23, 2026
fea039a
Fixes
keyboardDrummer Apr 23, 2026
2b0b010
Resolve merge conflicts in PythonToLaurel and fix test expectations
keyboardDrummer-bot Apr 23, 2026
d31c0df
:Revert "Fix pre-existing test failures in DictNoneTest and AnalyzeLa…
keyboardDrummer Apr 23, 2026
dfc8fdf
Revert "Fix CI: skip Java TestGen test 12 gracefully when javac or ja…
keyboardDrummer Apr 23, 2026
70717c3
Merge branch 'issue-21-assign-variable-type' of github.com:keyboardDr…
keyboardDrummer Apr 23, 2026
185f9da
Use Declare targets in Assign to eliminate extraDecls in HeapParamete…
keyboardDrummer-bot Apr 23, 2026
a38fd43
Fix pre-existing test failures in DictNoneTest and TestGen
keyboardDrummer-bot Apr 23, 2026
22d347b
Use Declare targets instead of extraDecls in HeapParameterization
keyboardDrummer-bot Apr 23, 2026
5c131f5
Fix DictNoneTest: handle expected error inside callback so test passe…
keyboardDrummer-bot Apr 23, 2026
779b2af
Add `modifies *` wildcard support to Laurel
keyboardDrummer-bot Apr 23, 2026
93af49b
Retry CI: all checks pass locally
keyboardDrummer-bot Apr 23, 2026
9475765
Fix pyAnalyze expected output for test_class_methods and test_class_w…
keyboardDrummer-bot Apr 23, 2026
36c7d7e
Fix doc build: add persist-credentials: false to checkout step
keyboardDrummer-bot Apr 23, 2026
3df9e8a
Merge branch 'main' into issue-21-assign-variable-type
keyboardDrummer Apr 23, 2026
d55020c
Address review comments
keyboardDrummer-bot Apr 23, 2026
29a460c
Merge branch 'main' into add-modifies-wildcard
shigoel Apr 23, 2026
b410bb7
Maintain all modifies clauses in concrete grammar translation
keyboardDrummer-bot Apr 23, 2026
6e6563a
Merge branch 'main' into issue-21-assign-variable-type and revert Dic…
keyboardDrummer-bot Apr 24, 2026
50050a5
Add test for modifies * on transparent body (no ensures)
keyboardDrummer-bot Apr 24, 2026
143c09e
Merge branch 'main' into add-opaque-keyword
robin-aws Apr 24, 2026
c5ca718
Fix
keyboardDrummer Apr 27, 2026
e836f9d
Add test combining modifies c and modifies *
keyboardDrummer-bot Apr 27, 2026
a5b17b0
Merge remote-tracking branch 'origin/main' into issue-21-assign-varia…
keyboardDrummer Apr 27, 2026
a638b8c
Merge branch 'main' into add-modifies-wildcard
thanhnguyen-aws Apr 27, 2026
70f1012
Merge branch 'main' into add-modifies-wildcard
keyboardDrummer-bot Apr 28, 2026
9584b73
Merge upstream/main into issue-21-assign-variable-type, resolve confl…
keyboardDrummer-bot Apr 28, 2026
9be587d
Fix modifies wildcard tests for opaque keyword grammar
keyboardDrummer-bot Apr 28, 2026
f3e9511
Fix issues
keyboardDrummer Apr 28, 2026
3243df4
Merge branch 'add-modifies-wildcard' of github.com:strata-org/Strata …
keyboardDrummer Apr 28, 2026
e53d5e0
Remove temp files
keyboardDrummer Apr 28, 2026
6c549bd
Fix conflicts
keyboardDrummer Apr 28, 2026
d14b93e
Remove obsolete test
keyboardDrummer Apr 28, 2026
0b90c56
Fix test
keyboardDrummer Apr 28, 2026
36ece7d
Fix T22_MultipleReturns: add missing 'opaque' keyword
keyboardDrummer-bot Apr 28, 2026
6c5dcdb
Extract StmtExprMd.isWildcard predicate to eliminate duplication
keyboardDrummer-bot Apr 28, 2026
71a4357
Merge branch 'main' into add-modifies-wildcard
tautschnig Apr 28, 2026
9f96122
Add more resolution checks
keyboardDrummer Apr 28, 2026
afff624
Add Laurel tests for resolution kind-mismatch errors
keyboardDrummer-bot Apr 28, 2026
77b18a8
Merge upstream/main into issue-21-assign-variable-type, resolve confl…
keyboardDrummer-bot Apr 28, 2026
bcfccb6
Merge branch 'main' into moreResolutionChecks
olivier-aws Apr 29, 2026
fc6c280
Merge remote-tracking branch 'origin/moreResolutionChecks' into issue…
keyboardDrummer Apr 29, 2026
ea85952
Do not generate core on pass diagnostics
keyboardDrummer Apr 29, 2026
198034b
Merge commit 'fe4578675012~1' into disallow-transparent-statement-bodies
keyboardDrummer Apr 29, 2026
c318dea
Merge remote-tracking branch 'fork/add-opaque-keyword' into disallow-…
keyboardDrummer Apr 29, 2026
6e057c9
Merge commit 'fe4578675012' into disallow-transparent-statement-bodies
keyboardDrummer Apr 29, 2026
b0ea928
Merge branch 'main' into add-modifies-wildcard
keyboardDrummer-bot Apr 29, 2026
e5d9752
Merge remote-tracking branch 'origin/main' into disallow-transparent-…
keyboardDrummer Apr 29, 2026
2c94491
Fix test
keyboardDrummer Apr 29, 2026
feaa876
Fix: don't skip core translation when pass diagnostics exist
keyboardDrummer-bot Apr 29, 2026
83c490b
Fixes
keyboardDrummer Apr 29, 2026
6a1d196
Remove gitignore addition
keyboardDrummer Apr 29, 2026
4fb9d74
Revert "Fix: don't skip core translation when pass diagnostics exist"
keyboardDrummer Apr 29, 2026
87c87f3
Fix test
keyboardDrummer Apr 29, 2026
d9404aa
update tests
keyboardDrummer Apr 29, 2026
2b9f2b0
Update tests
keyboardDrummer Apr 29, 2026
b68b31a
Merge remote-tracking branch 'origin/add-modifies-wildcard' into disa…
keyboardDrummer Apr 29, 2026
5602edc
Merge commit 'eee972e813d16~1' into disallow-transparent-statement-bo…
keyboardDrummer Apr 29, 2026
63ed7df
Merge commit 'eee972e813d16' into disallow-transparent-statement-bodies
keyboardDrummer Apr 29, 2026
71a600a
Update test
keyboardDrummer Apr 29, 2026
6efab79
Fix tests
keyboardDrummer Apr 29, 2026
51661e0
Update test
keyboardDrummer Apr 29, 2026
28cae40
Fix tests
keyboardDrummer Apr 29, 2026
a1730d7
Merge remote-tracking branch 'origin/main' into issue-21-assign-varia…
keyboardDrummer Apr 29, 2026
541d596
Fixes
keyboardDrummer Apr 29, 2026
5914743
Improve modified diagnostic
keyboardDrummer Apr 29, 2026
ad76318
Fixes
keyboardDrummer Apr 29, 2026
fb859b8
Undo small change
keyboardDrummer Apr 29, 2026
664ab31
Update expect files
keyboardDrummer Apr 29, 2026
6694822
Undo fix to call to main, since it triggers a latent bug
keyboardDrummer Apr 29, 2026
178425c
Update tests
keyboardDrummer Apr 30, 2026
fe02be1
Merge branch 'main' into disallow-transparent-statement-bodies
keyboardDrummer Apr 30, 2026
6043f2f
Improve source location creation in InferHoleType
keyboardDrummer Apr 30, 2026
8a97cd7
More improvements to source locations
keyboardDrummer Apr 30, 2026
5cb4822
More debugging improvements
keyboardDrummer Apr 30, 2026
f22f29c
Fix bug in resolution
keyboardDrummer Apr 30, 2026
107b72a
Add compilation bug check
keyboardDrummer Apr 30, 2026
f1f783f
update commment
keyboardDrummer Apr 30, 2026
99630d7
Fix test
keyboardDrummer Apr 29, 2026
b0c1bdd
Merge branch 'disallow-transparent-statement-bodies' of github.com:st…
keyboardDrummer Apr 30, 2026
64daf7c
Merge branch 'main' into disallow-transparent-statement-bodies
keyboardDrummer Apr 30, 2026
373e6c4
Tweak in infer hole types
keyboardDrummer Apr 30, 2026
7493074
Merge branch 'disallow-transparent-statement-bodies' of github.com:st…
keyboardDrummer Apr 30, 2026
8834318
Remove unused code
keyboardDrummer Apr 30, 2026
02a895b
Add missing defineNameCheckDup
keyboardDrummer Apr 30, 2026
dbc9da8
Fixes
keyboardDrummer Apr 30, 2026
a42c339
Fixes
keyboardDrummer Apr 30, 2026
c091620
Refactoring
keyboardDrummer Apr 30, 2026
75c037c
Merge branch 'disallow-transparent-statement-bodies' of github.com:ke…
keyboardDrummer Apr 30, 2026
ee0e39b
Fix merge mistake
keyboardDrummer Apr 30, 2026
fd2988e
Revert "Fix test"
keyboardDrummer Apr 30, 2026
5fb8577
Merge branch 'main' into disallow-transparent-statement-bodies
keyboardDrummer Apr 30, 2026
da915bf
Merge issue-21-assign-variable-type into disallow-transparent-stateme…
keyboardDrummer-bot May 1, 2026
942ff8d
Merge origin/main into merge-1076-1077, resolve conflicts
keyboardDrummer-bot May 1, 2026
63c492c
Fix merge conflicts between PR #1076 and PR #1077
keyboardDrummer-bot May 1, 2026
7a30a9b
Update golden files for test_class_methods and test_class_with_methods
keyboardDrummer-bot May 1, 2026
836e8d1
Add opaque keyword to CBMC Laurel test files
keyboardDrummer-bot May 1, 2026
1a3e5be
Merge main into issue-21-assign-variable-type, resolve conflicts
keyboardDrummer-bot May 1, 2026
0744a01
Address review feedback: naming, comments, error handling
keyboardDrummer-bot May 1, 2026
23846a4
Fix lint: add nopanic:ok suppression to stmtExprToVar
keyboardDrummer-bot May 1, 2026
3745ced
Address MikaelMayer review: fix InstanceCall bug, deduplicate call tr…
keyboardDrummer-bot May 1, 2026
0f9d668
Revert "Fix test"
keyboardDrummer May 1, 2026
07cb9f7
Recurse into Field target in collectExpr Assign case
keyboardDrummer-bot May 1, 2026
18b74e1
Merge branch 'issue-21-assign-variable-type' of github.com:strata-org…
keyboardDrummer May 1, 2026
5a85348
Add comment to early return
keyboardDrummer May 1, 2026
e672786
Merge branch 'main' into issue-21-assign-variable-type
keyboardDrummer May 1, 2026
63f1719
Remove Variable formatters from Laurel.lean
keyboardDrummer-bot May 1, 2026
29f181a
Fix doc comment inside do block causing build failure
keyboardDrummer-bot May 1, 2026
38e441e
Address review feedback: merge conflicts, inferArgsTyped arity check,…
keyboardDrummer-bot May 1, 2026
be7bb41
Reapply "Fix test"
keyboardDrummer May 1, 2026
3485da0
Merge branch 'main' into issue-21-assign-variable-type
keyboardDrummer May 1, 2026
3800ad0
Revert: restore servicelib_Storage_ label filter in AnalyzeLaurelTest
keyboardDrummer-bot May 1, 2026
4592a65
Refactor: avoid stmtExprToVar where Variable can be captured directly
keyboardDrummer-bot May 1, 2026
d6151eb
Review comment
keyboardDrummer May 2, 2026
346ff37
Replace fragile line-number reference in comment with case-name refer…
keyboardDrummer-bot May 4, 2026
fe42aba
Simplify a proof
keyboardDrummer May 4, 2026
7d53b9f
Refactoring
keyboardDrummer May 4, 2026
601512d
More comments
keyboardDrummer May 4, 2026
28eb58e
Merge remote-tracking branch 'fork/merge-1076-1077' into issue-21-ass…
keyboardDrummer May 4, 2026
224e1b7
Merge remote-tracking branch 'origin/disallow-transparent-statement-b…
keyboardDrummer May 4, 2026
5244d5e
Merge commit 'd95396fc61245' into issue-21-assign-variable-type
keyboardDrummer May 4, 2026
db66551
Check for multi-target function call
keyboardDrummer May 4, 2026
ec66975
Merge remote-tracking branch 'origin/disallow-transparent-statement-b…
keyboardDrummer May 4, 2026
828e362
Tweaks
keyboardDrummer May 4, 2026
aeb1f22
Add commit explaining the use of !
keyboardDrummer May 4, 2026
4e04832
Fix merge mistakes
keyboardDrummer May 4, 2026
6226226
Remove bareVar usages
keyboardDrummer May 4, 2026
39af273
Refactoring
keyboardDrummer May 4, 2026
64f9f7b
Recurse into Field targets in collectStaticCallNames
keyboardDrummer-bot May 4, 2026
334b09d
Address review feedback from tautschnig
keyboardDrummer-bot May 4, 2026
0030d33
Make expectedOutputCount default to 1 for non-call RHS, simplify erro…
keyboardDrummer-bot May 4, 2026
e17f070
Add sizeOf_field_target_lt_of_eq to simplify Field termination proofs
keyboardDrummer-bot May 4, 2026
98fceea
Add EliminateReturnStatements and ContractPass
keyboardDrummer-bot May 5, 2026
659c8cd
Improve testing code
keyboardDrummer May 4, 2026
d9f12f2
Merge remote-tracking branch 'origin/issue-21-assign-variable-type' i…
keyboardDrummer May 5, 2026
36bfbcf
Merge commit '8380aad89d96~1' into contract-pass-v2
keyboardDrummer May 5, 2026
2a7225d
Merge commit '8380aad89d96' into contract-pass-v2
keyboardDrummer May 5, 2026
6ce357c
Improve printing of Laurel blocks so newlines are inserted
keyboardDrummer May 4, 2026
90ce5a3
Add debugging diagnostic
keyboardDrummer May 5, 2026
04dd29f
Fixes
keyboardDrummer May 5, 2026
4c37f21
Fixes
keyboardDrummer May 5, 2026
101d511
Fix tests
keyboardDrummer May 5, 2026
2c5b120
More fixes
keyboardDrummer May 5, 2026
ec015a6
Updates
keyboardDrummer May 5, 2026
0d7da75
Update translator
keyboardDrummer May 5, 2026
32ba928
Fix test
keyboardDrummer May 5, 2026
ab70620
Fix missing source issue
keyboardDrummer May 5, 2026
19ec6fc
Fix related to functions with postconditions
keyboardDrummer May 5, 2026
771104e
Improve debugging of errors when translating to Core
keyboardDrummer May 5, 2026
51b3739
Trigger CI
keyboardDrummer May 5, 2026
4d34998
Update test to expose HeapParameterization bug
keyboardDrummer May 5, 2026
7760c92
Add fix
keyboardDrummer May 5, 2026
ebb7a48
Laurel: Lift procedure calls in asserts (#835)
ssomayyajula May 5, 2026
f8acfc6
Update
keyboardDrummer May 5, 2026
39189f3
update comment
keyboardDrummer May 5, 2026
515f13d
Replace panic
keyboardDrummer May 5, 2026
4c72dc4
Fixes
keyboardDrummer May 5, 2026
9788a1b
Fix bug in ConstrainedTypeElim
keyboardDrummer May 5, 2026
60cedac
Merge branch 'main' into heapParamScopeBug
keyboardDrummer May 5, 2026
c528572
Fixes
keyboardDrummer May 5, 2026
970ce12
Fixes for invokeOn
keyboardDrummer May 5, 2026
a7e5982
Fixes
keyboardDrummer May 5, 2026
e4abe90
undo test change
keyboardDrummer May 5, 2026
0a96d30
Fix ContractPass: handle functional procs and bare calls
keyboardDrummer-bot May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ vcs/*.smt2
*.py.ion
*.py.ion.core.st

Strata.code-workspace
Strata.code-workspace
Build/
2 changes: 1 addition & 1 deletion Strata/DDM/Format.lean
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ private partial def ArgF.mformatM {α} : ArgF α → FormatM PrecFormat
if z : entries.size = 0 then
pure (.atom .nil)
else do
let f i q s := return s ++ "; " ++ (← entries[i].mformatM).format
let f i q s := return s ++ ";\n" ++ (← entries[i].mformatM).format
let a := (← entries[0].mformatM).format
.atom <$> entries.size.foldlM f (start := 1) a

Expand Down
2 changes: 1 addition & 1 deletion Strata/Languages/Laurel/ConstrainedTypeElim.lean
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ private def mkWitnessProc (ptMap : ConstrainedTypeMap) (ct : ConstrainedType) :
{ name := mkId s!"$witness_{ct.name.text}"
inputs := []
outputs := []
body := .Transparent ⟨.Block [witnessInit, assert] none, src⟩
body := .Opaque [] (some ⟨.Block [witnessInit, assert] none, src⟩) []
preconditions := []
isFunctional := false
decreases := none }
Expand Down
343 changes: 343 additions & 0 deletions Strata/Languages/Laurel/ContractPass.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
/-
Copyright Strata Contributors

SPDX-License-Identifier: Apache-2.0 OR MIT
-/
module

public import Strata.Languages.Laurel.MapStmtExpr

/-!
## Contract Pass (Laurel → Laurel)

Removes pre- and postconditions from all procedures and replaces them with
explicit precondition/postcondition helper procedures, assumptions, and
assertions.

For each procedure with contracts:
- Generate a precondition procedure (`foo$pre`) returning the conjunction of preconditions.
- Generate a postcondition procedure (`foo$post`) that takes all inputs and all
outputs as parameters and returns the conjunction of postconditions. It is
marked as functional and does not call the original procedure.
- Insert `assume foo$pre(inputs)` at the start of the body.
- Insert `assert foo$post(inputs, outputs)` at the end of the body.

For each call to a contracted procedure:
- Assign all input arguments to temporary variables before the call.
- Insert `assert foo$pre(temps)` before the call (precondition check).
- After the call, insert `assume foo$post(temps, outputs)` (postcondition assumption).
-/

namespace Strata.Laurel

public section

private def mkMd (e : StmtExpr) : StmtExprMd := { val := e, source := none }
private def mkVarMd (v : Variable) : VariableMd := { val := v, source := none }

/-- Build a conjunction of expressions. Returns `LiteralBool true` for an empty list. -/
private def conjoin (exprs : List StmtExprMd) : StmtExprMd :=
match exprs with
| [] => mkMd (.LiteralBool true)
| [e] => e
| e :: rest => rest.foldl (fun acc x =>
mkMd (.PrimitiveOp .And [acc, x])) e

/-- Name for the precondition helper procedure. -/
def preCondProcName (procName : String) : String := s!"{procName}$pre"

/-- Name for the postcondition helper procedure. -/
def postCondProcName (procName : String) : String := s!"{procName}$post"

/-- Get postconditions from a procedure body. -/
private def getPostconditions (body : Body) : List Condition :=
match body with
| .Opaque postconds _ _ => postconds
| .Abstract postconds => postconds
| _ => []

/-- Build a call expression. -/
private def mkCall (callee : String) (args : List StmtExprMd) : StmtExprMd :=
mkMd (.StaticCall (mkId callee) args)

/-- Convert parameters to identifier expressions. -/
private def paramsToArgs (params : List Parameter) : List StmtExprMd :=
params.map fun p => mkMd (.Var (.Local p.name))

/-- Build a helper function that returns the conjunction of the given conditions. -/
private def mkConditionProc (name : String) (params : List Parameter)
(conditions : List Condition) : Procedure :=
{ name := mkId name
inputs := params
outputs := [⟨mkId "$result", { val := .TBool, source := none }⟩]
preconditions := []
decreases := none
isFunctional := true
body := .Transparent (conjoin (conditions.map (·.condition))) }

/-- Build a postcondition function that takes all inputs and all outputs as
parameters and returns the conjunction of postconditions. The function is
marked as functional and does not call the original procedure.

For a procedure `foo(a, b) returns (x, y)` with postcondition `P(a, b, x, y)`,
generates:
```
function foo$post(a, b, x, y) returns ($result : bool) {
P(a, b, x, y)
}
```
-/
private def mkPostConditionProc (name : String)
(inputParams : List Parameter) (outputParams : List Parameter)
(conditions : List Condition) : Procedure :=
let allParams := inputParams ++ outputParams
{ name := mkId name
inputs := allParams
outputs := [⟨mkId "$result", { val := .TBool, source := none }⟩]
preconditions := []
decreases := none
isFunctional := true
body := .Transparent (conjoin (conditions.map (·.condition))) }

/-- Extract a combined summary from a list of conditions. -/
private def combinedSummary (clauses : List Condition) : Option String :=
let summaries := clauses.filterMap (·.summary)
match summaries with
| [] => none
| [s] => some s
| ss => some (String.intercalate ", " ss)

/-- Information about a procedure's contracts. -/
private structure ContractInfo where
hasPreCondition : Bool
hasPostCondition : Bool
preName : String
postName : String
preSummary : Option String
postSummary : Option String
inputParams : List Parameter
outputParams : List Parameter

/-- Collect contract info for all procedures with contracts. -/
private def collectContractInfo (procs : List Procedure) : Std.HashMap String ContractInfo :=
procs.foldl (fun m proc =>
let postconds := getPostconditions proc.body
let hasPre := !proc.preconditions.isEmpty
let hasPost := !postconds.isEmpty
if hasPre || hasPost then
m.insert proc.name.text {
hasPreCondition := hasPre
hasPostCondition := hasPost
preName := preCondProcName proc.name.text
postName := postCondProcName proc.name.text
preSummary := combinedSummary proc.preconditions
postSummary := combinedSummary postconds
inputParams := proc.inputs
outputParams := proc.outputs
}
else m) {}

/-- Transform a procedure body to add assume/assert for its own contracts. -/
private def transformProcBody (proc : Procedure) (info : ContractInfo) : Body :=
let inputArgs := paramsToArgs proc.inputs
let postconds := getPostconditions proc.body
let preAssume : List StmtExprMd :=
if info.hasPreCondition then
let preSrc := match proc.preconditions.head? with
| some pc => pc.condition.source
| none => none
[⟨.Assume (mkCall info.preName inputArgs), preSrc⟩]
else []
let postAssert : List StmtExprMd :=
if info.hasPostCondition then
postconds.map fun pc =>
let summary := pc.summary.getD "postcondition"
⟨.Assert { condition := pc.condition, summary := some summary }, pc.condition.source⟩
else []
match proc.body with
| .Transparent body =>
.Transparent ⟨.Block (preAssume ++ [body] ++ postAssert) none, body.source⟩
| .Opaque _ (some impl) _ =>
.Opaque [] (some ⟨.Block (preAssume ++ [impl] ++ postAssert) none, impl.source⟩) []
| .Opaque _ none mods =>
.Opaque [] none mods
| .Abstract _ =>
.Abstract []
| b => b

/-- Generate temporary variable assignments for input arguments at a call site.
Returns (temp declarations+assignments, temp variable references).
Uses the parameter types from the procedure's contract info so that
resolution can type-check the generated temporaries.
`callIdx` distinguishes multiple calls to the same procedure. -/
private def mkTempAssignments (args : List StmtExprMd) (calleeName : String)
(inputParams : List Parameter) (callIdx : Nat) (src : Option FileRange)
: List StmtExprMd × List StmtExprMd :=
let indexed := args.zipIdx
let decls := indexed.map fun (arg, i) =>
let tempName := s!"${calleeName}${callIdx}$arg{i}"
let paramType := match inputParams[i]? with
| some p => p.type
| none => { val := .Unknown, source := none }
let param : Parameter := { name := mkId tempName, type := paramType }
⟨StmtExpr.Assign [mkVarMd (.Declare param)] arg, src⟩
let refs := indexed.map fun (_, i) =>
let tempName := s!"${calleeName}${callIdx}$arg{i}"
mkMd (.Var (.Local (mkId tempName)))
(decls, refs)

/-- Rewrite a single statement that may be a call to a contracted procedure.
Returns a list of statements (the original plus any inserted assert/assume).
Takes and returns a call counter for generating unique temp variable names.
When `isFunctional` is true, precondition checks use `assume` instead of
`assert` since asserts are not supported in functions during Core translation.

At call sites:
1. Assign input arguments to temporary variables.
2. Assert precondition using temps.
3. Execute the call using temps as arguments.
4. Assume postcondition using temps + output variables. -/
private def rewriteStmt (contractInfoMap : Std.HashMap String ContractInfo)
(isFunctional : Bool) (callCounter : Nat) (e : StmtExprMd) : List StmtExprMd × Nat :=
let src := e.source
let mkWithSrc (se : StmtExpr) : StmtExprMd := ⟨se, src⟩
match e.val with
| .Assign targets (.mk (.StaticCall callee args) callSrc) =>
match contractInfoMap.get? callee.text with
| some info =>
let (tempDecls, tempRefs) := mkTempAssignments args callee.text info.inputParams callCounter src
let callWithTemps : StmtExprMd := ⟨.Assign targets ⟨.StaticCall callee tempRefs, callSrc⟩, src⟩
let preCheck := if info.hasPreCondition then
if isFunctional then
[mkWithSrc (.Assume (mkCall info.preName tempRefs))]
else
[mkWithSrc (.Assert { condition := mkCall info.preName tempRefs, summary := some (info.preSummary.getD "precondition") })]
else []
-- After the call, assume postcondition with temps (inputs) + output variables
let outputArgs := targets.filterMap fun t =>
match t.val with
| .Local name => some (mkMd (.Var (.Local name)))
| .Declare param => some (mkMd (.Var (.Local param.name)))
| _ => none
let postAssume := if info.hasPostCondition
then [mkWithSrc (.Assume (mkCall info.postName (tempRefs ++ outputArgs)))] else []
(tempDecls ++ preCheck ++ [callWithTemps] ++ postAssume, callCounter + 1)
| none => ([e], callCounter)
| .StaticCall callee args =>
match contractInfoMap.get? callee.text with
| some info =>
let (tempDecls, tempRefs) := mkTempAssignments args callee.text info.inputParams callCounter src
let preCheck := if info.hasPreCondition then
if isFunctional then
[mkWithSrc (.Assume (mkCall info.preName tempRefs))]
else
[mkWithSrc (.Assert { condition := mkCall info.preName tempRefs, summary := some (info.preSummary.getD "precondition") })]
else []
-- For bare calls with postconditions, capture outputs in temp variables
-- so we can pass them to the $post function.
let (callStmt, postAssume) :=
if info.hasPostCondition && !info.outputParams.isEmpty then
let outputTempDecls := info.outputParams.zipIdx.map fun (p, i) =>
let tempName := s!"${callee.text}${callCounter}$out{i}"
mkVarMd (.Declare { name := mkId tempName, type := p.type })
let callWithOutputs : StmtExprMd :=
⟨.Assign outputTempDecls ⟨.StaticCall callee tempRefs, src⟩, src⟩
let outputRefs := info.outputParams.zipIdx.map fun (_, i) =>
let tempName := s!"${callee.text}${callCounter}$out{i}"
mkMd (.Var (.Local (mkId tempName)))
let assume := [mkWithSrc (.Assume (mkCall info.postName (tempRefs ++ outputRefs)))]
(callWithOutputs, assume)
else
(mkWithSrc (.StaticCall callee tempRefs), [])
(tempDecls ++ preCheck ++ [callStmt] ++ postAssume, callCounter + 1)
| none => ([e], callCounter)
| _ => ([e], callCounter)

/-- Rewrite call sites in a statement/expression tree. Processes Block children
at the statement level to avoid interfering with expression-level calls. -/
private def rewriteCallSites (contractInfoMap : Std.HashMap String ContractInfo)
(isFunctional : Bool) (expr : StmtExprMd) : StmtExprMd :=
let (result, _) := StateT.run (s := (0 : Nat)) <|
mapStmtExprM (m := StateM Nat) (fun e => do
match e.val with
| .Block stmts label =>
let mut newStmts : List StmtExprMd := []
let mut counter ← get
for stmt in stmts do
let (expanded, counter') := rewriteStmt contractInfoMap isFunctional counter stmt
newStmts := newStmts ++ expanded
counter := counter'
set counter
if newStmts.length == stmts.length then return e
else return ⟨.Block newStmts label, e.source⟩
| _ => return e) expr
-- Handle top-level non-Block statements (e.g., bare Assign or StaticCall)
let (expanded, _) := rewriteStmt contractInfoMap isFunctional 0 result
match expanded with
| [single] => single
| many => mkMd (.Block many none)

/-- Rewrite call sites in all bodies of a procedure. -/
private def rewriteCallSitesInProc (contractInfoMap : Std.HashMap String ContractInfo)
(proc : Procedure) : Procedure :=
let rw := rewriteCallSites contractInfoMap proc.isFunctional
match proc.body with
| .Transparent body =>
{ proc with body := .Transparent (rw body) }
| .Opaque posts impl mods =>
let body := Body.Opaque (posts.map (·.mapCondition rw)) (impl.map rw) (mods.map rw)
{ proc with body := body }
| _ => proc

/-- Build an axiom expression from `invokeOn` trigger and ensures clauses.
Produces `∀ p1, ∀ p2, ..., ∀ pn :: { trigger } (ensures1 && ensures2 && ...)`.
The trigger controls when the SMT solver instantiates the axiom. -/
private def mkInvokeOnAxiom (params : List Parameter) (trigger : StmtExprMd)
(postconds : List Condition) : StmtExprMd :=
let body := conjoin (postconds.map (·.condition))
-- Wrap in nested Forall from last param (innermost) to first (outermost).
-- The trigger is placed on the innermost quantifier.
params.foldr (init := (body, true)) (fun p (acc, isInnermost) =>
let trig := if isInnermost then some trigger else none
(mkMd (.Quantifier .Forall p trig acc), false)) |>.1

/-- Run the contract pass on a Laurel program.
All procedures with contracts are transformed. -/
def contractPass (program : Program) : Program :=
let contractInfoMap := collectContractInfo program.staticProcedures

-- Generate helper procedures for all procedures with contracts
let helperProcs := program.staticProcedures.flatMap fun proc =>
let postconds := getPostconditions proc.body
let preProc :=
if proc.preconditions.isEmpty then []
else [mkConditionProc (preCondProcName proc.name.text) proc.inputs proc.preconditions]
let postProc :=
if postconds.isEmpty then []
else [mkPostConditionProc (postCondProcName proc.name.text)
proc.inputs proc.outputs postconds]
preProc ++ postProc

-- Transform procedures: strip contracts, add assume/assert, rewrite call sites
let transformedProcs := program.staticProcedures.map fun proc =>
let proc := match proc.invokeOn with
| some trigger =>
let postconds := getPostconditions proc.body
if postconds.isEmpty then { proc with invokeOn := none }
else { proc with
axioms := [mkInvokeOnAxiom proc.inputs trigger postconds]
invokeOn := none }
| none => proc
let proc := match contractInfoMap.get? proc.name.text with
| some info =>
{ proc with
preconditions := []
body := transformProcBody proc info }
| none => proc
-- Rewrite call sites in the procedure body
rewriteCallSitesInProc contractInfoMap proc

{ program with staticProcedures := helperProcs ++ transformedProcs }

end -- public section
end Strata.Laurel
Loading
Loading