-
Notifications
You must be signed in to change notification settings - Fork 52
Enable calling procedures in contracts #1352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
keyboardDrummer
merged 146 commits into
strata-org:reviewed-kbd-will-merge-to-main
from
keyboardDrummer:issue-924-contract-and-proof-pass
Jun 23, 2026
Merged
Changes from 92 commits
Commits
Show all changes
146 commits
Select commit
Hold shift + click to select a range
7e34b04
Improve Laurel printer
keyboardDrummer f246686
Introduce transparency pass
keyboardDrummer b11e042
Fixes
keyboardDrummer 9e4ee85
Refactoring
keyboardDrummer 7408103
Remove pack multiple outputs pass
keyboardDrummer 4e8fa8b
Add error file
keyboardDrummer 2d19629
Update tests
keyboardDrummer c9fd151
Add sources to lift phase
keyboardDrummer 19987e8
Update tests and improve errors
keyboardDrummer 24c3983
Update comment
keyboardDrummer 3c23531
Trigger CI
keyboardDrummer 6046381
Trigger CI
keyboardDrummer 86962e4
Trigger CI
keyboardDrummer d192346
Replace panic with safe access
keyboardDrummer 7a04e66
Move proof skipping logic from LaurelToCore translator to transparenc…
keyboardDrummer a45f8f0
Small refactoring
keyboardDrummer 96b2686
Merge branch 'formatting-and-debugging-improvements' into transparenc…
keyboardDrummer d0fdd35
Cleanup
keyboardDrummer 2e0d7e1
Merge branch 'transparency-pass-only' of github.com:keyboardDrummer/S…
keyboardDrummer c62cfb4
Add contract pass
keyboardDrummer a95da10
Cleanup
keyboardDrummer 49255b4
Fixes
keyboardDrummer 10640f7
Fixes
keyboardDrummer 8c15c16
Do not run the contract pass for functions yet
keyboardDrummer 573a36b
Reduce id changes
keyboardDrummer 74ec111
Fixes
keyboardDrummer 800f6c4
Test fixes
keyboardDrummer e56cb20
Fix test
keyboardDrummer a352b47
Fix test
keyboardDrummer 65c92f4
Fix test
keyboardDrummer 4a8c667
Remove unused files
keyboardDrummer d7d7205
Undo Python test changes
keyboardDrummer 60c9ca3
Fixes
keyboardDrummer a526dad
Add missing bindings for PrimitiveOp
keyboardDrummer ace39ca
Improve comment
keyboardDrummer 2e94fc5
Add missing bindings for PrimitiveOp
keyboardDrummer 200e828
Improve comment
keyboardDrummer 8875ffd
Merge branch 'transparency-pass-only' into issue-924-contract-and-pro…
keyboardDrummer 9400328
Generate separate $pre and $post procedures for each condition
keyboardDrummer-bot 65ab6ae
Rename SepFormat.semicolon to SepFormat.semicolonNewline
keyboardDrummer-bot 6c0c39c
Fix contract pass: preserve postconditions in Opaque/Abstract bodies
keyboardDrummer-bot d6bff50
Fix contract pass: make postcondition helpers functional
keyboardDrummer-bot 2cae5e0
Update golden-file expected outputs for changed label counters
keyboardDrummer-bot 5c0a458
Extract emitCoreDiagnostic helper to reduce repetition
keyboardDrummer-bot ff09522
Code review
keyboardDrummer afaf9a2
Stop dropping source location from callee
keyboardDrummer 9f65463
Code review
keyboardDrummer d143eeb
Merge remote-tracking branch 'origin/formatting-and-debugging-improve…
keyboardDrummer 35d7d75
Merge commit 'a995388cb~1' into transparency-pass-only
keyboardDrummer fc62415
Merge commit 'a995388cb' into transparency-pass-only
keyboardDrummer e1caee4
Trigger CI
keyboardDrummer 3e76abc
Fix nit
keyboardDrummer 6f0d365
Use HashSet for asFunctionNames and add isDestructorName/isTesterName…
keyboardDrummer-bot 80f22a6
Merge branch 'transparency-pass-only' of github.com:keyboardDrummer/S…
keyboardDrummer 1e9710b
Fix warning
keyboardDrummer 944ffbd
Merge branch 'transparency-pass-only' into issue-924-contract-and-pro…
keyboardDrummer 20225bd
Trigger CI
keyboardDrummer b52f50b
Trigger CI
keyboardDrummer b6f0f64
Don't generate intermediate files
keyboardDrummer 6ee3c7e
Exclude hidden files from the lake cache hash
keyboardDrummer 4b44fbf
Exclude hidden files from the lake cache hash
keyboardDrummer 1c5a9e1
Add polymorphism comment and recursive transparent procedure test
keyboardDrummer-bot 81b558c
Fixes
keyboardDrummer ac0fdb1
Update recursive test to use procedures
keyboardDrummer cf29a09
Delete recursive transparent
keyboardDrummer 780cbee
Merge branch 'main2' into transparency-pass-only
keyboardDrummer 3dc9527
Add a test-case
keyboardDrummer 74d532a
Merge upstream/main2 into transparency-pass-only
keyboardDrummer-bot e9887a6
Merge branch 'transparency-pass-only' into issue-924-contract-and-pro…
keyboardDrummer 70cb26e
Fixes
keyboardDrummer 17c1d84
Fix test after merge
keyboardDrummer 68cec09
Fix test expectation
keyboardDrummer dbfcb95
Remove unused module
keyboardDrummer 2f8eb11
Fix bug in Resolution.lean
keyboardDrummer 389e214
Merge branch 'transparency-pass-only' into issue-924-contract-and-pro…
keyboardDrummer d141467
Merge commit 'a364a70a~1' into issue-924-contract-and-proof-pass
keyboardDrummer 87d7214
Merge commit 'a364a70a' into issue-924-contract-and-proof-pass
keyboardDrummer 61c2e83
Address quality follow-ups from review
keyboardDrummer-bot 9ca5c77
Add test and fix for counter created by contract pass
keyboardDrummer b4938f0
Update Strata/Languages/Laurel/ContractPass.lean
keyboardDrummer 3c0e745
Merge branch 'issue-924-contract-and-proof-pass' of github.com:keyboa…
keyboardDrummer 9c9ace9
Update Strata/Languages/Laurel/EliminateValuesInReturns.lean
keyboardDrummer d8d9b96
Merge branch 'issue-924-contract-and-proof-pass' of github.com:keyboa…
keyboardDrummer bef6885
Cleanup
keyboardDrummer 96982e3
Merge remote-tracking branch 'fork/quality-followups-from-fabiomadge'…
keyboardDrummer 551e699
partial
keyboardDrummer 67f43bf
Simplify termination proof
keyboardDrummer ba8524a
Merge branch 'main2' into issue-924-contract-and-proof-pass
keyboardDrummer ac0b6be
Fix test
keyboardDrummer 31e9007
Merge branch 'noHiddenFilesInHash' into issue-924-contract-and-proof-…
keyboardDrummer 7eef859
Code review changes
keyboardDrummer 4b8ef55
Merge remote-tracking branch 'origin/main2' into issue-924-contract-a…
keyboardDrummer 237eabe
Pass tweaks
keyboardDrummer 4ffc62f
Fixes
keyboardDrummer 5178451
More pass refactoring changes
keyboardDrummer 0ba057d
Fixes
keyboardDrummer 1627ce4
Fixes
keyboardDrummer 816a738
Include final pass in the all pass list
keyboardDrummer 8470d93
Fixes
keyboardDrummer aebe44c
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer b7508d7
Fix test
keyboardDrummer f1507d1
Update Laurel docs
keyboardDrummer 1077979
Change text
keyboardDrummer b30f4cb
Fix invokeOn axiom: guard with preconditions + reject output-referenc…
keyboardDrummer-bot 6dd545a
Fix if-then-else prepend duplication in LiftImperativeExpressions
keyboardDrummer-bot d1f9d6c
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer 0322796
Fix test
keyboardDrummer 878f325
Code review
keyboardDrummer e0c8b68
Fix early return
keyboardDrummer a63384a
Fix lifting bug
keyboardDrummer 02ad82c
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer 799f167
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer 83e793b
Port fixes to lifting pass
keyboardDrummer 6e0ce93
Merge branch 'reviewed-kbd-will-merge-to-main' into issue-924-contrac…
keyboardDrummer e1fc044
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer dcfb0e9
Some fixes for resolution during phases
keyboardDrummer a0ec015
Fixes to resolution so it throws fewer errors during passes
keyboardDrummer 25f1d5d
Emit resolution errors during passes
keyboardDrummer fa7f844
Source fix
keyboardDrummer 441ec1e
Improve well-formedness of the 3 heap passes
keyboardDrummer cf706b2
Fix for type hierarchy
keyboardDrummer 66589c5
Refactoring
keyboardDrummer 903f89e
Allow dangling addition in blocks
keyboardDrummer 1a0e8ae
Fix diagnostic and test
keyboardDrummer 018939b
Remove function return type expectation check
keyboardDrummer 8d34b32
Update resolver check to use procedure instead of function
keyboardDrummer d9b39ac
Small improvements
keyboardDrummer a24cc86
Merge branch 'issue-924-contract-and-proof-pass' of github.com:keyboa…
keyboardDrummer c438d8c
Merge branch 'fixResolutionErrorsDuringPhases' into issue-924-contrac…
keyboardDrummer fcb3903
Fix merge
keyboardDrummer 4efaebd
Remove obsolete test
keyboardDrummer 8ce6fc6
Add multi output test-case to ImpureExpressions
keyboardDrummer 157ec6b
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer d7a4c3c
Refactor liftImperative
keyboardDrummer 0b03bff
Simplify lift pass
keyboardDrummer 97e3146
Bring back test
keyboardDrummer ffcc39d
Recursion proof refactoring
keyboardDrummer 1ef148f
Fix proofs
keyboardDrummer a279ff7
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer 34c850f
Cleanup
keyboardDrummer 12b5e59
Fix
keyboardDrummer bfc0b6c
Fix test
keyboardDrummer 2e2a7b3
Fix tests
keyboardDrummer 5f46178
Update expect file
keyboardDrummer 16be9b5
Merge remote-tracking branch 'origin/reviewed-kbd-will-merge-to-main'…
keyboardDrummer 97870a3
Undo added test and fix related to that
keyboardDrummer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,315 @@ | ||
| /- | ||
| Copyright Strata Contributors | ||
|
|
||
| SPDX-License-Identifier: Apache-2.0 OR MIT | ||
| -/ | ||
| module | ||
|
|
||
| public import Strata.Languages.Laurel.MapStmtExpr | ||
| public import Strata.Languages.Laurel.LaurelPass | ||
|
|
||
| /-! | ||
| ## 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 separate precondition procedure (`foo$pre0`, `foo$pre1`, ...) for each precondition. | ||
| - Generate a separate postcondition procedure (`foo$post0`, `foo$post1`, ...) for each postcondition. | ||
| Each takes all inputs and all outputs as parameters and returns the condition. | ||
| - Insert `assume foo$pre0(inputs); assume foo$pre1(inputs); ...` at the start of the body. | ||
| - Insert `assert foo$post0(inputs, outputs); assert foo$post1(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$pre0(temps); assert foo$pre1(temps); ...` before the call. | ||
| - After the call, insert `assume foo$post0(temps, outputs); assume foo$post1(temps, outputs); ...`. | ||
| -/ | ||
|
|
||
| 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 } | ||
|
|
||
| /-- Name for the i-th precondition helper procedure. -/ | ||
| def preCondProcName (procName : String) (i : Nat) : String := s!"{procName}$pre{i}" | ||
|
|
||
| /-- Name for the i-th postcondition helper procedure. -/ | ||
| def postCondProcName (procName : String) (i : Nat) : String := s!"{procName}$post{i}" | ||
|
|
||
| /-- 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 for a single condition over the given parameters. | ||
| Preconditions pass `proc.inputs`; postconditions pass `proc.inputs ++ proc.outputs`. -/ | ||
| private def mkConditionProc (name : String) (params : List Parameter) | ||
| (condition : Condition) : Procedure := | ||
| { name := mkId name | ||
| inputs := params | ||
| outputs := [⟨mkId "$result", { val := .TBool, source := none }⟩] | ||
| preconditions := [] | ||
| decreases := none | ||
| isFunctional := true | ||
| body := .Transparent condition.condition } | ||
|
|
||
| /-- Information about a procedure's contracts. -/ | ||
| private structure ContractInfo where | ||
| preNames : List (String × Option String) -- (procName, summary) for each precondition | ||
| postNames : List (String × Option String) -- (procName, summary) for each postcondition | ||
| inputParams : List Parameter | ||
| outputParams : List Parameter | ||
|
|
||
| private def ContractInfo.hasPreCondition (info : ContractInfo) : Bool := !info.preNames.isEmpty | ||
| private def ContractInfo.hasPostCondition (info : ContractInfo) : Bool := !info.postNames.isEmpty | ||
|
|
||
| /-- 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 !proc.isFunctional && (hasPre || hasPost) then | ||
| let preNames := proc.preconditions.zipIdx.map fun (c, i) => | ||
| (preCondProcName proc.name.text i, c.summary) | ||
| let postNames := postconds.zipIdx.map fun (c, i) => | ||
| (postCondProcName proc.name.text i, c.summary) | ||
| m.insert proc.name.text { | ||
| preNames := preNames | ||
| postNames := postNames | ||
| 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 preAssumes : List StmtExprMd := | ||
| proc.preconditions.zip info.preNames |>.map fun (pc, name, _) => | ||
| ⟨.Assume (mkCall name inputArgs), pc.condition.source⟩ | ||
| match proc.body with | ||
| | .Transparent body => | ||
| let postAsserts : List StmtExprMd := | ||
| postconds.zip info.postNames |>.map fun (pc, _name, _summary) => | ||
| let summary := pc.summary.getD "postcondition" | ||
| ⟨.Assert { condition := pc.condition, summary := some summary }, pc.condition.source⟩ | ||
| .Transparent ⟨.Block (preAssumes ++ [body] ++ postAsserts) none, body.source⟩ | ||
| | .Opaque _ (some impl) _ => | ||
| .Opaque postconds (some ⟨.Block (preAssumes ++ [impl]) none, impl.source⟩) [] | ||
| | .Opaque _ none mods => | ||
| .Opaque postconds none mods | ||
| | .Abstract _ => | ||
| .Abstract postconds | ||
| | b => b | ||
|
|
||
| /-- Monad used by the contract-pass rewriter; carries a global counter for | ||
| generating fresh temporary variable names. -/ | ||
| private abbrev ContractM := StateM Nat | ||
|
|
||
| /-- Allocate a fresh temporary name with the `$cp_` prefix. The global counter | ||
| guarantees uniqueness across the entire pass. -/ | ||
| private def freshTemp : ContractM String := do | ||
| let n ← get | ||
| set (n + 1) | ||
| return s!"$cp_{n}" | ||
|
|
||
| /-- Generate temporary variable assignments for input arguments at a call site. | ||
| Returns (temp declarations+assignments, temp variable references). -/ | ||
| private def mkTempAssignments (args : List StmtExprMd) | ||
| (inputParams : List Parameter) (src : Option FileRange) | ||
| : ContractM (List StmtExprMd × List StmtExprMd) := do | ||
| let mut decls : List StmtExprMd := [] | ||
| let mut refs : List StmtExprMd := [] | ||
| for arg in args, i in List.range args.length do | ||
| let tempName ← freshTemp | ||
| let paramType := match inputParams[i]? with | ||
| | some p => p.type | ||
| | none => { val := .Unknown, source := none } | ||
| let param : Parameter := { name := mkId tempName, type := paramType } | ||
| decls := decls ++ [⟨StmtExpr.Assign [mkVarMd (.Declare param)] arg, src⟩] | ||
| refs := refs ++ [mkMd (.Var (.Local (mkId tempName)))] | ||
| return (decls, refs) | ||
|
|
||
| /-- Generate precondition checks (one per precondition) for a call site. -/ | ||
| private def mkPreChecks (info : ContractInfo) (isFunctional : Bool) | ||
| (tempRefs : List StmtExprMd) (src : Option FileRange) : List StmtExprMd := | ||
| if !info.hasPreCondition then [] | ||
| else info.preNames.map fun (name, summary) => | ||
| let call := mkCall name tempRefs | ||
| if isFunctional then | ||
| ⟨.Assume call, src⟩ | ||
| else | ||
| ⟨.Assert { condition := call, summary := some (summary.getD "precondition") }, src⟩ | ||
|
|
||
| /-- Generate postcondition assumes (one per postcondition) for a call site. -/ | ||
| private def mkPostAssumes (info : ContractInfo) | ||
| (tempRefs : List StmtExprMd) (outputArgs : List StmtExprMd) (src : Option FileRange) : List StmtExprMd := | ||
| if !info.hasPostCondition then [] | ||
| else info.postNames.map fun (name, _) => | ||
| ⟨.Assume (mkCall name (tempRefs ++ outputArgs)), src⟩ | ||
|
|
||
| /-- Rewrite call sites in a statement/expression tree. -/ | ||
| private def rewriteCallSites (contractInfoMap : Std.HashMap String ContractInfo) | ||
| (isFunctional : Bool) (expr : StmtExprMd) : ContractM StmtExprMd := do | ||
| let rewriteStaticCall (callee : Identifier) (args : List StmtExprMd) | ||
| (info : ContractInfo) (src : Option FileRange) | ||
| : ContractM (List StmtExprMd) := do | ||
| let (tempDecls, tempRefs) ← mkTempAssignments args info.inputParams src | ||
| let preCheck := mkPreChecks info isFunctional tempRefs src | ||
| let (callStmt, postAssume, returnValue) ← | ||
| if info.hasPostCondition && !info.outputParams.isEmpty then do | ||
| let mut outputTempDecls : List VariableMd := [] | ||
| let mut outputRefs : List StmtExprMd := [] | ||
| for p in info.outputParams do | ||
| let tempName ← freshTemp | ||
| outputTempDecls := outputTempDecls ++ [mkVarMd (.Declare { name := mkId tempName, type := p.type })] | ||
| outputRefs := outputRefs ++ [mkMd (.Var (.Local (mkId tempName)))] | ||
| let callWithOutputs : StmtExprMd := | ||
| ⟨.Assign outputTempDecls ⟨.StaticCall callee tempRefs, src⟩, src⟩ | ||
| let assume := mkPostAssumes info tempRefs outputRefs src | ||
| let retVal : List StmtExprMd := match outputRefs with | ||
| | [single] => [single] | ||
| | _ => [] | ||
| pure (callWithOutputs, assume, retVal) | ||
| else | ||
| pure (⟨.StaticCall callee tempRefs, src⟩, [], []) | ||
| return tempDecls ++ preCheck ++ [callStmt] ++ postAssume ++ returnValue | ||
| let result ← | ||
| mapStmtExprFlattenM (m := ContractM) | ||
| -- Pre: intercept Assign targets (StaticCall ...) before recursion | ||
| (fun e => do | ||
| match e.val with | ||
| | .Assign targets (.mk (.StaticCall callee args) callSrc) => | ||
| match contractInfoMap.get? callee.text with | ||
| | some info => | ||
| let src := e.source | ||
| -- Recurse into arguments | ||
| let args' ← args.mapM (mapStmtExprM (m := ContractM) (fun e' => do | ||
| match e'.val with | ||
| | .StaticCall callee' args' => | ||
| match contractInfoMap.get? callee'.text with | ||
| | some info' => | ||
| let stmts ← rewriteStaticCall callee' args' info' e'.source | ||
| return ⟨.Block stmts none, e'.source⟩ | ||
| | none => return e' | ||
| | _ => return e')) | ||
| let (tempDecls, tempRefs) ← mkTempAssignments args' info.inputParams src | ||
| let callWithTemps : StmtExprMd := ⟨.Assign targets ⟨.StaticCall callee tempRefs, callSrc⟩, src⟩ | ||
| let preCheck := mkPreChecks info isFunctional tempRefs src | ||
| 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 := mkPostAssumes info tempRefs outputArgs src | ||
| return some (tempDecls ++ preCheck ++ [callWithTemps] ++ postAssume) | ||
| | none => return none | ||
| | _ => return none) | ||
| -- Post: handle bare StaticCall | ||
| (fun e => do | ||
| match e.val with | ||
| | .StaticCall callee args => | ||
| match contractInfoMap.get? callee.text with | ||
| | some info => | ||
| let stmts ← rewriteStaticCall callee args info e.source | ||
| return stmts | ||
| | none => return [e] | ||
| | _ => return [e]) expr | ||
| return result | ||
|
|
||
| /-- Rewrite call sites in all bodies of a procedure. -/ | ||
| private def rewriteCallSitesInProc (contractInfoMap : Std.HashMap String ContractInfo) | ||
| (proc : Procedure) : ContractM Procedure := do | ||
| let rw := rewriteCallSites contractInfoMap proc.isFunctional | ||
| match proc.body with | ||
| | .Transparent body => | ||
| let body' ← rw body | ||
| return { proc with body := .Transparent body' } | ||
| | .Opaque posts impl mods => | ||
| let posts' ← posts.mapM (·.mapM rw) | ||
| let impl' ← impl.mapM rw | ||
| let mods' ← mods.mapM rw | ||
| return { proc with body := Body.Opaque posts' impl' mods' } | ||
| | _ => return 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 := match postconds.map (·.condition) with | ||
| | [] => mkMd (.LiteralBool true) | ||
| | [e] => e | ||
| | e :: rest => rest.foldl (fun acc x => mkMd (.PrimitiveOp .And [acc, x])) e | ||
| -- 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 lowerContracts (program : Program) : Program := | ||
| let contractInfoMap := collectContractInfo program.staticProcedures | ||
|
|
||
| -- Generate helper procedures for all procedures with contracts | ||
| let helperProcs := (program.staticProcedures.filter (fun proc => !proc.isFunctional)).flatMap fun proc => | ||
| let postconds := getPostconditions proc.body | ||
| let preProcs := proc.preconditions.zipIdx.map fun (c, i) => | ||
| mkConditionProc (preCondProcName proc.name.text i) proc.inputs c | ||
| let postProcs := postconds.zipIdx.map fun (c, i) => | ||
| mkConditionProc (postCondProcName proc.name.text i) (proc.inputs ++ proc.outputs) c | ||
| preProcs ++ postProcs | ||
|
|
||
| -- Transform procedures: strip contracts, add assume/assert, rewrite call sites | ||
| -- Run all call-site rewriting in a single ContractM to share the global counter. | ||
| let (transformedProcs, _) := (program.staticProcedures.mapM fun (proc : Procedure) => do | ||
| if proc.isFunctional then | ||
| return proc | ||
| else | ||
| let proc : Procedure := 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 : Procedure := 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).run 0 | ||
|
|
||
| { program with staticProcedures := helperProcs ++ transformedProcs } | ||
|
|
||
| public def contractPass : LoweringPass where | ||
| name := "LowerContract" | ||
| documentation := "Lowers pre and postcondition to assertions and assumptions around call-sites and procedure bodies" | ||
| run := fun p _m => | ||
| let p' := lowerContracts p | ||
| (p', [], {}) | ||
| comesBefore := [] | ||
|
|
||
| end -- public section | ||
| end Strata.Laurel | ||
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.