diff --git a/Strata/Backends/CBMC/GOTO/InstToJson.lean b/Strata/Backends/CBMC/GOTO/InstToJson.lean
index 1d729feeac..8bbbec5227 100644
--- a/Strata/Backends/CBMC/GOTO/InstToJson.lean
+++ b/Strata/Backends/CBMC/GOTO/InstToJson.lean
@@ -236,9 +236,8 @@ def instructionToJson (inst : Instruction) : Except String Json := do
("instructionId", Json.str (toString inst.type)),
("locationNumber", Json.num inst.locationNum)
]
- let guardField ← if inst.type == .GOTO || !Expr.beq inst.guard Expr.true then do
+ let guardField ← do
pure [("guard", ← exprToJsonWithNamedFields inst.guard)]
- else pure []
let codeField ← if inst.code == Code.skip then pure [] else do
pure [("code", ← codeToJson inst.code)]
let targetsField := match inst.type, inst.target with
diff --git a/Strata/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean b/Strata/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean
index acb40ec514..44bbee6206 100644
--- a/Strata/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean
+++ b/Strata/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean
@@ -255,6 +255,36 @@ def LExprT.toGotoExpr {TBase: LExprParamsT} [ToString TBase.base.IDMeta] (e : LE
let e1g ← toGotoExpr e1
let e2g ← toGotoExpr e2
return { id := .binary .Equal, type := .Boolean, operands := [e1g, e2g] }
+ -- Quaternary Functions (4 arguments)
+ | .app m (.app _ (.app _ (.app _ (.op _ fn _) e1) e2) e3) e4 =>
+ let op ← fnToGotoID (toString fn)
+ let gty ← m.type.toGotoType
+ let e1g ← toGotoExpr e1
+ let e2g ← toGotoExpr e2
+ let e3g ← toGotoExpr e3
+ let e4g ← toGotoExpr e4
+ return { id := op, type := gty, operands := [e1g, e2g, e3g, e4g] }
+ -- Quinary Functions (5 arguments)
+ | .app m (.app _ (.app _ (.app _ (.app _ (.op _ fn _) e1) e2) e3) e4) e5 =>
+ let op ← fnToGotoID (toString fn)
+ let gty ← m.type.toGotoType
+ let e1g ← toGotoExpr e1
+ let e2g ← toGotoExpr e2
+ let e3g ← toGotoExpr e3
+ let e4g ← toGotoExpr e4
+ let e5g ← toGotoExpr e5
+ return { id := op, type := gty, operands := [e1g, e2g, e3g, e4g, e5g] }
+ -- Senary Functions (6 arguments)
+ | .app m (.app _ (.app _ (.app _ (.app _ (.app _ (.op _ fn _) e1) e2) e3) e4) e5) e6 =>
+ let op ← fnToGotoID (toString fn)
+ let gty ← m.type.toGotoType
+ let e1g ← toGotoExpr e1
+ let e2g ← toGotoExpr e2
+ let e3g ← toGotoExpr e3
+ let e4g ← toGotoExpr e4
+ let e5g ← toGotoExpr e5
+ let e6g ← toGotoExpr e6
+ return { id := op, type := gty, operands := [e1g, e2g, e3g, e4g, e5g, e6g] }
| _ => .error f!"[toGotoExpr] Not yet implemented: {e}"
/--
@@ -354,6 +384,41 @@ def LExpr.toGotoExprCtx {TBase: LExprParams} [ToString $ LExpr TBase.mono]
let tg ← toGotoExprCtx bvars t
let eg ← toGotoExprCtx bvars e
return (Expr.ite cg tg eg)
+ -- N-ary Functions (4+ arguments) — handles function applications with more
+ -- than 3 arguments that aren't caught by the ternary/binary/unary cases.
+ -- Pattern: .app _ (.app _ (.app _ (.app _ (.op _ fn ty) e1) e2) e3) e4
+ | .app _ (.app _ (.app _ (.app _ (.op _ fn (some ty)) e1) e2) e3) e4 =>
+ let op ← fnToGotoID (toString fn)
+ let retty := ty.destructArrow.getLast!
+ let gty ← retty.toGotoType
+ let e1g ← toGotoExprCtx bvars e1
+ let e2g ← toGotoExprCtx bvars e2
+ let e3g ← toGotoExprCtx bvars e3
+ let e4g ← toGotoExprCtx bvars e4
+ return { id := op, type := gty, operands := [e1g, e2g, e3g, e4g] }
+ -- Quinary (5 arguments)
+ | .app _ (.app _ (.app _ (.app _ (.app _ (.op _ fn (some ty)) e1) e2) e3) e4) e5 =>
+ let op ← fnToGotoID (toString fn)
+ let retty := ty.destructArrow.getLast!
+ let gty ← retty.toGotoType
+ let e1g ← toGotoExprCtx bvars e1
+ let e2g ← toGotoExprCtx bvars e2
+ let e3g ← toGotoExprCtx bvars e3
+ let e4g ← toGotoExprCtx bvars e4
+ let e5g ← toGotoExprCtx bvars e5
+ return { id := op, type := gty, operands := [e1g, e2g, e3g, e4g, e5g] }
+ -- Senary (6 arguments)
+ | .app _ (.app _ (.app _ (.app _ (.app _ (.app _ (.op _ fn (some ty)) e1) e2) e3) e4) e5) e6 =>
+ let op ← fnToGotoID (toString fn)
+ let retty := ty.destructArrow.getLast!
+ let gty ← retty.toGotoType
+ let e1g ← toGotoExprCtx bvars e1
+ let e2g ← toGotoExprCtx bvars e2
+ let e3g ← toGotoExprCtx bvars e3
+ let e4g ← toGotoExprCtx bvars e4
+ let e5g ← toGotoExprCtx bvars e5
+ let e6g ← toGotoExprCtx bvars e6
+ return { id := op, type := gty, operands := [e1g, e2g, e3g, e4g, e5g, e6g] }
| _ => .error f!"[toGotoExprCtx] Not yet implemented: {toString e}"
/--
diff --git a/Strata/Cli/VerifyOptions.lean b/Strata/Cli/VerifyOptions.lean
index 71d8d55e6d..7425c9394c 100644
--- a/Strata/Cli/VerifyOptions.lean
+++ b/Strata/Cli/VerifyOptions.lean
@@ -158,7 +158,9 @@ def parseVerifyOptions (pflags : ParsedFlags)
def laurelTranslateFlags : List Flag := [
{ name := "keep-all-files",
help := "Store intermediate Laurel and Core programs in
.",
- takesArg := .arg "dir" }
+ takesArg := .arg "dir" },
+ { name := "always-call-core-functions",
+ help := "Redirect calls to single-output procedures to their pure $asFunction versions (keeps them constant-foldable during symbolic evaluation)." }
]
/-- All CLI flags accepted by Laurel verify commands. -/
diff --git a/Strata/Languages/Core/CoreOp.lean b/Strata/Languages/Core/CoreOp.lean
index af2d2590dc..4404d9c075 100644
--- a/Strata/Languages/Core/CoreOp.lean
+++ b/Strata/Languages/Core/CoreOp.lean
@@ -199,7 +199,7 @@ inductive MapOpKind where
deriving Repr, DecidableEq, Inhabited, BEq, Hashable
def MapOpKind.names : List (MapOpKind × String) :=
- [(.Const, "const"), (.Select, "select"), (.Update, "update")]
+ [(.Const, "mapConst"), (.Select, "select"), (.Update, "update")]
def MapOpKind.toString (k : MapOpKind) : String := lookupName names k
instance : ToString MapOpKind := ⟨MapOpKind.toString⟩
diff --git a/Strata/Languages/Core/DDMTransform/FormatCore.lean b/Strata/Languages/Core/DDMTransform/FormatCore.lean
index 6015bb1832..84979bbbc0 100644
--- a/Strata/Languages/Core/DDMTransform/FormatCore.lean
+++ b/Strata/Languages/Core/DDMTransform/FormatCore.lean
@@ -689,9 +689,26 @@ partial def lappToExpr {M} [Inhabited M]
: ToCSTM M (CoreDDM.Expr M) := do
let (head, args) := Lambda.getLFuncCall e
match head with
- | .op _ fn _ =>
- let argExprs ← args.mapM (lexprToExpr · qLevel)
- lopToExpr fn.name argExprs
+ | .op _ fn ty =>
+ -- `mapConst` (the constant-map builtin) has no inferable key type, so it is
+ -- emitted with an explicit key-type annotation `mapConst(v)`. Recover `K`
+ -- from the op's function type `V → Map K V`.
+ if fn.name == "mapConst" then
+ match args with
+ | [valArg] =>
+ let valCST ← lexprToExpr valArg qLevel
+ let kCST ← match ty with
+ | some (.tcons "arrow" [_, .tcons "Map" [k, _]]) => lmonoTyToCoreType k
+ | some (.tcons "Map" [k, _]) => lmonoTyToCoreType k
+ | _ => pure (CoreType.tvar default unknownTypeVar)
+ -- The value type is inferred from `v` on re-parse, so a placeholder is fine.
+ pure (.map_const default kCST (CoreType.tvar default unknownTypeVar) valCST)
+ | _ =>
+ let argExprs ← args.mapM (lexprToExpr · qLevel)
+ lopToExpr fn.name argExprs
+ else
+ let argExprs ← args.mapM (lexprToExpr · qLevel)
+ lopToExpr fn.name argExprs
| .app _ fn arg =>
-- getLFuncCall couldn't decompose further (fn is not .app or .op)
let fnCST ← lexprToExpr fn qLevel
diff --git a/Strata/Languages/Core/DDMTransform/Grammar.lean b/Strata/Languages/Core/DDMTransform/Grammar.lean
index e5fc5a538a..8b98c756f4 100644
--- a/Strata/Languages/Core/DDMTransform/Grammar.lean
+++ b/Strata/Languages/Core/DDMTransform/Grammar.lean
@@ -103,6 +103,10 @@ fn old (tp : Type, v : tp) : tp => "old " v;
fn map_get (K : Type, V : Type, m : Map K V, k : K) : V => m "[" k "]";
fn map_set (K : Type, V : Type, m : Map K V, k : K, v : V) : Map K V =>
m "[" k ":=" v "]";
+// map_const uses explicit key-type annotation syntax: the key type cannot be
+// inferred from the single value argument, so it is written `mapConst(v)`.
+// The value type V is inferred from `v`.
+fn map_const (K : Type, V : Type, v : V) : Map K V => "mapConst" "<" K ">" "(" v ")";
// seq_empty uses explicit type annotation syntax since there are no value
// arguments to infer the type parameter from.
diff --git a/Strata/Languages/Core/DDMTransform/Translate.lean b/Strata/Languages/Core/DDMTransform/Translate.lean
index e01318cbde..0a1f0cc2ef 100644
--- a/Strata/Languages/Core/DDMTransform/Translate.lean
+++ b/Strata/Languages/Core/DDMTransform/Translate.lean
@@ -977,6 +977,12 @@ partial def translateExpr (p : Program) (bindings : TransBindings) (arg : Arg) :
let i ← translateExpr p bindings ia
let x ← translateExpr p bindings xa
return .mkApp () fn [m, i, x]
+ | .fn _ q`Core.map_const, [_ktp, _vtp, va] =>
+ let kty ← translateLMonoTy bindings _ktp
+ let vty ← translateLMonoTy bindings _vtp
+ let fn : LExpr Core.CoreLParams.mono := (Core.coreOpExpr (.map .Const) (.some (LMonoTy.mkArrow vty [Core.mapTy kty vty])))
+ let v ← translateExpr p bindings va
+ return .mkApp () fn [v]
-- Seq operations
| .fn _ q`Core.seq_length, [_atp, sa] =>
let ety ← translateLMonoTy bindings _atp
diff --git a/Strata/Languages/Core/Factory.lean b/Strata/Languages/Core/Factory.lean
index d25bd42640..b1730f5a6f 100644
--- a/Strata/Languages/Core/Factory.lean
+++ b/Strata/Languages/Core/Factory.lean
@@ -363,18 +363,21 @@ def reNoneFunc : WFLFunc CoreLParams :=
nullaryUneval "Re.None" mty[regex]
/- A constant `Map` constructor with type `∀k, v. v → Map k v`.
- `const(d)` returns a map where every key maps to the value `d`. -/
+ `mapConst(d)` returns a map where every key maps to the value `d`.
+ Named `mapConst` (not `const`) to avoid colliding with the `const`
+ declaration keyword in the Core grammar, which would otherwise make
+ pretty-printed programs fail to re-parse. -/
def mapConstFunc : WFLFunc CoreLParams :=
- polyUneval "const" ["k", "v"]
+ polyUneval "mapConst" ["k", "v"]
[("d", mty[%v])]
(mapTy mty[%k] mty[%v])
(axioms := [
esM[∀ (%v): -- %1 d
(∀ (%k): -- %0 kk
{(((~select : (Map %k %v) → %k → %v)
- ((~const : %v → (Map %k %v)) %1)) %0)}
+ ((~mapConst : %v → (Map %k %v)) %1)) %0)}
(((~select : (Map %k %v) → %k → %v)
- ((~const : %v → (Map %k %v)) %1)) %0) == %1)]
+ ((~mapConst : %v → (Map %k %v)) %1)) %0) == %1)]
])
/- A `Map` selection function with type `∀k, v. Map k v → k → v`. -/
diff --git a/Strata/Languages/Laurel/ConstrainedTypeElim.lean b/Strata/Languages/Laurel/ConstrainedTypeElim.lean
index 47b3bb709f..ccccf32393 100644
--- a/Strata/Languages/Laurel/ConstrainedTypeElim.lean
+++ b/Strata/Languages/Laurel/ConstrainedTypeElim.lean
@@ -71,9 +71,9 @@ def constraintCallFor (ptMap : ConstrainedTypeMap) (ty : HighType)
(varName : Identifier) (src : Option FileRange := none) : Option StmtExprMd :=
constraintCallForExpr ptMap ty ⟨.Var (.Local varName), src⟩ src
-/-- Generate a constraint function for a constrained type.
- For nested types, the function calls the parent's constraint function. -/
-def mkConstraintFunc (ptMap : ConstrainedTypeMap) (ct : ConstrainedType) : Procedure :=
+/-- Generate a constraint procedure for a constrained type.
+ For nested types, the procedure calls the parent's constraint procedure. -/
+def mkConstraintProc (ptMap : ConstrainedTypeMap) (ct : ConstrainedType) : Procedure :=
let baseType := resolveType ptMap ct.base
let bodyExpr: StmtExprMd := match ct.base.val with
| .UserDefined parent =>
@@ -90,7 +90,6 @@ def mkConstraintFunc (ptMap : ConstrainedTypeMap) (ct : ConstrainedType) : Proce
inputs := [{ name := ct.valueName, type := baseType }]
outputs := [{ name := mkId "result", type := { val := .TBool, source := none } }]
body := .Transparent { val := .Return bodyExpr, source := none }
- isFunctional := true
decreases := none
preconditions := [] }
@@ -166,7 +165,7 @@ def elimProc (ptMap : ConstrainedTypeMap) (model : SemanticModel) (proc : Proced
let inputRequires : List Condition := proc.inputs.filterMap fun p =>
(constraintCallFor ptMap p.type.val p.name (src := p.type.source)).map
fun c => { condition := c }
- let outputEnsures : List Condition := if proc.isFunctional then [] else proc.outputs.filterMap fun p =>
+ let outputEnsures : List Condition := proc.outputs.filterMap fun p =>
(constraintCallFor ptMap p.type.val p.name (src := p.type.source)).map
fun c => { condition := ⟨c.val, p.type.source⟩ }
let body' := match proc.body with
@@ -174,8 +173,7 @@ def elimProc (ptMap : ConstrainedTypeMap) (model : SemanticModel) (proc : Proced
let body := elimStmts ptMap model bodyExpr
if outputEnsures.isEmpty then .Transparent body
else
- let retBody := if proc.isFunctional then ⟨.Return (some body), bodyExpr.source⟩ else body
- .Opaque outputEnsures (some retBody) []
+ .Opaque outputEnsures (some body) []
| .Opaque postconds impl modif =>
let impl' := impl.map (elimStmts ptMap model)
.Opaque (postconds ++ outputEnsures) impl' modif
@@ -206,7 +204,6 @@ private def mkWitnessProc (ptMap : ConstrainedTypeMap) (ct : ConstrainedType) :
outputs := []
body := .Opaque [] (some ⟨.Block [witnessInit, assert] none, src⟩) []
preconditions := []
- isFunctional := false
decreases := none }
/-- Eliminate constrained types within a composite type definition: resolve
@@ -227,13 +224,9 @@ public def constrainedTypeElim (model : SemanticModel) (program : Program)
let ptMap := buildConstrainedTypeMap program.types
if ptMap.isEmpty then (program, []) else
let constraintFuncs := program.types.filterMap fun
- | .Constrained ct => some (mkConstraintFunc ptMap ct) | _ => none
+ | .Constrained ct => some (mkConstraintProc ptMap ct) | _ => none
let witnessProcedures := program.types.filterMap fun
| .Constrained ct => some (mkWitnessProc ptMap ct) | _ => none
- let funcDiags := program.staticProcedures.foldl (init := []) fun acc proc =>
- if proc.isFunctional && proc.outputs.any (fun p => isConstrainedType ptMap p.type.val) then
- acc.cons (diagnosticFromSource proc.name.source "constrained return types on functions are not yet supported")
- else acc
({ program with
staticProcedures := constraintFuncs ++ program.staticProcedures.map (elimProc ptMap model)
++ witnessProcedures
@@ -241,7 +234,7 @@ public def constrainedTypeElim (model : SemanticModel) (program : Program)
| .Constrained _ => none
| .Composite ct => some (.Composite (elimCompositeType ptMap model ct))
| other => some other },
- funcDiags)
+ [])
/-- Pipeline pass: constrained type elimination. -/
public def constrainedTypeElimPass : LoweringPass where
diff --git a/Strata/Languages/Laurel/ContractPass.lean b/Strata/Languages/Laurel/ContractPass.lean
index 2d9b98ffb0..e992f535db 100644
--- a/Strata/Languages/Laurel/ContractPass.lean
+++ b/Strata/Languages/Laurel/ContractPass.lean
@@ -62,13 +62,16 @@ private def paramsToArgs (params : List Parameter) : List StmtExprMd :=
Preconditions pass `proc.inputs`; postconditions use `mkPostConditionProc`. -/
private def mkConditionProc (name : String) (params : List Parameter)
(condition : Condition) : Procedure :=
+ let src := condition.condition.source
+ let assign : StmtExprMd := ⟨.Assign [⟨.Local (mkId "$result"), src⟩] condition.condition, src⟩
+ let exit : StmtExprMd := ⟨.Exit returnLabel, src⟩
+ let body : StmtExprMd := ⟨.Block [assign, exit] (some returnLabel), src⟩
{ name := mkId name
inputs := params
outputs := [⟨mkId "$result", { val := .TBool, source := none }⟩]
preconditions := []
decreases := none
- isFunctional := true
- body := .Transparent condition.condition }
+ body := .Transparent body }
/-- Suffix appended to a procedure's output-parameter names when they are lowered
into a postcondition helper *function*.
@@ -118,13 +121,17 @@ private def mkPostConditionProc (name : String) (inputs outputs : List Parameter
(condition : Condition) : Procedure :=
let outputNames := outputs.map (·.name.text)
let renamedOutputs := outputs.map (fun p => { p with name := mkId (p.name.text ++ outParamSuffix) })
+ let condExpr := renameOutputsInPostExpr outputNames condition.condition
+ let src := condExpr.source
+ let assign : StmtExprMd := ⟨.Assign [⟨.Local (mkId "$result"), src⟩] condExpr, src⟩
+ let exit : StmtExprMd := ⟨.Exit returnLabel, src⟩
+ let body : StmtExprMd := ⟨.Block [assign, exit] (some returnLabel), src⟩
{ name := mkId name
inputs := inputs ++ renamedOutputs
outputs := [⟨mkId "$result", { val := .TBool, source := none }⟩]
preconditions := []
decreases := none
- isFunctional := true
- body := .Transparent (renameOutputsInPostExpr outputNames condition.condition) }
+ body := .Transparent body }
/-- Information about a procedure's contracts. -/
private structure ContractInfo where
@@ -142,7 +149,7 @@ private def collectContractInfo (procs : List Procedure) : Std.HashMap String Co
let postconds := getPostconditions proc.body
let hasPre := !proc.preconditions.isEmpty
let hasPost := !postconds.isEmpty
- if !proc.isFunctional && (hasPre || hasPost) then
+ if 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) =>
@@ -162,19 +169,21 @@ private def transformProcBody (proc : Procedure) (info : ContractInfo) : Body :=
let preAssumes : List StmtExprMd :=
proc.preconditions.zip info.preNames |>.map fun (pc, name, _) =>
⟨.Assume (mkCall name inputArgs), pc.condition.source⟩
+ let postAsserts : List StmtExprMd :=
+ postconds.zip info.postNames |>.filterMap fun (pc, _name, _summary) =>
+ if pc.free then none
+ else
+ let summary := pc.summary.getD "postcondition"
+ some ⟨.Assert { condition := pc.condition, summary := some summary }, 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 [] (some ⟨.Block (preAssumes ++ [impl] ++ postAsserts) none, impl.source⟩) []
| .Opaque _ none mods =>
- .Opaque postconds none mods
+ .Opaque [] none mods
| .Abstract _ =>
- .Abstract postconds
+ .Abstract []
| b => b
/-- Monad used by the contract-pass rewriter; carries a global counter for
@@ -206,15 +215,12 @@ private def mkTempAssignments (args : List StmtExprMd)
return (decls, refs)
/-- Generate precondition checks (one per precondition) for a call site. -/
-private def mkPreChecks (info : ContractInfo) (isFunctional : Bool)
+private def mkPreChecks (info : ContractInfo)
(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⟩
+ ⟨.Assert { condition := call, summary := some (summary.getD "precondition") }, src⟩
/-- Generate postcondition assumes (one per postcondition) for a call site. -/
private def mkPostAssumes (info : ContractInfo)
@@ -249,12 +255,12 @@ private def mkCallArgs (info : ContractInfo) (origArgs tempRefs : List StmtExprM
/-- Rewrite call sites in a statement/expression tree. -/
private def rewriteCallSites (contractInfoMap : Std.HashMap String ContractInfo)
- (isFunctional : Bool) (expr : StmtExprMd) : ContractM StmtExprMd := do
+ (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 preCheck := mkPreChecks info tempRefs src
let (callStmt, postAssume, returnValue) ←
if info.hasPostCondition && !info.outputParams.isEmpty then do
let mut outputTempDecls : List VariableMd := []
@@ -295,7 +301,7 @@ private def rewriteCallSites (contractInfoMap : Std.HashMap String ContractInfo)
let (tempDecls, tempRefs) ← mkTempAssignments args' info.inputParams src
let callArgs := mkCallArgs info args' tempRefs
let callWithTemps : StmtExprMd := ⟨.Assign targets ⟨.StaticCall callee callArgs, callSrc⟩, src⟩
- let preCheck := mkPreChecks info isFunctional tempRefs src
+ let preCheck := mkPreChecks info tempRefs src
let outputArgs := targets.filterMap fun t =>
match t.val with
| .Local name => some (mkMd (.Var (.Local name)))
@@ -320,7 +326,7 @@ private def rewriteCallSites (contractInfoMap : Std.HashMap String ContractInfo)
/-- 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
+ let rw := rewriteCallSites contractInfoMap
match proc.body with
| .Transparent body =>
let body' ← rw body
@@ -412,7 +418,7 @@ def lowerContracts (program : Program) : Program × List DiagnosticModel :=
let diagnostics := program.staticProcedures.filterMap invokeOnOutputRefError
-- Generate helper procedures for all procedures with contracts
- let helperProcs := (program.staticProcedures.filter (fun proc => !proc.isFunctional)).flatMap fun proc =>
+ let helperProcs := program.staticProcedures.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
@@ -423,28 +429,25 @@ def lowerContracts (program : Program) : Program × List DiagnosticModel :=
-- 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 if invokeOnOutputRefError proc |>.isSome then
- -- Skip axiom generation; diagnostic already emitted
- { proc with invokeOn := none }
- else { proc with
- axioms := [mkInvokeOnAxiom proc.inputs trigger proc.preconditions 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
+ let proc : Procedure := match proc.invokeOn with
+ | some trigger =>
+ let postconds := getPostconditions proc.body
+ if postconds.isEmpty then { proc with invokeOn := none }
+ else if invokeOnOutputRefError proc |>.isSome then
+ -- Skip axiom generation; diagnostic already emitted
+ { proc with invokeOn := none }
+ else { proc with
+ axioms := [mkInvokeOnAxiom proc.inputs trigger proc.preconditions 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 }, diagnostics)
diff --git a/Strata/Languages/Laurel/CoreDefinitionsForLaurel.lean b/Strata/Languages/Laurel/CoreDefinitionsForLaurel.lean
index dfe3e09782..8e22c41b43 100644
--- a/Strata/Languages/Laurel/CoreDefinitionsForLaurel.lean
+++ b/Strata/Languages/Laurel/CoreDefinitionsForLaurel.lean
@@ -16,7 +16,7 @@ namespace Strata.Laurel
public section
/--
-Core map operations (`select`, `update`, `const`) expressed in Laurel syntax.
+Core map operations (`select`, `update`, `mapConst`) expressed in Laurel syntax.
These are polymorphic map primitives used by the Laurel-to-Core translator.
Since Laurel doesn't have polymorphic types, `int` is used as a placeholder type
for all parameters — the actual types are inferred during Core translation.
@@ -35,13 +35,13 @@ datatype LaurelUnit { MkLaurelUnit() }
// And remove the hacky filter in HeapParameterization
datatype Box { MkBox() }
-function select(map: int, key: int) : Box
+procedure select(map: int, key: int) : Box
external;
-function update(map: int, key: int, value: int) : Box
+procedure update(map: int, key: int, value: int) : Box
external;
-function const(value: int) : Box
+procedure mapConst(value: int) : Box
external;
#end
diff --git a/Strata/Languages/Laurel/CoreGroupingAndOrdering.lean b/Strata/Languages/Laurel/CoreGroupingAndOrdering.lean
index 85303d227e..cef4425859 100644
--- a/Strata/Languages/Laurel/CoreGroupingAndOrdering.lean
+++ b/Strata/Languages/Laurel/CoreGroupingAndOrdering.lean
@@ -203,8 +203,14 @@ open Std (Format ToFormat)
public section
+/-- Format a procedure as a function, replacing the leading "procedure" keyword with "function". -/
+private def formatAsFunction (proc : Procedure) : Format :=
+ let s := (ToFormat.format proc).pretty 100
+ let s := if s.startsWith "procedure" then "function" ++ s.drop "procedure".length else s
+ Format.text s
+
def formatOrderedDecl : OrderedDecl → Format
- | .funcs funcs _ => Format.joinSep (funcs.map ToFormat.format) "\n\n"
+ | .funcs funcs _ => Format.joinSep (funcs.map formatAsFunction) "\n\n"
| .procedure proc => ToFormat.format proc
| .datatypes dts => Format.joinSep (dts.map ToFormat.format) "\n\n"
| .constant c => ToFormat.format c
diff --git a/Strata/Languages/Laurel/DesugarShortCircuit.lean b/Strata/Languages/Laurel/DesugarShortCircuit.lean
index 9c7c24dc76..c134b00b81 100644
--- a/Strata/Languages/Laurel/DesugarShortCircuit.lean
+++ b/Strata/Languages/Laurel/DesugarShortCircuit.lean
@@ -50,7 +50,8 @@ private def desugarShortCircuitNode (imperativeCallees : List String) (expr : St
/-- Desugar short-circuit operators in a program. -/
def desugarShortCircuit (program : Program) : Program :=
- let imperativeCallees := (program.staticProcedures.filter (!·.isFunctional)).map (·.name.text)
+ let imperativeCallees := program.staticProcedures.map (·.name.text)
+ -- TODO shouldn't imperativeCallees always be empty here?
mapProgram (mapStmtExpr (desugarShortCircuitNode imperativeCallees)) program
end -- public section
diff --git a/Strata/Languages/Laurel/EliminateDeterministicHoles.lean b/Strata/Languages/Laurel/EliminateDeterministicHoles.lean
index 83538bf728..72cd884616 100644
--- a/Strata/Languages/Laurel/EliminateDeterministicHoles.lean
+++ b/Strata/Languages/Laurel/EliminateDeterministicHoles.lean
@@ -34,7 +34,7 @@ structure ElimHoleState where
private abbrev ElimHoleM := StateM ElimHoleState
-/-- Generate a fresh uninterpreted function for a typed hole and return a call to it. -/
+/-- Generate a fresh uninterpreted procedure for a typed hole and return a call to it. -/
private def mkHoleCall (source : Option FileRange) (holeType : HighTypeMd) : ElimHoleM StmtExprMd := do
let s ← get
let n := s.counter
@@ -47,7 +47,6 @@ private def mkHoleCall (source : Option FileRange) (holeType : HighTypeMd) : Eli
outputs := [{ name := "$result", type := holeType }]
preconditions := []
decreases := none
- isFunctional := true
body := .Opaque [] none []
}
modify fun s => { s with generatedFunctions := s.generatedFunctions ++ [holeProc] }
diff --git a/Strata/Languages/Laurel/EliminateReturnStatements.lean b/Strata/Languages/Laurel/EliminateReturnStatements.lean
index c0129afc07..7008e44648 100644
--- a/Strata/Languages/Laurel/EliminateReturnStatements.lean
+++ b/Strata/Languages/Laurel/EliminateReturnStatements.lean
@@ -24,10 +24,8 @@ namespace Strata.Laurel
public section
-private def returnLabel : String := "$return"
-
-
-
+@[expose, match_pattern]
+public def returnLabel : String := "$return"
/-- Transform a single procedure: wrap body in a labelled block and replace returns. -/
private def eliminateReturnStmts (proc : Procedure) : Procedure :=
diff --git a/Strata/Languages/Laurel/EliminateValueInReturns.lean b/Strata/Languages/Laurel/EliminateValueInReturns.lean
index 3d274f6c48..a6a10e457c 100644
--- a/Strata/Languages/Laurel/EliminateValueInReturns.lean
+++ b/Strata/Languages/Laurel/EliminateValueInReturns.lean
@@ -56,7 +56,7 @@ def hasValuedReturn (stmt : StmtExprMd) : Bool :=
/-- Apply value-return elimination to a single procedure. Rewrites `return expr`
into `outParam := expr; return` for any procedure with exactly one output
- parameter (functional and non-functional alike).
+ parameter.
Emits an error if a valued return is used with zero or multiple output parameters. -/
def eliminateValueReturnsInProc (proc : Procedure) : Procedure :=
match proc.outputs with
diff --git a/Strata/Languages/Laurel/Grammar/AbstractToConcreteTreeTranslator.lean b/Strata/Languages/Laurel/Grammar/AbstractToConcreteTreeTranslator.lean
index 145c39694f..8dc17d952e 100644
--- a/Strata/Languages/Laurel/Grammar/AbstractToConcreteTreeTranslator.lean
+++ b/Strata/Languages/Laurel/Grammar/AbstractToConcreteTreeTranslator.lean
@@ -233,7 +233,6 @@ private def modifiesClausesToArgs (modifies : List StmtExprMd) : Array Arg :=
wildcardArgs ++ specificArgs
private def procedureToOp (proc : Procedure) : StrataDDM.Operation :=
- let opName := if proc.isFunctional then "function" else "procedure"
let params := proc.inputs.map parameterToArg |>.toArray
let returnTypeArg : Arg :=
match proc.outputs with
@@ -256,14 +255,7 @@ private def procedureToOp (proc : Procedure) : StrataDDM.Operation :=
laurelOp "invokeOnClause" #[stmtExprToArg e])
let (opaqueSpecArg, bodyArg) := match proc.body with
| .Transparent body =>
- -- For functions, the body is implicitly wrapped in a Return by ConcreteToAbstract;
- -- unwrap it here so the concrete output doesn't show an explicit `return`.
- let emitBody := if proc.isFunctional then
- match body.val with
- | .Return (some inner) => inner
- | _ => body
- else body
- (optionArg none, optionArg (some (laurelOp "body" #[stmtExprToArg emitBody])))
+ (optionArg none, optionArg (some (laurelOp "body" #[stmtExprToArg body])))
| .Opaque postconds impl modifies =>
let ens := postconds.map ensuresClauseToArg |>.toArray
let mods := if modifies.isEmpty then #[] else modifiesClausesToArgs modifies
@@ -275,7 +267,7 @@ private def procedureToOp (proc : Procedure) : StrataDDM.Operation :=
| .External =>
(optionArg none, optionArg (some (laurelOp "externalBody")))
{ ann := sr
- name := { dialect := "Laurel", name := opName }
+ name := { dialect := "Laurel", name := "procedure" }
args := #[
ident proc.name.text,
commaSep params,
diff --git a/Strata/Languages/Laurel/Grammar/ConcreteToAbstractTreeTranslator.lean b/Strata/Languages/Laurel/Grammar/ConcreteToAbstractTreeTranslator.lean
index bf5e10dd12..78673dfeaa 100644
--- a/Strata/Languages/Laurel/Grammar/ConcreteToAbstractTreeTranslator.lean
+++ b/Strata/Languages/Laurel/Grammar/ConcreteToAbstractTreeTranslator.lean
@@ -150,7 +150,6 @@ instance : Inhabited Procedure where
outputs := []
preconditions := []
decreases := none
- isFunctional := false
invokeOn := none
body := .Transparent { val := .LiteralBool true, source := none }
}
@@ -354,7 +353,7 @@ partial def translateStmtExpr (arg : Arg) : TransM StmtExprMd := do
let cond ← translateStmtExpr condArg
let invariants ← translateInvariantClauses translateStmtExpr invSeqArg
let body ← translateStmtExpr bodyArg
- return mkStmtExprMd (.While cond invariants none body) src
+ return mkStmtExprMd (.While cond invariants none body false) src
| q`Laurel.forLoop, #[initArg, condArg, stepArg, invSeqArg, bodyArg] =>
let init ← translateStmtExpr initArg
let cond ← translateStmtExpr condArg
@@ -362,7 +361,7 @@ partial def translateStmtExpr (arg : Arg) : TransM StmtExprMd := do
let invariants ← translateInvariantClauses translateStmtExpr invSeqArg
let body ← translateStmtExpr bodyArg
let whileBody := mkStmtExprMd (.Block [body, step] none) src
- let whileStmt := mkStmtExprMd (.While cond invariants none whileBody) src
+ let whileStmt := mkStmtExprMd (.While cond invariants none whileBody false) src
return mkStmtExprMd (.Block [init, whileStmt] none) src
| q`Laurel.doWhile, #[bodyArg, condArg, invSeqArg] =>
-- A `do … while` is a post-test `While`. The `EliminateDoWhile` pass
@@ -568,11 +567,6 @@ def parseProcedure (arg : Arg) : TransM Procedure := do
| _, _ => TransM.error s!"Expected body or externalBody operation, got {repr bodyOp.name}"
| .option _ none => pure none
| _ => TransM.error s!"Expected body, got {repr bodyArg}"
- -- For functions, wrap the body in a Return so the last expression
- -- is treated as the return value by downstream passes.
- let body := if op.name == q`Laurel.function then
- body.map fun b => ⟨.Return (some b), b.source⟩
- else body
-- Determine procedure body kind
let procBody :=
if isExternal then Body.External
@@ -586,7 +580,6 @@ def parseProcedure (arg : Arg) : TransM Procedure := do
outputs := returnParameters
preconditions := preconditions
decreases := none
- isFunctional := op.name == q`Laurel.function
invokeOn := invokeOn
body := procBody
}
diff --git a/Strata/Languages/Laurel/Grammar/LaurelGrammar.st b/Strata/Languages/Laurel/Grammar/LaurelGrammar.st
index 53f0c33f99..9d2ec74e72 100644
--- a/Strata/Languages/Laurel/Grammar/LaurelGrammar.st
+++ b/Strata/Languages/Laurel/Grammar/LaurelGrammar.st
@@ -118,7 +118,7 @@ category ElseBranch;
op elseBranch(stmts : StmtExpr) : ElseBranch => @[prec(0)] "\nelse " stmts;
op ifThenElse (cond: StmtExpr, thenBranch: StmtExpr, elseBranch: Option ElseBranch): StmtExpr =>
- @[prec(20)] "if " cond "\nthen " thenBranch:0 elseBranch:0;
+ @[prec(20)] "if " cond indent(2, "\nthen " thenBranch:0 elseBranch:0);
op assert (cond : StmtExpr, errorMessage: Option ErrorSummary) : StmtExpr => @[prec(0)] "assert " cond:0 errorMessage:0;
op assume (cond : StmtExpr) : StmtExpr => @[prec(0)] "assume " cond:0;
@@ -210,15 +210,6 @@ op procedure (name : Ident, parameters: CommaSepBy Parameter,
body : Option Body) : Procedure =>
"procedure " name "(" parameters ")" returnType returnParameters requires invokeOn opaqueSpec body ";";
-op function (name : Ident, parameters: CommaSepBy Parameter,
- returnType: Option ReturnType,
- returnParameters: Option ReturnParameters,
- requires: Seq RequiresClause,
- invokeOn: Option InvokeOnClause,
- opaqueSpec: Option OpaqueSpec,
- body : Option Body) : Procedure =>
- "function " name "(" parameters ")" returnType returnParameters requires invokeOn opaqueSpec body ";";
-
op composite (name: Ident, extending: Option Extends, fields: Seq Field, procedures: Seq Procedure): Composite => "composite " name extending " {" fields procedures " }";
category ConstrainedType;
diff --git a/Strata/Languages/Laurel/HeapParameterizationConstants.lean b/Strata/Languages/Laurel/HeapParameterizationConstants.lean
index a9c6e80206..c81bd0022d 100644
--- a/Strata/Languages/Laurel/HeapParameterizationConstants.lean
+++ b/Strata/Languages/Laurel/HeapParameterizationConstants.lean
@@ -50,22 +50,19 @@ datatype Heap {
}
// Read a field from the heap: readField(heap, obj, field) = Heap..data!(heap)[obj][field]
-function readField(heap: Heap, obj: Composite, field: Field): Box {
- select(select(Heap..data!(heap), obj), field)
-};
+procedure readField(heap: Heap, obj: Composite, field: Field): Box
+ return select(select(Heap..data!(heap), obj), field);
// Update a field in the heap
-function updateField(heap: Heap, obj: Composite, field: Field, val: Box): Heap {
- MkHeap(
+procedure updateField(heap: Heap, obj: Composite, field: Field, val: Box): Heap
+ return MkHeap(
update(Heap..data!(heap), obj,
update(select(Heap..data!(heap), obj), field, val)),
- Heap..nextReference!(heap))
-};
+ Heap..nextReference!(heap));
// Increment the heap allocation nextReference, returning a new heap
-function increment(heap: Heap): Heap {
- MkHeap(Heap..data!(heap), Heap..nextReference!(heap) + 1)
-};
+procedure increment(heap: Heap): Heap
+ return MkHeap(Heap..data!(heap), Heap..nextReference!(heap) + 1);
#end
diff --git a/Strata/Languages/Laurel/InlineLocalVariables.lean b/Strata/Languages/Laurel/InlineLocalVariables.lean
new file mode 100644
index 0000000000..6bbd7abe81
--- /dev/null
+++ b/Strata/Languages/Laurel/InlineLocalVariables.lean
@@ -0,0 +1,228 @@
+/-
+ Copyright Strata Contributors
+
+ SPDX-License-Identifier: Apache-2.0 OR MIT
+-/
+module
+
+public import Strata.Languages.Laurel.LaurelPass
+public import Strata.Languages.Laurel.UnorderedCore
+import Strata.Languages.Laurel.LaurelAST
+import Strata.Languages.Laurel.TransparencyPass
+
+/-!
+## Inline Local Variables Pass
+
+Runs after the transparency pass and operates **only on functions** (the
+`$asFunction` copies produced by the transparency pass). Function bodies are
+pure expressions and cannot contain local variable declarations once they reach
+the Core schema translation. This pass eliminates them by inlining.
+
+For each local variable of the form `var := `, every reference to
+`` that occurs after the declaration is replaced with ``, and the
+declaration itself is dropped. The inlined expression is itself inlined first,
+so chains like
+
+```
+var a := 1;
+var b := a + 1;
+```
+
+become `b ↦ 1 + 1`.
+
+Because functions are pure, an inlined variable is never reassigned. Any
+assignment to such a `` is therefore a user error and emits a diagnostic.
+-/
+
+namespace Strata.Laurel
+
+/-- Substitution from an inlined local variable name to the expression it was
+ initialized with. Newer bindings are pushed to the front so `List.lookup`
+ finds the most recent declaration first. -/
+private abbrev InlineSubst := List (Identifier × StmtExprMd)
+
+/-- Diagnostics are accumulated in the state. -/
+private abbrev InlineM := StateM (Array DiagnosticModel)
+
+private def emitDiag (d : DiagnosticModel) : InlineM Unit :=
+ modify (·.push d)
+
+public section
+
+mutual
+
+/--
+Inline local variables within an expression, given the substitution in scope.
+
+Only `Block` nodes introduce new (sequential) scope, so the substitution is not
+threaded back out of `inlineExpr`; child expressions simply inherit `subst`.
+-/
+private partial def inlineExpr (subst : InlineSubst) (expr : StmtExprMd) : InlineM StmtExprMd := do
+ let source := expr.source
+ match expr.val with
+ | .Var (.Local name) =>
+ match subst.lookup name with
+ | some replacement => return replacement
+ | none => return expr
+ | .Var (.Field target fieldName) =>
+ return ⟨.Var (.Field (← inlineExpr subst target) fieldName), source⟩
+ | .Var (.Declare _) => return expr
+
+ | .Block stmts label =>
+ let stmts' ← inlineBlockStmts subst stmts
+ return ⟨.Block stmts' label, source⟩
+
+ | .Assign targets value =>
+ -- An assignment to an inlined local is contradictory: report it.
+ for t in targets do
+ match t.val with
+ | .Local name =>
+ if (subst.lookup name).isSome then
+ emitDiag (diagnosticFromSource (t.source.orElse fun _ => source)
+ s!"cannot assign to '{name.text}': it is an inlined local variable")
+ | _ => pure ()
+ let targets' ← targets.mapM (inlineVariable subst)
+ let value' ← inlineExpr subst value
+ return ⟨.Assign targets' value', source⟩
+
+ | .IfThenElse cond th el =>
+ let cond' ← inlineExpr subst cond
+ let th' ← inlineExpr subst th
+ let el' ← el.mapM (inlineExpr subst)
+ return ⟨.IfThenElse cond' th' el', source⟩
+
+ | .While cond invs dec body postTest =>
+ let cond' ← inlineExpr subst cond
+ let invs' ← invs.mapM (inlineExpr subst)
+ let dec' ← dec.mapM (inlineExpr subst)
+ let body' ← inlineExpr subst body
+ return ⟨.While cond' invs' dec' body' postTest, source⟩
+
+ | .Return value =>
+ return ⟨.Return (← value.mapM (inlineExpr subst)), source⟩
+
+ | .PrimitiveOp op args skipProof =>
+ return ⟨.PrimitiveOp op (← args.mapM (inlineExpr subst)) skipProof, source⟩
+
+ | .StaticCall callee args =>
+ return ⟨.StaticCall callee (← args.mapM (inlineExpr subst)), source⟩
+
+ | .InstanceCall target callee args =>
+ let target' ← inlineExpr subst target
+ let args' ← args.mapM (inlineExpr subst)
+ return ⟨.InstanceCall target' callee args', source⟩
+
+ | .PureFieldUpdate target fieldName newValue =>
+ let target' ← inlineExpr subst target
+ let newValue' ← inlineExpr subst newValue
+ return ⟨.PureFieldUpdate target' fieldName newValue', source⟩
+
+ | .ReferenceEquals lhs rhs =>
+ return ⟨.ReferenceEquals (← inlineExpr subst lhs) (← inlineExpr subst rhs), source⟩
+
+ | .AsType target ty =>
+ return ⟨.AsType (← inlineExpr subst target) ty, source⟩
+
+ | .IsType target ty =>
+ return ⟨.IsType (← inlineExpr subst target) ty, source⟩
+
+ | .Quantifier mode param trigger body =>
+ -- The bound variable shadows any inlined local of the same name.
+ let innerSubst := subst.filter (·.1 != param.name)
+ let trigger' ← trigger.mapM (inlineExpr innerSubst)
+ let body' ← inlineExpr innerSubst body
+ return ⟨.Quantifier mode param trigger' body', source⟩
+
+ | .Assigned name =>
+ return ⟨.Assigned (← inlineExpr subst name), source⟩
+
+ | .Old value =>
+ return ⟨.Old (← inlineExpr subst value), source⟩
+
+ | .Fresh value =>
+ return ⟨.Fresh (← inlineExpr subst value), source⟩
+
+ | .Assert cond =>
+ return ⟨.Assert { cond with condition := ← inlineExpr subst cond.condition }, source⟩
+
+ | .Assume cond =>
+ return ⟨.Assume (← inlineExpr subst cond), source⟩
+
+ | .ProveBy value proof =>
+ return ⟨.ProveBy (← inlineExpr subst value) (← inlineExpr subst proof), source⟩
+
+ | .ContractOf ty func =>
+ return ⟨.ContractOf ty (← inlineExpr subst func), source⟩
+
+ | .IncrDecr mode op target =>
+ return ⟨.IncrDecr mode op (← inlineVariable subst target), source⟩
+
+ -- Leaves: nothing to inline.
+ | .Exit _ | .LiteralInt _ | .LiteralBool _ | .LiteralString _ | .LiteralDecimal _
+ | .LiteralBv _ _ | .New _ | .This | .Abstract | .All | .Hole .. => return expr
+
+/-- Inline within the target expression of a field variable; other variable
+ forms have no sub-expressions to rewrite. -/
+private partial def inlineVariable (subst : InlineSubst) (v : VariableMd) : InlineM VariableMd := do
+ match v.val with
+ | .Field target fieldName =>
+ return ⟨.Field (← inlineExpr subst target) fieldName, v.source⟩
+ | _ => return v
+
+/--
+Process the statements of a block sequentially, threading the substitution.
+
+A `var := ` declaration (`Assign` with a single `Declare` target)
+is removed, and `` is bound to the inlined `` for the remaining
+statements. All other statements are inlined under the current substitution.
+-/
+private partial def inlineBlockStmts (subst : InlineSubst) (stmts : List StmtExprMd) : InlineM (List StmtExprMd) := do
+ match stmts with
+ | [] => return []
+ | stmt :: rest =>
+ match stmt.val with
+ | .Assign [⟨.Declare param, _⟩] value =>
+ let value' ← inlineExpr subst value
+ inlineBlockStmts ((param.name, value') :: subst) rest
+ | _ =>
+ let stmt' ← inlineExpr subst stmt
+ let rest' ← inlineBlockStmts subst rest
+ return stmt' :: rest'
+
+end
+
+/-- Inline local variables in a single function, returning the rewritten
+ procedure and any diagnostics. -/
+def inlineLocalVariablesInFunction (proc : Procedure) : Procedure × Array DiagnosticModel :=
+ match proc.body with
+ | .Transparent body =>
+ let (body', diags) := (inlineExpr [] body).run #[]
+ ({ proc with body := .Transparent body' }, diags)
+ | .Opaque postconds impl modif =>
+ match impl with
+ | some i =>
+ let (i', diags) := (inlineExpr [] i).run #[]
+ ({ proc with body := .Opaque postconds (some i') modif }, diags)
+ | none => (proc, #[])
+ | _ => (proc, #[])
+
+/-- Inline local variables in every function of an `UnorderedCoreWithLaurelTypes`.
+ Only `functions` are transformed; `coreProcedures` are left unchanged. -/
+def inlineLocalVariablesInFunctions (uc : UnorderedCoreWithLaurelTypes)
+ : UnorderedCoreWithLaurelTypes × List DiagnosticModel :=
+ let results := uc.functions.map inlineLocalVariablesInFunction
+ let functions' := results.map (·.1)
+ let diags := results.flatMap (·.2.toList)
+ ({ uc with functions := functions' }, diags)
+
+public def inlineLocalVariablesPass : LaurelPass UnorderedCoreWithLaurelTypes UnorderedCoreWithLaurelTypes where
+ name := "InlineLocalVariablesPass"
+ documentation := "Inlines local variable declarations of the form `var := ` in function bodies. References to the variable after its declaration are replaced with the initializer expression, and the declaration is removed. Assignments to an inlined variable emit a diagnostic. Operates only on functions, which are pure and cannot carry local variable declarations into Core."
+ comesAfter := [⟨ transparencyPass.meta, "Inlining of local variables in functions only makes sense after the transparency pass has created the functions"⟩]
+ run := fun _ p _ =>
+ let (uc, diags) := inlineLocalVariablesInFunctions p
+ (uc, diags, {})
+
+end -- public section
+
+end Strata.Laurel
diff --git a/Strata/Languages/Laurel/LaurelAST.lean b/Strata/Languages/Laurel/LaurelAST.lean
index fb02d7cd59..4acc132f9b 100644
--- a/Strata/Languages/Laurel/LaurelAST.lean
+++ b/Strata/Languages/Laurel/LaurelAST.lean
@@ -221,8 +221,6 @@ structure Procedure : Type where
preconditions : List Condition
/-- Optional termination measure for recursive procedures. -/
decreases : Option (AstNode StmtExpr) -- optionally prove termination
- /-- If true, the body may only have functional constructs, so no destructive assignments or loops. -/
- isFunctional : Bool
/-- The procedure body: transparent, opaque, or abstract. -/
body : Body
/-- Optional trigger for auto-invocation. When present, the translator also emits an axiom
@@ -315,7 +313,7 @@ inductive StmtExpr : Type where
| While (cond : AstNode StmtExpr) (invariants : List (AstNode StmtExpr))
(decreases : Option (AstNode StmtExpr))
(body : AstNode StmtExpr)
- (postTest : Bool := false)
+ (postTest : Bool)
/-- Exit a labelled block. Models `break` and `continue` statements. -/
| Exit (target : String)
/-- Return from the enclosing procedure with an optional value. -/
@@ -441,21 +439,6 @@ def StmtExpr.constrName : StmtExpr → String
@[expose] abbrev StmtExprMd := AstNode StmtExpr
@[expose] abbrev VariableMd := AstNode Variable
-/-- The label of the implicit block that wraps every procedure body.
-
- `LaurelToCoreTranslator` lowers each procedure body to a single
- `Core.Statement.block bodyLabel …`, and lowers an early `return`
- (or, in the Python frontend, a Python `return`) to `Exit bodyLabel`,
- so that jumping to the end of the body falls through past the block.
- The resolution pass pre-registers this label in scope (via `withLabel`)
- before walking a body, so those `Exit bodyLabel` jumps resolve even
- though the label has no syntactic declaration site.
-
- Shared here so the translator, the resolver, and frontends agree on the
- exact string rather than each hard-coding it. The leading `$` keeps it
- out of the user-name space (no source identifier can contain `$`). -/
-def bodyLabel : String := "$body"
-
theorem AstNode.sizeOf_val_lt {t : Type} [SizeOf t] (e : AstNode t) : sizeOf e.val < sizeOf e := by
cases e; grind
diff --git a/Strata/Languages/Laurel/LaurelCompilationPipeline.lean b/Strata/Languages/Laurel/LaurelCompilationPipeline.lean
index 4e048572c5..124cf62e98 100644
--- a/Strata/Languages/Laurel/LaurelCompilationPipeline.lean
+++ b/Strata/Languages/Laurel/LaurelCompilationPipeline.lean
@@ -21,6 +21,7 @@ import Strata.Languages.Laurel.CoreDefinitionsForLaurel
import Strata.Languages.Laurel.CoreGroupingAndOrdering
import Strata.Languages.Laurel.TransparencyPass
import Strata.Languages.Laurel.LiftImperativeExpressions
+import Strata.Languages.Laurel.InlineLocalVariables
import Strata.Languages.Laurel.ConstrainedTypeElim
import Strata.Languages.Laurel.ContractPass
import Strata.Languages.Laurel.PushOldInward
@@ -172,7 +173,8 @@ private def runLaurelPasses
/-- The ordered sequence of passes on the unordered Core representation. -/
private def unorderedCorePipeline : Array (LaurelPass UnorderedCoreWithLaurelTypes UnorderedCoreWithLaurelTypes) := #[
- liftImperativeExpressionsPass
+ liftImperativeExpressionsPass,
+ inlineLocalVariablesPass
]
/--
@@ -211,9 +213,12 @@ def translateWithLaurel (options : LaurelTranslateOptions) (program : Program)
emit "transparencyPass" "core.st" unorderedCore
let mut unorderedCore := unorderedCore
let mut fnModel := model
+ let mut ucDiags : List DiagnosticModel := []
for pass in unorderedCorePipeline do
- unorderedCore := (pass.run options unorderedCore fnModel).1
+ let (uc, passPassDiags, _) := pass.run options unorderedCore fnModel
+ unorderedCore := uc
+ ucDiags := ucDiags ++ passPassDiags
if pass.needsResolves then
let compositeTypes := program.types.filter (fun t => match t with | .Composite _ => true | _ => false)
let (uc', m', errors) := resolveUnorderedCore unorderedCore (some fnModel) compositeTypes
@@ -222,16 +227,22 @@ def translateWithLaurel (options : LaurelTranslateOptions) (program : Program)
{ d with message :=
s!"Internal error: resolution after '{pass.name}' introduced this diagnostic: {d.message}" }
emit pass.name "unorderedCoreWithLaurelTypes.st" unorderedCore
- return (none, passDiags ++ newDiags, program, stats)
+ return (none, passDiags ++ ucDiags ++ newDiags, program, stats)
unorderedCore := uc'
fnModel := m'
emit pass.name "unorderedCoreWithLaurelTypes.st" unorderedCore
+ -- An error introduced by an unordered-core pass (e.g. an assignment to an
+ -- inlined local) prevents producing a Core program, just like Laurel pass
+ -- errors above.
+ if ucDiags.any (·.type != .Warning) then
+ return (none, passDiags ++ ucDiags, program, stats)
+
let coreWithLaurelTypes := (orderingPass.run options unorderedCore model).1
emit "CoreWithLaurelTypes" "core.st" coreWithLaurelTypes
let (coreProgram, coreDiagnostics, _) := laurelToCoreSchemaPass.run options coreWithLaurelTypes fnModel
- let mut allDiagnostics: List DiagnosticModel := passDiags ++ coreDiagnostics;
+ let mut allDiagnostics: List DiagnosticModel := passDiags ++ ucDiags ++ coreDiagnostics;
emit "Core" "core.st" coreProgram
let coreProgramOption :=
diff --git a/Strata/Languages/Laurel/LaurelPass.lean b/Strata/Languages/Laurel/LaurelPass.lean
index 7a61739a16..58d57049d0 100644
--- a/Strata/Languages/Laurel/LaurelPass.lean
+++ b/Strata/Languages/Laurel/LaurelPass.lean
@@ -21,6 +21,14 @@ structure LaurelTranslateOptions where
this option has no effect. Use with the verifier's `useArrayTheory`. -/
enumeratedModifiesClauses : Bool := false
keepAllFilesPrefix : Option String := none
+ /-- When `true`, calls in procedure bodies to single-output procedures that
+ have a `$asFunction` twin are redirected to that pure function version
+ instead of the procedural twin. This keeps such calls constant-foldable
+ during symbolic evaluation (avoiding the term blowup that occurs when each
+ call produces a fresh symbolic output via the procedural twin).
+ Multi-output procedures are left as procedure calls, since a single
+ function application cannot fill multiple assignment targets. -/
+ alwaysCallCoreFunctions : Bool := true
instance : Inhabited LaurelTranslateOptions where
default := {}
diff --git a/Strata/Languages/Laurel/LaurelToCoreSchemaPass.lean b/Strata/Languages/Laurel/LaurelToCoreSchemaPass.lean
index e5a62a87dd..af415c410b 100644
--- a/Strata/Languages/Laurel/LaurelToCoreSchemaPass.lean
+++ b/Strata/Languages/Laurel/LaurelToCoreSchemaPass.lean
@@ -9,6 +9,7 @@ public import Strata.Languages.Core.Program
public import Strata.Languages.Core.Options
public import Strata.Languages.Laurel.PushOldInward
public import Strata.Languages.Laurel.CoreGroupingAndOrdering
+public import Strata.Languages.Laurel.EliminateReturnStatements
import Strata.Languages.Laurel.Grammar.AbstractToConcreteTreeTranslator
import Strata.Util.Tactics
public import Strata.Languages.Laurel.Resolution
@@ -59,10 +60,15 @@ structure TranslateState where
why the program was deemed invalid so that if no other diagnostics explain
the suppression, these can be surfaced to the user. -/
coreDiagnostics : List DiagnosticModel := []
+ procedureNames: Std.HashSet String
/-- The translation monad: state over Except, allowing both accumulated diagnostics and hard failures -/
@[expose] abbrev TranslateM := OptionT (StateM TranslateState)
+/-- Emit a diagnostic into the translation state (soft warning, does not abort) -/
+def containsProcedure (name : Identifier) : TranslateM Bool := do
+ return (← get).procedureNames.contains name.text
+
/-- Emit a diagnostic into the translation state (soft warning, does not abort) -/
def emitDiagnostic (d : DiagnosticModel) : TranslateM Unit :=
modify fun s => { s with diagnostics := s.diagnostics ++ [d] }
@@ -108,6 +114,18 @@ decreasing_by all_goals (first | (cases elementType; term_by_mem) | (cases keyTy
def lookupType (name : Identifier) : TranslateM LMonoTy := do
translateType ((← get).model.get name).getType
+/-- Compute the Core value type `V` of a `mapConst` argument, i.e. the type of
+ `arg`. Nested `mapConst` calls have the `Box` placeholder as their declared
+ return type, so `computeExprType` cannot recover their structural `Map` type;
+ we reconstruct it here (`mapConst(x) : Map TypeTag (typeof x)`). -/
+private partial def mapConstValTy (model : SemanticModel) (arg : StmtExprMd) : TranslateM LMonoTy := do
+ match arg.val with
+ | .StaticCall callee [inner] =>
+ if callee.text == "mapConst" then
+ return Core.mapTy (.tcons "TypeTag" []) (← mapConstValTy model inner)
+ else translateType (computeExprType model arg)
+ | _ => translateType (computeExprType model arg)
+
/-- Run a `TranslateM` action, returning either a hard error or the result and final state -/
def runTranslateM (s : TranslateState) (m : TranslateM α) : (Option α × TranslateState) :=
m s
@@ -225,10 +243,25 @@ def translateExpr (expr : StmtExprMd)
return .ite () bcond bthen belse
| .StaticCall callee args =>
-- In a pure context, only Core functions (not procedures) are allowed
- if isPureContext && !model.isFunction callee then
+ if isPureContext && (← containsProcedure callee) then
disallowed expr.source s!"calls to procedures are not supported in functions or contracts"
else
- let fnOp : Core.Expression.Expr := .op () ⟨callee.text, ()⟩ none
+ -- The `mapConst` constant-map builtin has no inferable key type, so we
+ -- annotate its op with the concrete function type `V → Map K V` (from
+ -- the resolved result type). This lets the pretty-printer emit the
+ -- explicit `mapConst(v)` syntax so the program round-trips.
+ let fnOp : Core.Expression.Expr ←
+ if callee.text == "mapConst" then
+ -- `mapConst : V → Map TypeTag V`. Key type is always `TypeTag`
+ -- (the type-tag domain of the ancestor tables); the value type is
+ -- the type of the single argument.
+ match args with
+ | [valArg] =>
+ let vTy ← mapConstValTy model valArg
+ let kTy : LMonoTy := .tcons "TypeTag" []
+ pure (.op () ⟨callee.text, ()⟩ (some (LMonoTy.mkArrow vTy [Core.mapTy kTy vTy])))
+ | _ => pure (.op () ⟨callee.text, ()⟩ none)
+ else pure (.op () ⟨callee.text, ()⟩ none)
args.attach.foldlM (fun acc ⟨arg, _⟩ => do
let re ← translateExpr arg boundVars isPureContext
return .app () acc re) fnOp
@@ -259,7 +292,7 @@ def translateExpr (expr : StmtExprMd)
throwExprDiagnostic $ diagnosticFromSource expr.source
"IncrDecr should have been eliminated by EliminateIncrDecr pass" DiagnosticType.StrataBug
| .While _ _ _ _ _ =>
- disallowed expr.source "loops are not supported in functions or contracts"
+ disallowed expr.source "loops are not supported in transparent bodies or contracts"
| .Exit _ => disallowed expr.source "exit is not supported in expression position"
| .Block (⟨ .Assert _, innerSrc⟩ :: rest) label => do
@@ -272,9 +305,11 @@ def translateExpr (expr : StmtExprMd)
let valueExpr ← translateExpr initializer boundVars isPureContext
let bodyExpr ← translateExpr { val := StmtExpr.Block rest label, source := innerSrc } (name :: boundVars) isPureContext
let coreMonoType ← translateType ty
- return .app () (.abs () name.text (some coreMonoType) bodyExpr) valueExpr
+ disallowed innerSrc "local variables in transparent bodies are not YET supported"
+ -- This doesn't work because of a limitation in Core.
+ -- return .app () (.abs () (some coreMonoType) bodyExpr) valueExpr
| .Block (⟨ .Var (.Declare _), innerSrc⟩ :: rest) label => do
- _ ← disallowed innerSrc "local variables in functions must have initializers"
+ _ ← disallowed innerSrc "local variables must have initializers in transparent bodies or contracts "
translateExpr { val := StmtExpr.Block rest label, source := innerSrc } boundVars isPureContext
| .Block (⟨ .IfThenElse cond thenBranch (some elseBranch), innerSrc⟩ :: rest) label =>
disallowed innerSrc "if-then-else only supported as the last statement in a block"
@@ -419,8 +454,6 @@ Diagnostics are emitted into the monad state.
-/
def translateStmt (stmt : StmtExprMd)
: TranslateM (List Core.Statement) := do
- let s ← get
- let model := s.model
let md := astNodeToCoreMd stmt
match _h : stmt.val with
| .Assert cond =>
@@ -493,7 +526,9 @@ def translateStmt (stmt : StmtExprMd)
-- Match on the value to decide how to translate
match _hv : value.val with
| .StaticCall callee args =>
- if model.isFunction callee then
+ if (← containsProcedure callee) then
+ translateCallTargets callee args
+ else
-- Function call: translate as a normal expression assignment
let coreExpr ← translateExpr value
match targets with
@@ -504,8 +539,6 @@ def translateStmt (stmt : StmtExprMd)
return result
| _ =>
throwStmtDiagnostic $ md.toDiagnostic "function call without a single target" DiagnosticType.StrataBug
- else
- translateCallTargets callee args
| .InstanceCall _target callee args =>
translateCallTargets callee args
| .Hole _ _ =>
@@ -531,7 +564,7 @@ def translateStmt (stmt : StmtExprMd)
return [Imperative.Stmt.ite (.det bcond) bthen belse md]
| .StaticCall callee args =>
-- Check if this is a function or procedure
- if model.isFunction callee then
+ if !(← containsProcedure callee) then
-- Function call in statement position: preserve as unused init
exprAsUnusedInit stmt md
else
@@ -551,14 +584,10 @@ def translateStmt (stmt : StmtExprMd)
| .InstanceCall .. =>
-- Instance method call as statement: no return value, treated as no-op
return ([])
- | .Return valueOpt =>
- match valueOpt with
- | none =>
- return [.exit bodyLabel md]
- | some _ =>
- let d := md.toDiagnostic "Return statement with value should have been eliminated by EliminateValueReturns pass" DiagnosticType.StrataBug
- emitCoreDiagnostic d
- return [.exit bodyLabel md]
+ | .Return _ =>
+ let d := md.toDiagnostic "Return statement should have been eliminated by EliminateReturnStatements pass" DiagnosticType.StrataBug
+ emitCoreDiagnostic d
+ return default
| .While cond invariants decreasesExpr body postTest =>
if postTest then
return ← throwStmtDiagnostic (diagnosticFromSource cond.source
@@ -658,9 +687,19 @@ def translateProcedure (proc : Procedure) : TranslateM Core.Procedure := do
translateChecks postconds s!"postcondition" bodyStmts.isNone
(defaultSummary := "postcondition")
| _ => pure []
- -- Wrap body in a labeled block so early returns (exit) work correctly.
- -- `bodyLabel` is the shared "$body" constant the resolver pre-registers.
- let body : List Core.Statement := [.block bodyLabel (bodyStmts.getD []) mdWithUnknownLoc]
+ let body : List Core.Statement :=
+ match bodyStmts with
+ | some ss => ss
+ | none =>
+ -- A bodiless procedure (e.g. a generated `$hole`, or any opaque/abstract
+ -- declaration) would otherwise produce an empty structured body, which the
+ -- Core interpreter rejects when called ("has no body"). Emit a single
+ -- `assume true` so the body is non-empty. This is a no-op for both
+ -- verification (such a procedure's postconditions are already marked
+ -- `free`, so there is nothing to check against the body) and concrete
+ -- execution (the outputs stay havoc'd), but it lets the interpreter step
+ -- through the call instead of erroring.
+ [Core.Statement.assume "assume_true" (.true ()) Imperative.MetaData.empty]
let spec : Core.Procedure.Spec := { preconditions, postconditions }
return { header, spec, body := .structured body }
@@ -672,10 +711,15 @@ instance : Inhabited LaurelVerifyOptions where
default := {}
/-- Unwrap the pattern produced by EliminateValuesInReturns + EliminateReturnStatements:
- `{ result := ; exit "$return" } $return` → `` -/
+ `{ result := ; exit "$return" } $return` → ``
+ Also handles an extra wrapping layer from the contract pass:
+ `{ { result := ; exit "$return" } $return } none` → ``
+ Support for transparent multi-out procedures is not yet available.
+-/
private def unwrapReturnBlock (b : StmtExprMd) : StmtExprMd :=
match b.val with
- | .Block [⟨.Assign [⟨.Local _, _⟩] value, _⟩, ⟨.Exit "$return", _⟩] (some "$return") => value
+ | .Block [⟨.Assign [⟨.Local _, _⟩] value, _⟩, ⟨.Exit returnLabel, _⟩] (some returnLabel) => value
+ | .Block [⟨.Block [⟨.Assign [⟨.Local _, _⟩] value, _⟩, ⟨.Exit returnLabel, _⟩] (some returnLabel), _⟩] _ => value
| _ => b
/--
@@ -807,7 +851,13 @@ public def laurelToCoreSchemaPass : LaurelPass CoreWithLaurelTypes Core.Program
- Laurel parameter definitions are translated to Core ones.
- Laurel calling conventions are translated to Core ones."
run := fun options p fnModel =>
- let initState : TranslateState := { model := fnModel, overflowChecks := options.overflowChecks }
+ let initState : TranslateState := {
+ model := fnModel,
+ overflowChecks := options.overflowChecks,
+ procedureNames := p.decls.foldl (fun r d => match d with
+ | .procedure p => r.insert p.name.text
+ | _ => r ) (Std.HashSet.emptyWithCapacity 0)
+ }
let (coreProgramOption, translateState) :=
runTranslateM initState (translateLaurelToCore options p)
let diagnostics : List DiagnosticModel :=
diff --git a/Strata/Languages/Laurel/LiftImperativeExpressions.lean b/Strata/Languages/Laurel/LiftImperativeExpressions.lean
index d47560585d..78ccc08988 100644
--- a/Strata/Languages/Laurel/LiftImperativeExpressions.lean
+++ b/Strata/Languages/Laurel/LiftImperativeExpressions.lean
@@ -198,10 +198,18 @@ def containsAssignmentOrImperativeCall (imperativeCallees : List String) (expr :
mutual
def asLifted { t: Type } (runner: LiftM t) : LiftM t := do
- let savedState ← get
+ -- Save only the bookkeeping that `runner` is meant to run in a fresh
+ -- sub-scope (`prependedStmts` and `subst`). We must NOT restore the whole
+ -- state: the monotonic counters (`condCounter`, `varCounters`) advanced by
+ -- `runner` reflect fresh names (e.g. `$cndtn_N`) that escape into the output
+ -- via the returned/prepended statements. Rolling those counters back would
+ -- let a later `freshTempVar`/`freshTempFor` reuse the same name, producing a
+ -- duplicate definition in the same scope.
+ let savedPrepends := (← get).prependedStmts
+ let savedSubst := (← get).subst
modify fun s => { s with prependedStmts := [], subst := []}
let result ← runner
- modify fun _ => savedState
+ modify fun s => { s with prependedStmts := savedPrepends, subst := savedSubst }
return result
/--
@@ -422,14 +430,14 @@ def transformExpr (expr : StmtExprMd) : LiftM StmtExprMd := do
let seqRet ← transformExpr retExpr
return ⟨.Return (some seqRet), source⟩
- | .While cond invs dec body =>
+ | .While cond invs dec body postTest =>
let seqCond ← transformExpr cond
let seqInvs ← invs.mapM transformExpr
let seqDec ← match dec with
| some d => pure (some (← transformExpr d))
| none => pure none
let seqBody ← transformExpr body
- return ⟨.While seqCond seqInvs seqDec seqBody, source⟩
+ return ⟨.While seqCond seqInvs seqDec seqBody postTest, source⟩
| .PureFieldUpdate target fieldName newValue =>
let seqTarget ← transformExpr target
@@ -596,21 +604,11 @@ def transformStmt (stmt : StmtExprMd) : LiftM (List StmtExprMd) := do
let prepends ← takePrepends
return prepends ++ [⟨.StaticCall name seqArgs, source⟩]
- | .PrimitiveOp _ args =>
- -- A `PrimitiveOp` in statement position. If it carries any side effects
- -- (an embedded assignment or imperative call — typically the result of
- -- the postfix increment lowering `(x := x + 1) - 1`), lift them out and
- -- discard the unused pure result. Otherwise leave the expression
- -- statement intact so the Core translator can preserve it via
- -- `exprAsUnusedInit`.
- let imperativeCallees := (← get).imperativeCallees
- if containsAssignmentOrImperativeCall imperativeCallees stmt then
- let _ ← args.reverse.mapM transformExpr
- let prepends ← takePrepends
- modify fun s => { s with subst := [] }
- return prepends
- else
- return [stmt]
+ | .PrimitiveOp op args _ =>
+ let seqArgs ← args.reverse.mapM transformExpr
+ let prepends ← takePrepends
+ modify fun s => { s with subst := [] }
+ return prepends ++ [⟨.PrimitiveOp op seqArgs.reverse, source⟩]
| .Return (some retExpr) =>
let seqRet ← transformExpr retExpr
@@ -618,10 +616,6 @@ def transformStmt (stmt : StmtExprMd) : LiftM (List StmtExprMd) := do
modify fun s => { s with subst := [] }
return prepends ++ [⟨.Return (some seqRet), source⟩]
- | .PrimitiveOp name args _ =>
- let seqArgs ← args.mapM transformExpr
- let prepends ← takePrepends
- return prepends ++ [⟨.PrimitiveOp name seqArgs, source⟩]
| _ =>
return [stmt]
termination_by (sizeOf stmt, 0)
diff --git a/Strata/Languages/Laurel/MergeAndLiftReturns.lean b/Strata/Languages/Laurel/MergeAndLiftReturns.lean
index 28a40b215a..f2d78a4f1e 100644
--- a/Strata/Languages/Laurel/MergeAndLiftReturns.lean
+++ b/Strata/Languages/Laurel/MergeAndLiftReturns.lean
@@ -43,6 +43,8 @@ def removeReturns (stmt : StmtExprMd) : Except DiagnosticModel StmtExprMd :=
| .Assert _ => .ok passThrough
| .Block _ _ => .ok passThrough
| .IfThenElse _ _ (some _) => .error (diagnosticFromSource head.source "in a transparent body, if-then-else is only supported as the last statement in a block")
+ | .While _ _ _ _ _ => .error $ diagnosticFromSource head.source $ "loops are not supported in transparent bodies or contracts"
+ | .Var (.Declare _) => .error $ diagnosticFromSource head.source $ "local variables must have initializers in transparent bodies or contracts"
| _ => .error (diagnosticFromSource head.source
s!"unsupported statement {head.val.constructorName} in block head")
| .IfThenElse cond thenBr (some elseBr) => do
diff --git a/Strata/Languages/Laurel/ModifiesClauses.lean b/Strata/Languages/Laurel/ModifiesClauses.lean
index 8b4817224f..c3c888a210 100644
--- a/Strata/Languages/Laurel/ModifiesClauses.lean
+++ b/Strata/Languages/Laurel/ModifiesClauses.lean
@@ -130,7 +130,6 @@ def insertFrameChecks (proc : Procedure) (frame : StmtExprMd) (body : StmtExprMd
let beforeExits := mapStmtExpr (fun e =>
match e.val with
| .Return _ => wrap e
- | .Exit l => if l == bodyLabel then wrap e else e
| _ => e) body
{ val := .Block [beforeExits, check] none, source := src }
diff --git a/Strata/Languages/Laurel/Resolution.lean b/Strata/Languages/Laurel/Resolution.lean
index a3e3e5090d..ba707e1dc2 100644
--- a/Strata/Languages/Laurel/Resolution.lean
+++ b/Strata/Languages/Laurel/Resolution.lean
@@ -1645,8 +1645,8 @@ def Synth.staticCall (exprMd : StmtExprMd)
-- Core translation:
-- * `select(map, key)` ⇒ the map's value type
-- * `update(map, key, val)` ⇒ the map type itself
- -- * `const(val)` ⇒ `Map _ (typeof val)` (key type is not recoverable)
- if callee == "select" || callee == "update" || callee == "const" then
+ -- * `mapConst(val)` ⇒ `Map _ (typeof val)` (key type is not recoverable)
+ if callee == "select" || callee == "update" || callee == "mapConst" then
let resolved ← args.attach.mapM (fun ⟨a, hMem⟩ => do
have := hMem
Synth.resolveStmtExpr a)
@@ -1659,7 +1659,7 @@ def Synth.staticCall (exprMd : StmtExprMd)
| .TMap _ valueTy => pure valueTy
| _ => pure ⟨ .Unknown, source ⟩
| "update", mapTy :: _ => pure mapTy
- | "const", valTy :: _ => pure ⟨ .TMap ⟨.UserDefined "TypeTag", source⟩ valTy, source ⟩
+ | "mapConst", valTy :: _ => pure ⟨ .TMap ⟨.UserDefined "TypeTag", source⟩ valTy, source ⟩
| _, _ => pure ⟨ .Unknown, source ⟩
return (.StaticCall callee args', resultTy)
@@ -2620,11 +2620,7 @@ def resolveProcedure (proc : Procedure) : ResolveM Procedure := do
let dec' ← proc.decreases.mapM resolveStmtExpr
let savedAnswer := (← get).answerType
modify fun s => { s with answerType := some (outputs'.map (·.type)) }
- -- Pre-register the implicit `bodyLabel` block that the LaurelToCore
- -- translator wraps every body in (`Core.Statement.block bodyLabel …`),
- -- so that frontends emitting `Exit bodyLabel` for early-return lowering
- -- (e.g. PythonToLaurel) don't trip Check.exit's label-scope check.
- let body' ← withLabel (some bodyLabel) <| resolveBody proc.body
+ let body' ← resolveBody proc.body
modify fun s => { s with answerType := savedAnswer }
-- Transparent (static) procedure bodies are supported (#1215): the
-- TransparencyPass derives a functional `$asFunction` copy, and the
@@ -2634,7 +2630,6 @@ def resolveProcedure (proc : Procedure) : ResolveM Procedure := do
let invokeOn' ← proc.invokeOn.mapM resolveStmtExpr
let axioms' ← proc.axioms.mapM resolveStmtExpr
return { name := procName', inputs := inputs', outputs := outputs',
- isFunctional := proc.isFunctional,
preconditions := pres', decreases := dec',
invokeOn := invokeOn',
axioms := axioms',
@@ -2666,14 +2661,12 @@ def resolveInstanceProcedure (typeName : Identifier) (proc : Procedure) : Resolv
let dec' ← proc.decreases.mapM resolveStmtExpr
let savedAnswer := (← get).answerType
modify fun s => { s with answerType := some (outputs'.map (·.type)) }
- -- See `resolveProcedure` for the rationale on `bodyLabel`.
- let body' ← withLabel (some bodyLabel) <| resolveBody proc.body
+ let body' ← resolveBody proc.body
modify fun s => { s with answerType := savedAnswer }
let invokeOn' ← proc.invokeOn.mapM resolveStmtExpr
modify fun s => { s with instanceTypeName := savedInstType }
let axioms' ← proc.axioms.mapM resolveStmtExpr
return { name := procName', inputs := inputs', outputs := outputs',
- isFunctional := proc.isFunctional,
preconditions := pres', decreases := dec',
invokeOn := invokeOn',
axioms := axioms',
@@ -2770,7 +2763,6 @@ private def mkTesterProcedure (dt : DatatypeDefinition) (ctor : DatatypeConstruc
outputs := [outputParam]
preconditions := []
decreases := none
- isFunctional := true
body := .External }
/-- Insert a definition into the refToDef map using the ID already on the identifier. -/
@@ -3144,32 +3136,6 @@ public def resolve (program : Program) (existingModel: Option SemanticModel := n
/-! ## Resolution for UnorderedCoreWithLaurelTypes -/
-/--
-Convert an `UnorderedCoreWithLaurelTypes` to a flat `Program` suitable for
-resolution. Additional type definitions (e.g. composite types from the original
-Laurel program) can be supplied so that `UserDefined` type references resolve
-correctly.
--/
-private def unorderedCoreToProgram (uc : UnorderedCoreWithLaurelTypes)
- (additionalTypes : List TypeDefinition := []) : Program :=
- { staticProcedures := uc.functions ++ uc.coreProcedures,
- staticFields := [],
- types := uc.datatypes.map TypeDefinition.Datatype ++ additionalTypes,
- constants := uc.constants }
-
-/--
-Reconstruct an `UnorderedCoreWithLaurelTypes` from a resolved `Program`.
--/
-private def fromResolvedProgram (resolvedProgram : Program)
- : UnorderedCoreWithLaurelTypes :=
- let resolvedProcs := resolvedProgram.staticProcedures
- let resolvedDatatypes := resolvedProgram.types.filterMap fun td =>
- match td with | .Datatype dt => some dt | _ => none
- { functions := resolvedProcs.filter (·.isFunctional)
- coreProcedures := resolvedProcs.filter (!·.isFunctional)
- datatypes := resolvedDatatypes
- constants := resolvedProgram.constants }
-
/--
Resolve an `UnorderedCoreWithLaurelTypes` by converting to a flat `Program`,
running the resolution pass, and reconstructing the result. Returns the
@@ -3184,9 +3150,107 @@ public def resolveUnorderedCore (uc : UnorderedCoreWithLaurelTypes)
(existingModel : Option SemanticModel := none)
(additionalTypes : List TypeDefinition := [])
: UnorderedCoreWithLaurelTypes × SemanticModel × Array DiagnosticModel :=
- let fnProgram := unorderedCoreToProgram uc additionalTypes
- let fnResolveResult := resolve fnProgram existingModel
- (fromResolvedProgram fnResolveResult.program, fnResolveResult.model, fnResolveResult.errors)
+ -- Phase 1: pre-register all top-level names, then resolve references
+ let phase1 : ResolveM UnorderedCoreWithLaurelTypes := do
+ -- Pre-register additional types (e.g. composite types from the original Laurel program)
+ for td in additionalTypes do
+ match td with
+ | .Composite ct =>
+ let _ ← defineNameCheckDup ct.name (.compositeType ct)
+ for field in ct.fields do
+ let qualifiedName := ct.name.text ++ "." ++ field.name.text
+ let _ ← defineNameCheckDup field.name (.field ct.name field) (some qualifiedName)
+ for proc in ct.instanceProcedures do
+ let _ ← defineNameCheckDup proc.name (.instanceProcedure ct.name proc)
+ | .Constrained ct =>
+ let _ ← defineNameCheckDup ct.name (.constrainedType ct)
+ | .Datatype dt =>
+ let _ ← defineNameCheckDup dt.name (.datatypeDefinition dt)
+ for ctor in dt.constructors do
+ let _ ← defineNameCheckDup ctor.name (.datatypeConstructor dt.name ctor)
+ let testerProc := mkTesterProcedure dt ctor
+ let _ ← defineNameCheckDup (mkId (dt.testerName ctor))
+ (.staticProcedure testerProc) (some (dt.testerName ctor))
+ for p in ctor.args do
+ let pName ← defineNameCheckDup p.name (.datatypeDestructor dt.name p) (some (dt.destructorName p))
+ let _ ← defineNameCheckDup pName (.datatypeDestructor dt.name p) (some (dt.unsafeDestructorName p))
+ | .Alias ta =>
+ let _ ← defineNameCheckDup ta.name (.typeAlias ta)
+
+ -- Pre-register datatypes from the unordered core
+ for dt in uc.datatypes do
+ let _ ← defineNameCheckDup dt.name (.datatypeDefinition dt)
+ for ctor in dt.constructors do
+ let _ ← defineNameCheckDup ctor.name (.datatypeConstructor dt.name ctor)
+ let testerProc := mkTesterProcedure dt ctor
+ let _ ← defineNameCheckDup (mkId (dt.testerName ctor))
+ (.staticProcedure testerProc) (some (dt.testerName ctor))
+ for p in ctor.args do
+ let pName ← defineNameCheckDup p.name (.datatypeDestructor dt.name p) (some (dt.destructorName p))
+ let _ ← defineNameCheckDup pName (.datatypeDestructor dt.name p) (some (dt.unsafeDestructorName p))
+
+ -- Pre-register constants
+ for c in uc.constants do
+ let _ ← defineNameCheckDup c.name (.constant c)
+
+ -- Pre-register functions and core procedures
+ for proc in uc.functions do
+ let _ ← defineNameCheckDup proc.name (.staticProcedure proc)
+ for proc in uc.coreProcedures do
+ let _ ← defineNameCheckDup proc.name (.staticProcedure proc)
+
+ -- Build type scopes for additional composite types (for field resolution)
+ for td in additionalTypes do
+ if let .Composite ct := td then
+ let s ← get
+ let mut typeScope : Scope := {}
+ for parent in ct.extending do
+ match s.typeScopes.get? parent.text with
+ | some parentScope =>
+ for (k, v) in parentScope do
+ typeScope := typeScope.insert k v
+ | none => pure ()
+ for field in ct.fields do
+ let qualifiedKey := ct.name.text ++ "." ++ field.name.text
+ match s.scope.get? qualifiedKey with
+ | some entry => typeScope := typeScope.insert field.name.text entry
+ | none => pure ()
+ modify fun s => { s with typeScopes := s.typeScopes.insert ct.name.text typeScope }
+
+ -- Resolve datatypes
+ let datatypes' ← uc.datatypes.mapM fun dt => do
+ match ← resolveTypeDefinition (.Datatype dt) with
+ | .Datatype dt' => pure dt'
+ | _ => pure dt -- unreachable
+
+ -- Resolve constants
+ let constants' ← uc.constants.mapM resolveConstant
+
+ -- Resolve functions and core procedures
+ let functions' ← uc.functions.mapM resolveProcedure
+ let coreProcedures' ← uc.coreProcedures.mapM resolveProcedure
+
+ return { functions := functions', coreProcedures := coreProcedures',
+ datatypes := datatypes', constants := constants' }
+
+ let nextId := existingModel.elim 1 (fun m => m.nextId)
+ let (uc', finalState) := phase1.run { nextId := nextId }
+
+ -- Phase 2: build refToDef from the resolved unordered core
+ let program' : Program := {
+ staticProcedures := uc'.functions ++ uc'.coreProcedures,
+ staticFields := [],
+ types := uc'.datatypes.map .Datatype ++ additionalTypes,
+ constants := uc'.constants
+ }
+ let refToDef := buildRefToDef program'
+
+ let model : SemanticModel := {
+ compositeCount := additionalTypes.length,
+ refToDef := refToDef,
+ nextId := finalState.nextId
+ }
+ (uc', model, finalState.errors)
end -- public section
end Strata.Laurel
diff --git a/Strata/Languages/Laurel/SemanticModel.lean b/Strata/Languages/Laurel/SemanticModel.lean
index c41e15c5dd..78b69a3e25 100644
--- a/Strata/Languages/Laurel/SemanticModel.lean
+++ b/Strata/Languages/Laurel/SemanticModel.lean
@@ -129,18 +129,6 @@ def SemanticModel.get? (model: SemanticModel) (iden: Identifier): Option Resolve
def SemanticModel.get (model: SemanticModel) (iden: Identifier): ResolvedNode :=
(model.get? iden).getD default
-def SemanticModel.isFunction (model: SemanticModel) (id: Identifier): Bool :=
- match model.get id with
- | .staticProcedure proc => proc.isFunctional
- | .parameter _ => true
- | .datatypeConstructor _ _ => true
- | .datatypeDestructor _ _ => true
- | .constant _ => true
- | .unresolved _ => true -- functions calls are more permissive, so true avoids possibly incorrect errors
- | node =>
- dbg_trace s!"Sound but incomplete BUG! id: {repr id}, is not a procedure, but a node {repr node}"
- false
-
/--
Compute the flattened set of ancestors for a composite type, including itself.
Traverses the `extending` list transitively.
diff --git a/Strata/Languages/Laurel/TransparencyPass.lean b/Strata/Languages/Laurel/TransparencyPass.lean
index 9ed040c98a..e846e24eb0 100644
--- a/Strata/Languages/Laurel/TransparencyPass.lean
+++ b/Strata/Languages/Laurel/TransparencyPass.lean
@@ -74,6 +74,45 @@ private def rewriteCallsToFunctional (asFunctionNames : Std.HashSet String) (exp
| .PrimitiveOp operator arguments _ => ⟨ .PrimitiveOp operator arguments true, e.source⟩
| _ => e) expr
+/-- Narrowly redirect `StaticCall` callees whose names are in `redirectNames`
+ to their `$asFunction` versions, leaving everything else (selectors,
+ primitive ops, non-redirected calls) untouched. Unlike
+ `rewriteCallsToFunctional`, this does not adjust selector names or mark
+ primitive ops as proof terms, so it is safe to apply to imperative
+ procedure bodies.
+
+ The callee's `uniqueId` is preserved: it still resolves (via the semantic
+ model) to the base procedure, whose output type matches the `$asFunction`'s
+ return type, so `computeExprType`/`getCallType` continue to type the call
+ correctly. The renamed callee text (`X$asFunction`) is not in
+ `procedureNames`, so the Laurel→Core translator lowers it as a pure function
+ application rather than a procedure call. -/
+private def redirectCallsToFunctional (redirectNames : Std.HashSet String) (expr : StmtExprMd) : StmtExprMd :=
+ mapStmtExpr (fun e =>
+ match e.val with
+ | .StaticCall callee args =>
+ if redirectNames.contains callee.text then
+ let funcCallee := { callee with text := callee.text ++ "$asFunction" }
+ ⟨.StaticCall funcCallee args, e.source⟩
+ else e
+ | _ => e) expr
+
+/-- Apply `redirectCallsToFunctional` to a procedure's implementation and
+ postcondition expressions. Used when `alwaysCallAsFunction` is set so that
+ calls to transparent, single-output procedures become pure function
+ applications at their call sites. -/
+private def redirectCallsInProc (redirectNames : Std.HashSet String) (proc : Procedure) : Procedure :=
+ let r := redirectCallsToFunctional redirectNames
+ match proc.body with
+ | .Opaque postconds impl modif =>
+ { proc with body := .Opaque (postconds.map fun c => { c with condition := r c.condition })
+ (impl.map r) modif }
+ | .Transparent body =>
+ { proc with body := .Transparent (r body) }
+ | .Abstract postconds =>
+ { proc with body := .Abstract (postconds.map fun c => { c with condition := r c.condition }) }
+ | .External => proc
+
/-- Rewrite quantifier bodies like function bodies: strip assert/assume and
rewrite calls to their `$asFunction` variants. This ensures that calls
inside quantifiers (e.g. in modifies frame conditions) reference the
@@ -127,7 +166,7 @@ private def mkFunctionCopy (asFunctionNames : Std.HashSet String) (proc : Proced
| .Transparent b => .Transparent (rewriteCallsToFunctional asFunctionNames (if hasProcedureTwin then stripAssertAssume b else b))
| .Opaque _ _ _ => if hasProcedureTwin then .Opaque [] none [] else proc.body
| x => x
- { proc with name := funcName, isFunctional := true, body := body }
+ { proc with name := funcName, body := body }
/-- Append a free postcondition to a procedure's body postconditions.
For Opaque and Abstract bodies, the free condition is appended to the
@@ -148,20 +187,66 @@ private def addFreePostcondition (proc : Procedure) (freePost : StmtExprMd) : Pr
{ proc with body := .Opaque [freeCond] (some body) [] }
| _ => proc
-def createFunctionsForTransparentBodies (program : Program) : UnorderedCoreWithLaurelTypes :=
- let (toUpdate, _) := program.staticProcedures.partition (fun p => !p.body.isExternal && !p.isFunctional)
- let toUpdateNames : Std.HashSet String := toUpdate.foldl (fun s p => s.insert p.name.text) {}
- -- $asFunction copies for non-external procedures
+/--
+Transparency pass: translate a Laurel program to the UnorderedCoreWithLaurelTypes IR.
+
+For each procedure:
+- Generate a function with the same signature, named `foo$asFunction`
+- If transparent, the function gets a functional body (assertions erased, calls to functional versions)
+- If the function has a body, add a free postcondition equating the procedure output to the function
+-/
+def createFunctionsForTransparentBodies (program : Program) (options : LaurelTranslateOptions := {}) : UnorderedCoreWithLaurelTypes :=
+ let (toUpdate, _) := program.staticProcedures.partition (fun p => !p.body.isExternal)
+ -- A transparent procedure whose body is purely functional (no Assume/Assert
+ -- from contract instrumentation) needs only a function copy, not a procedural
+ -- twin. This matches the old `isFunctional` behavior for condition helpers.
+ let needsProcTwin (p : Procedure) : Bool := match p.body with
+ | .Transparent b => blockContainsAssumeOrAssert b
+ | _ => true
+ let (imperativeProcs, _) := toUpdate.partition needsProcTwin
+ let toUpdateNames : Std.HashSet String := imperativeProcs.foldl (fun s p => s.insert p.name.text) {}
+ -- Names of single-output procedures whose calls can be redirected to their
+ -- `$asFunction` version: `mkFreePostcondition` only equates a single output
+ -- to the function, and a single function application can only fill one
+ -- assignment target. Multi-output procedures are excluded.
+ let singleOutputNames : Std.HashSet String :=
+ imperativeProcs.foldl (fun s p =>
+ if p.outputs.length == 1 && p.body.isTransparent then s.insert p.name.text else s) {}
+ -- $asFunction copies for procedures that have a procedural twin;
+ -- transparent-only procedures keep their original name.
let functions := program.staticProcedures.map (mkFunctionCopy toUpdateNames)
- let coreProcedures := toUpdate.map fun proc =>
+ let coreProcedures := imperativeProcs.map fun proc =>
let freePostcondition := mkFreePostcondition proc
- let proc := { proc with isFunctional := false, axioms := proc.axioms.map (rewriteCallsToFunctional toUpdateNames) }
+ let proc := { proc with axioms := proc.axioms.map (rewriteCallsToFunctional toUpdateNames) }
let proc := rewriteQuantifierBodiesInProc toUpdateNames proc
+ -- When requested, redirect every call to a single-output twinned procedure
+ -- to its `$asFunction` version so calls stay constant-foldable during
+ -- symbolic evaluation (instead of producing fresh symbolic outputs via the
+ -- procedural twin).
+ let proc := if options.alwaysCallCoreFunctions then redirectCallsInProc singleOutputNames proc else proc
addFreePostcondition proc freePostcondition
let datatypes := program.types.filterMap fun td => match td with
| .Datatype dt => some dt
| _ => none
{ functions, coreProcedures, datatypes, constants := program.constants }
+where
+ /-- Check if an expression tree contains Assume or Assert statements anywhere.
+ The contract pass inserts these for procedures with contracts. -/
+ blockContainsAssumeOrAssert (e : StmtExprMd) : Bool :=
+ match e with
+ | AstNode.mk val _ =>
+ match val with
+ | .Assume _ | .Assert _ => true
+ | .Block stmts _ => stmts.attach.any fun ⟨s, _⟩ => blockContainsAssumeOrAssert s
+ | .IfThenElse c t f =>
+ blockContainsAssumeOrAssert c || blockContainsAssumeOrAssert t ||
+ match f with | some fe => blockContainsAssumeOrAssert fe | none => false
+ | .StaticCall _ args => args.attach.any fun ⟨a, _⟩ => blockContainsAssumeOrAssert a
+ | .Assign _ v => blockContainsAssumeOrAssert v
+ | .While c _ _ b _ => blockContainsAssumeOrAssert c || blockContainsAssumeOrAssert b
+ | .Return v => match v with | some ve => blockContainsAssumeOrAssert ve | none => false
+ | .PrimitiveOp _ args _ => args.attach.any fun ⟨a, _⟩ => blockContainsAssumeOrAssert a
+ | _ => false
public def transparencyPass : LaurelPass Laurel.Program UnorderedCoreWithLaurelTypes where
name := "TransparencyPass"
@@ -171,8 +256,8 @@ For each procedure:
- Generate a function with the same signature, named `foo$asFunction`
- If transparent, the function gets a functional body (assertions erased, calls to functional versions)
- If the function has a body, add a free postcondition equating the procedure output to the function"
- run := fun _ p _ =>
- (createFunctionsForTransparentBodies p, [], {})
+ run := fun opts p _ =>
+ (createFunctionsForTransparentBodies p opts, [], {})
end -- public section
end Strata.Laurel
diff --git a/Strata/Languages/Laurel/TypeHierarchy.lean b/Strata/Languages/Laurel/TypeHierarchy.lean
index 079eb38be0..71c5f9b8a1 100644
--- a/Strata/Languages/Laurel/TypeHierarchy.lean
+++ b/Strata/Languages/Laurel/TypeHierarchy.lean
@@ -43,11 +43,11 @@ def generateTypeHierarchyDecls (model : SemanticModel) (program: Program) : List
let innerMapTy : HighTypeMd := ⟨.TMap typeTagTy boolTy, none⟩
let outerMapTy : HighTypeMd := ⟨.TMap typeTagTy innerMapTy, none⟩
-- Helper: build an inner map (Map TypeTag bool) for a given composite type
- -- Start with const(false), then update each composite type's entry
+ -- Start with mapConst(false), then update each composite type's entry
let mkInnerMap (ct : CompositeType) : StmtExprMd :=
let ancestors := computeAncestors model ct.name
let falseConst := mkMd (.LiteralBool false)
- let emptyInner := mkMd (.StaticCall "const" [falseConst])
+ let emptyInner := mkMd (.StaticCall "mapConst" [falseConst])
composites.foldl (fun acc otherCt =>
let isAncestor := ancestors.any (·.name == otherCt.name)
if isAncestor then
@@ -63,8 +63,8 @@ def generateTypeHierarchyDecls (model : SemanticModel) (program: Program) : List
initializer := some (mkInnerMap ct) : Constant }
-- Build ancestorsPerType by referencing the individual ancestorsFor constants
let falseConst := mkMd (.LiteralBool false)
- let emptyInner := mkMd (.StaticCall "const" [falseConst])
- let emptyOuter := mkMd (.StaticCall "const" [emptyInner])
+ let emptyInner := mkMd (.StaticCall "mapConst" [falseConst])
+ let emptyOuter := mkMd (.StaticCall "mapConst" [emptyInner])
let outerMapExpr := composites.foldl (fun acc ct =>
let typeConst := mkMd (.StaticCall (mkId $ ct.name.text ++ "_TypeTag") [])
let innerMapRef := mkMd (.StaticCall s!"ancestorsFor{ct.name.text}" [])
diff --git a/StrataPython/StrataPython/Cli.lean b/StrataPython/StrataPython/Cli.lean
index bea3454845..829d4c0a4b 100644
--- a/StrataPython/StrataPython/Cli.lean
+++ b/StrataPython/StrataPython/Cli.lean
@@ -575,7 +575,8 @@ def pyInterpretCommand : _root_.Command where
if let some dir := keepDir then
IO.FS.createDirAll dir
IO.FS.writeFile (dir ++ "/laurel.st") (toString (Std.format laurel))
- match ← StrataPython.translateCombinedLaurel laurel with
+ match ← StrataPython.translateCombinedLaurel laurel keepDir
+ (alwaysCallCoreFunctions := false) with
| (some core, diags) => pure (core, diags)
| (none, diags) => exitFailure s!"Laurel to Core translation failed: {diags}"
| .error () =>
diff --git a/StrataPython/StrataPython/PySpecPipeline.lean b/StrataPython/StrataPython/PySpecPipeline.lean
index 02322f079b..d7a063e066 100644
--- a/StrataPython/StrataPython/PySpecPipeline.lean
+++ b/StrataPython/StrataPython/PySpecPipeline.lean
@@ -396,17 +396,21 @@ public def splitProcNames (prog : Core.Program)
public def translateCombinedLaurelWithLowered (combined : Laurel.Program)
(keepAllFilesPrefix : Option String := none)
(pipelineCtx : Option Pipeline.PipelineContext := none)
+ (alwaysCallCoreFunctions : Bool := true)
: IO (Option Core.Program × List DiagnosticModel × Laurel.Program × Statistics) := do
let (coreOption, errors, lowered, stats) ←
- Laurel.translateWithLaurel { inlineFunctionsWhenPossible := true, keepAllFilesPrefix }
+ Laurel.translateWithLaurel { inlineFunctionsWhenPossible := true, keepAllFilesPrefix, alwaysCallCoreFunctions }
combined (pipelineCtx := pipelineCtx)
return (coreOption.map appendCorePartOfRuntime, errors, lowered, stats)
/-- Translate a combined Laurel program to Core and prepend the full
runtime prelude. -/
public def translateCombinedLaurel (combined : Laurel.Program) (keepAllFilesPrefix : Option String := none)
+ (alwaysCallCoreFunctions : Bool := true)
: IO (Option Core.Program × List DiagnosticModel) := do
- let (coreOption, errors, _, _) ← translateCombinedLaurelWithLowered combined keepAllFilesPrefix
+ let (coreOption, errors, _, _) ←
+ translateCombinedLaurelWithLowered combined keepAllFilesPrefix
+ (alwaysCallCoreFunctions := alwaysCallCoreFunctions)
return (coreOption, errors)
/-- Run the pyAnalyzeLaurel pipeline: read a Python Ion program,
diff --git a/StrataPython/StrataPython/PythonRuntimeLaurelPart.lean b/StrataPython/StrataPython/PythonRuntimeLaurelPart.lean
index c1352bfd71..1b10e6171b 100644
--- a/StrataPython/StrataPython/PythonRuntimeLaurelPart.lean
+++ b/StrataPython/StrataPython/PythonRuntimeLaurelPart.lean
@@ -157,150 +157,127 @@ composite re_Match {
}
// re.Match methods — uninterpreted (capture groups are beyond SMT-LIB)
-function re_Match_group (self : re_Match, n : int) : string;
-function re_Match_start (self : re_Match, n : int) : int;
-function re_Match_end (self : re_Match, n : int) : int;
-function re_Match_span_start (self : re_Match, n : int) : int;
-function re_Match_span_end (self : re_Match, n : int) : int;
-function re_Match_lastindex (self : re_Match) : int;
-function re_Match_lastgroup (self : re_Match) : string;
-function re_Match_groups (self : re_Match) : ListStr;
-
-function re_pattern_error(pattern : string) : Error
+procedure re_Match_group (self : re_Match, n : int) : string;
+procedure re_Match_start (self : re_Match, n : int) : int;
+procedure re_Match_end (self : re_Match, n : int) : int;
+procedure re_Match_span_start (self : re_Match, n : int) : int;
+procedure re_Match_span_end (self : re_Match, n : int) : int;
+procedure re_Match_lastindex (self : re_Match) : int;
+procedure re_Match_lastgroup (self : re_Match) : string;
+procedure re_Match_groups (self : re_Match) : ListStr;
+
+procedure re_pattern_error(pattern : string) : Error
external;
// The _bool variants are also factory functions (not inlined here) so that
// unsupported patterns leave an uninterpreted Bool UF rather than an
// uninterpreted RegLan UF. An uninterpreted Bool UF produces `unknown`
// gracefully; an uninterpreted RegLan UF causes cvc5 theory-combination errors.
-function re_fullmatch_bool(pattern : string, s : string) : bool
+procedure re_fullmatch_bool(pattern : string, s : string) : bool
external;
-function re_match_bool(pattern : string, s : string) : bool
+procedure re_match_bool(pattern : string, s : string) : bool
external;
-function re_search_bool(pattern : string, s : string) : bool
+procedure re_search_bool(pattern : string, s : string) : bool
external;
-function Str.InRegEx(s: string, r: Core regex): bool external;
-function Str.Length(s: string): int external;
+procedure Str.InRegEx(s: string, r: Core regex): bool external;
+procedure Str.Length(s: string): int external;
// /////////////////////////////////////////////////////////////////////////////////////
-function mk_re_Match(s : string) : Any {
- from_ClassInstance("re_Match",
+procedure mk_re_Match(s : string) : Any
+ return from_ClassInstance("re_Match",
DictStrAny_cons("re_match_string", from_str(s),
DictStrAny_cons("re_match_pos", from_int(0),
DictStrAny_cons("re_match_endpos", from_int(Str.Length(s)),
- DictStrAny_empty()))))
-};
+ DictStrAny_empty()))));
// re.compile is a no-op: returns the pattern string unchanged.
-function re_compile(pattern : Any) : Any
+procedure re_compile(pattern : Any) : Any
requires Any..isfrom_str(pattern)
-{
- pattern
-};
+ return pattern;
-function re_fullmatch(pattern : Any, s : Any) : Any
+procedure re_fullmatch(pattern : Any, s : Any) : Any
requires Any..isfrom_str(pattern) && Any..isfrom_str(s)
-{
- if Error..isRePatternError(re_pattern_error(Any..as_string!(pattern)))
- then exception(re_pattern_error(Any..as_string!(pattern)))
- else if re_fullmatch_bool(Any..as_string!(pattern), Any..as_string!(s))
- then mk_re_Match(Any..as_string!(s))
- else from_None()
-};
-function re_match(pattern : Any, s : Any) : Any
+ return if Error..isRePatternError(re_pattern_error(Any..as_string!(pattern)))
+ then exception(re_pattern_error(Any..as_string!(pattern)))
+ else if re_fullmatch_bool(Any..as_string!(pattern), Any..as_string!(s))
+ then mk_re_Match(Any..as_string!(s))
+ else from_None();
+
+procedure re_match(pattern : Any, s : Any) : Any
requires Any..isfrom_str(pattern) && Any..isfrom_str(s)
-{
- if Error..isRePatternError(re_pattern_error(Any..as_string!(pattern)))
- then exception(re_pattern_error(Any..as_string!(pattern)))
- else if re_match_bool(Any..as_string!(pattern), Any..as_string!(s))
- then mk_re_Match(Any..as_string!(s))
- else from_None()
-};
-function re_search(pattern : Any, s : Any) : Any
+ return if Error..isRePatternError(re_pattern_error(Any..as_string!(pattern)))
+ then exception(re_pattern_error(Any..as_string!(pattern)))
+ else if re_match_bool(Any..as_string!(pattern), Any..as_string!(s))
+ then mk_re_Match(Any..as_string!(s))
+ else from_None();
+
+procedure re_search(pattern : Any, s : Any) : Any
requires Any..isfrom_str(pattern) && Any..isfrom_str(s)
-{
- if Error..isRePatternError(re_pattern_error(Any..as_string!(pattern)))
- then exception(re_pattern_error(Any..as_string!(pattern)))
- else if re_search_bool(Any..as_string!(pattern), Any..as_string!(s))
- then mk_re_Match(Any..as_string!(s))
- else from_None()
-};
+ return if Error..isRePatternError(re_pattern_error(Any..as_string!(pattern)))
+ then exception(re_pattern_error(Any..as_string!(pattern)))
+ else if re_search_bool(Any..as_string!(pattern), Any..as_string!(s))
+ then mk_re_Match(Any..as_string!(s))
+ else from_None();
// /////////////////////////////////////////////////////////////////////////////////////
//Functions that we provide to Python user
//to write assertions/contracts about about types of variables
-function isBool (v: Any) : Any {
- from_bool (Any..isfrom_bool(v))
-};
+procedure isBool (v: Any) : Any
+ return from_bool (Any..isfrom_bool(v));
-function isInt (v: Any) : Any {
- from_bool (Any..isfrom_int(v))
-};
+procedure isInt (v: Any) : Any
+ return from_bool (Any..isfrom_int(v));
-function isFloat (v: Any) : Any {
- from_bool (Any..isfrom_float(v))
-};
+procedure isFloat (v: Any) : Any
+ return from_bool (Any..isfrom_float(v));
-function isString (v: Any) : Any {
- from_bool (Any..isfrom_str(v))
-};
+procedure isString (v: Any) : Any
+ return from_bool (Any..isfrom_str(v));
-function isdatetime (v: Any) : Any {
- from_bool (Any..isfrom_datetime(v))
-};
+procedure isdatetime (v: Any) : Any
+ return from_bool (Any..isfrom_datetime(v));
-function isDict (v: Any) : Any {
- from_bool (Any..isfrom_DictStrAny(v))
-};
+procedure isDict (v: Any) : Any
+ return from_bool (Any..isfrom_DictStrAny(v));
-function isList (v: Any) : Any {
- from_bool (Any..isfrom_ListAny(v))
-};
+procedure isList (v: Any) : Any
+ return from_bool (Any..isfrom_ListAny(v));
-function isClassInstance (v: Any) : Any {
- from_bool (Any..isfrom_ClassInstance(v))
-};
+procedure isClassInstance (v: Any) : Any
+ return from_bool (Any..isfrom_ClassInstance(v));
-function isInstance_of_Int (v: Any) : Any {
- from_bool (Any..isfrom_int(v) || Any..isfrom_bool(v))
-};
+procedure isInstance_of_Int (v: Any) : Any
+ return from_bool (Any..isfrom_int(v) || Any..isfrom_bool(v));
-function isInstance_of_Float (v: Any) : Any {
- from_bool (Any..isfrom_float(v) || Any..isfrom_int(v) || Any..isfrom_bool(v))
-};
+procedure isInstance_of_Float (v: Any) : Any
+ return from_bool (Any..isfrom_float(v) || Any..isfrom_int(v) || Any..isfrom_bool(v));
// /////////////////////////////////////////////////////////////////////////////////////
//Functions that we provide to Python user
//to write assertions/contracts about about types of errors
// /////////////////////////////////////////////////////////////////////////////////////
-function isTypeError (e: Error) : Any {
- from_bool (Error..isTypeError(e))
-};
+procedure isTypeError (e: Error) : Any
+return from_bool (Error..isTypeError(e));
-function isAttributeError (e: Error) : Any {
- from_bool (Error..isAttributeError(e))
-};
+procedure isAttributeError (e: Error) : Any
+return from_bool (Error..isAttributeError(e));
-function isAssertionError (e: Error) : Any {
- from_bool (Error..isAssertionError(e))
-};
+procedure isAssertionError (e: Error) : Any
+return from_bool (Error..isAssertionError(e));
-function isUnimplementedError (e: Error) : Any {
- from_bool (Error..isUnimplementedError(e))
-};
+procedure isUnimplementedError (e: Error) : Any
+return from_bool (Error..isUnimplementedError(e));
-function isUndefinedError (e: Error) : Any {
- from_bool (Error..isUndefinedError(e))
-};
+procedure isUndefinedError (e: Error) : Any
+return from_bool (Error..isUndefinedError(e));
-function isError (e: Error) : bool {
- ! Error..isNoError(e)
-};
+procedure isError (e: Error) : bool
+return ! Error..isNoError(e);
// /////////////////////////////////////////////////////////////////////////////////////
//The following function convert Any type to bool
@@ -308,197 +285,152 @@ function isError (e: Error) : bool {
// https://docs.python.org/3/library/stdtypes.html
// /////////////////////////////////////////////////////////////////////////////////////
-function Any_to_bool (v: Any) : bool
-{
- if (Any..isfrom_bool(v)) then Any..as_bool!(v) else
+procedure Any_to_bool (v: Any) : bool
+return if (Any..isfrom_bool(v)) then Any..as_bool!(v) else
if (Any..isfrom_None(v)) then false else
if (Any..isfrom_str(v)) then !(Any..as_string!(v) == "") else
if (Any..isfrom_int(v)) then !(Any..as_int!(v) == 0) else
if (Any..isfrom_float(v)) then !(Any..as_float!(v) == 0.0) else
if (Any..isfrom_DictStrAny(v)) then !(Any..as_Dict!(v) == DictStrAny_empty()) else
if (Any..isfrom_ListAny(v)) then !(Any..as_ListAny!(v) == ListAny_nil()) else
- >
-};
+ >;
-function to_bool_any(v: Any) : Any
-{
- from_bool(Any_to_bool(v))
-};
+procedure to_bool_any(v: Any) : Any
+return from_bool(Any_to_bool(v));
// /////////////////////////////////////////////////////////////////////////////////////
// ListAny functions
// /////////////////////////////////////////////////////////////////////////////////////
-function List_len (l : ListAny) : int
-{
- if ListAny..isListAny_nil(l) then 0 else 1 + List_len(ListAny..tail!(l))
-};
+procedure List_len (l : ListAny) : int
+return if ListAny..isListAny_nil(l) then 0 else 1 + List_len(ListAny..tail!(l));
procedure List_len_pos(l : ListAny)
invokeOn List_len(l)
opaque
ensures List_len(l) >= 0;
-function List_contains (l : ListAny, x: Any) : bool
-{
- if ListAny..isListAny_nil(l) then false else (ListAny..head!(l) == x) || List_contains(ListAny..tail!(l), x)
-};
+procedure List_contains (l : ListAny, x: Any) : bool
+return if ListAny..isListAny_nil(l) then false else (ListAny..head!(l) == x) || List_contains(ListAny..tail!(l), x);
-function List_extend (l1 : ListAny, l2: ListAny) : ListAny
-{
- if ListAny..isListAny_nil(l1) then l2
- else ListAny_cons(ListAny..head!(l1), List_extend(ListAny..tail!(l1), l2))
-};
+procedure List_extend (l1 : ListAny, l2: ListAny) : ListAny
+return if ListAny..isListAny_nil(l1) then l2
+ else ListAny_cons(ListAny..head!(l1), List_extend(ListAny..tail!(l1), l2));
-function List_get_non_neg (l : ListAny, i : int) : Any
+procedure List_get_non_neg (l : ListAny, i : int) : Any
requires i >= 0 && i < List_len(l)
-{
- if ListAny..isListAny_nil(l) then from_None()
+return if ListAny..isListAny_nil(l) then from_None()
else if i == 0 then ListAny..head!(l)
- else List_get_non_neg(ListAny..tail!(l), i - 1)
-};
+ else List_get_non_neg(ListAny..tail!(l), i - 1);
-function List_get (l : ListAny, i : int) : Any
+procedure List_get (l : ListAny, i : int) : Any
requires i >= - List_len(l) && i < List_len(l)
-{
- if i >= 0 then List_get_non_neg(l, i)
- else List_get_non_neg(l, List_len(l) + i)
-};
+return if i >= 0 then List_get_non_neg(l, i)
+ else List_get_non_neg(l, List_len(l) + i);
-function List_take (l : ListAny, i: int) : ListAny
+procedure List_take (l : ListAny, i: int) : ListAny
requires i >= 0 && i <= List_len(l)
-{
- if ListAny..isListAny_nil(l) then ListAny_nil()
+return if ListAny..isListAny_nil(l) then ListAny_nil()
else if i == 0 then ListAny_nil()
- else ListAny_cons(ListAny..head!(l), List_take(ListAny..tail!(l), i - 1))
-};
+ else ListAny_cons(ListAny..head!(l), List_take(ListAny..tail!(l), i - 1));
procedure List_take_len(l : ListAny, i: int)
invokeOn List_len(List_take(l,i))
opaque
ensures i >= 0 && i <= List_len(l) ==> List_len(List_take(l,i)) == i;
-function List_drop (l : ListAny, i: int) : ListAny
+procedure List_drop (l : ListAny, i: int) : ListAny
requires i >= 0 && i <= List_len(l)
-{
- if ListAny..isListAny_nil(l) then ListAny_nil()
+return if ListAny..isListAny_nil(l) then ListAny_nil()
else if i == 0 then l
- else List_drop(ListAny..tail!(l), i - 1)
-};
+ else List_drop(ListAny..tail!(l), i - 1);
procedure List_drop_len(l : ListAny, i: int)
invokeOn List_len(List_drop(l,i))
opaque
ensures i >= 0 && i <= List_len(l) ==> List_len(List_drop(l,i)) == List_len(l) - i;
-function int_max (i1: int, i2: int) : int
-{
- if i1 >= i2 then i1 else i2
-};
+procedure int_max (i1: int, i2: int) : int
+return if i1 >= i2 then i1 else i2;
-function int_min (i1: int, i2: int) : int
-{
- if i1 <= i2 then i1 else i2
-};
+procedure int_min (i1: int, i2: int) : int
+return if i1 <= i2 then i1 else i2;
-function List_slice_non_neg (l : ListAny, start : int, stop: int) : ListAny
+procedure List_slice_non_neg (l : ListAny, start : int, stop: int) : ListAny
requires start >= 0 && stop >= 0
-{
- if (start >= List_len(l)) || (start >= stop) then ListAny_nil()
- else List_take (List_drop (l, start), int_min(stop, List_len(l)) - start)
-};
+return if (start >= List_len(l)) || (start >= stop) then ListAny_nil()
+ else List_take (List_drop (l, start), int_min(stop, List_len(l)) - start);
-function List_slice (l : ListAny, start : int, stop: int) : ListAny
-{
- List_slice_non_neg (l,
+procedure List_slice (l : ListAny, start : int, stop: int) : ListAny
+return List_slice_non_neg (l,
if start >= 0 then start else int_max (List_len(l) + start, 0),
if stop >= 0 then stop else int_max (List_len(l) + stop, 0)
- )
-};
+ );
-function List_set_non_neg (l : ListAny, i : int, v: Any) : ListAny
+procedure List_set_non_neg (l : ListAny, i : int, v: Any) : ListAny
requires i >= 0 && i < List_len(l)
-{
- if ListAny..isListAny_nil(l) then ListAny_nil()
+return if ListAny..isListAny_nil(l) then ListAny_nil()
else if i == 0 then ListAny_cons(v, ListAny..tail!(l))
- else ListAny_cons(ListAny..head!(l), List_set_non_neg(ListAny..tail!(l), i - 1, v))
-};
+ else ListAny_cons(ListAny..head!(l), List_set_non_neg(ListAny..tail!(l), i - 1, v));
-function List_set (l : ListAny, i : int, v: Any) : ListAny
+procedure List_set (l : ListAny, i : int, v: Any) : ListAny
requires i >= - List_len(l) && i < List_len(l)
-{
- if i >= 0 then List_set_non_neg(l, i, v)
- else List_set_non_neg(l, List_len(l) + i, v)
-};
+return if i >= 0 then List_set_non_neg(l, i, v)
+ else List_set_non_neg(l, List_len(l) + i, v);
//Require recursive function on int
-function List_repeat (l: ListAny, n: int): ListAny;
+procedure List_repeat (l: ListAny, n: int): ListAny;
-function range (start: Any, stop: Any, step: Any) : Any
+procedure range (start: Any, stop: Any, step: Any) : Any
requires Any..isfrom_int(start) && Any..isfrom_None(stop) && Any..isfrom_None(step);
// /////////////////////////////////////////////////////////////////////////////////////
// DictStrAny functions
// /////////////////////////////////////////////////////////////////////////////////////
-function DictStrAny_contains (d : DictStrAny, key: string) : bool
-{
- if DictStrAny..isDictStrAny_empty(d) then false
- else (DictStrAny..key!(d) == key) || DictStrAny_contains(DictStrAny..tail!(d), key)
-};
+procedure DictStrAny_contains (d : DictStrAny, key: string) : bool
+return if DictStrAny..isDictStrAny_empty(d) then false
+ else (DictStrAny..key!(d) == key) || DictStrAny_contains(DictStrAny..tail!(d), key);
-function DictStrAny_get (d : DictStrAny, key: string) : Any
+procedure DictStrAny_get (d : DictStrAny, key: string) : Any
requires DictStrAny_contains(d, key)
-{
- if DictStrAny..isDictStrAny_empty(d) then from_None()
+return if DictStrAny..isDictStrAny_empty(d) then from_None()
else if DictStrAny..key!(d) == key then DictStrAny..val!(d)
- else DictStrAny_get(DictStrAny..tail!(d), key)
-};
+ else DictStrAny_get(DictStrAny..tail!(d), key);
-function DictStrAny_get_or_none (d : DictStrAny, key: string) : Any
-{
- if DictStrAny_contains(d, key) then DictStrAny_get(d, key)
- else from_None()
-};
+procedure DictStrAny_get_or_none (d : DictStrAny, key: string) : Any
+return if DictStrAny_contains(d, key) then DictStrAny_get(d, key)
+ else from_None();
-function Any_get_or_none (dict: Any, key: Any) : Any
+procedure Any_get_or_none (dict: Any, key: Any) : Any
requires Any..isfrom_DictStrAny(dict) && Any..isfrom_str(key)
-{
- DictStrAny_get_or_none(Any..as_Dict!(dict), Any..as_string!(key))
-};
+return DictStrAny_get_or_none(Any..as_Dict!(dict), Any..as_string!(key));
-function DictStrAny_insert (d : DictStrAny, key: string, val: Any) : DictStrAny
-{
- if DictStrAny..isDictStrAny_empty(d) then DictStrAny_cons(key, val, DictStrAny_empty())
+procedure DictStrAny_insert (d : DictStrAny, key: string, val: Any) : DictStrAny
+return if DictStrAny..isDictStrAny_empty(d) then DictStrAny_cons(key, val, DictStrAny_empty())
else if DictStrAny..key!(d) == key then DictStrAny_cons(key, val, DictStrAny..tail!(d))
- else DictStrAny_cons(DictStrAny..key!(d), DictStrAny..val!(d), DictStrAny_insert(DictStrAny..tail!(d), key, val))
-};
+ else DictStrAny_cons(DictStrAny..key!(d), DictStrAny..val!(d), DictStrAny_insert(DictStrAny..tail!(d), key, val));
-function Any_get (dictOrList: Any, index: Any): Any
+procedure Any_get (dictOrList: Any, index: Any): Any
requires (Any..isfrom_DictStrAny(dictOrList) && Any..isfrom_str(index) && DictStrAny_contains(Any..as_Dict!(dictOrList), Any..as_string!(index))) ||
(Any..isfrom_ListAny(dictOrList) && Any..isfrom_int(index) && Any..as_int!(index) >= - List_len(Any..as_ListAny!(dictOrList)) && Any..as_int!(index) < List_len(Any..as_ListAny!(dictOrList)))
-{
- if Any..isfrom_DictStrAny(dictOrList) then
+return if Any..isfrom_DictStrAny(dictOrList) then
DictStrAny_get(Any..as_Dict!(dictOrList), Any..as_string!(index))
else
- List_get(Any..as_ListAny!(dictOrList), Any..as_int!(index))
-};
+ List_get(Any..as_ListAny!(dictOrList), Any..as_int!(index));
-function Any_get_slice (list: Any, index: Any): Any
+procedure Any_get_slice (list: Any, index: Any): Any
requires (Any..isfrom_ListAny(list) && Any..isfrom_Slice(index))
-{
- from_ListAny(List_slice(
+return from_ListAny(List_slice(
Any..as_ListAny!(list),
Any..start!(index),
if OptionInt..isOptSome(Any..stop!(index))
then OptionInt..unwrap!(Any..stop!(index))
- else List_len(Any..as_ListAny!(list))))
-};
+ else List_len(Any..as_ListAny!(list))));
-function Any_get! (dictOrList: Any, index: Any): Any
-{
- if Any..isexception(dictOrList) then dictOrList
+procedure Any_get! (dictOrList: Any, index: Any): Any
+return if Any..isexception(dictOrList) then dictOrList
else if Any..isexception(index) then index
else if !(Any..isfrom_DictStrAny(dictOrList) && Any..isfrom_str(index)) && !(Any..isfrom_ListAny(dictOrList) && Any..isfrom_int(index)) then
exception (TypeError("Invalid subscription type"))
@@ -507,23 +439,19 @@ function Any_get! (dictOrList: Any, index: Any): Any
else if Any..isfrom_ListAny(dictOrList) && Any..isfrom_int(index) && Any..as_int!(index) >= - List_len(Any..as_ListAny!(dictOrList)) && Any..as_int!(index) < List_len(Any..as_ListAny!(dictOrList)) then
List_get(Any..as_ListAny!(dictOrList), Any..as_int!(index))
else
- exception (IndexError("Invalid subscription"))
-};
+ exception (IndexError("Invalid subscription"));
-function Any_set (dictOrList: Any, index: Any, val: Any): Any
+procedure Any_set (dictOrList: Any, index: Any, val: Any): Any
requires (Any..isfrom_DictStrAny(dictOrList) && Any..isfrom_str(index)) ||
(Any..isfrom_ListAny(dictOrList) && Any..isfrom_int(index) &&
Any..as_int!(index) >= - List_len(Any..as_ListAny!(dictOrList)) && Any..as_int!(index) < List_len(Any..as_ListAny!(dictOrList)))
-{
- if Any..isfrom_DictStrAny(dictOrList) then
+return if Any..isfrom_DictStrAny(dictOrList) then
from_DictStrAny(DictStrAny_insert(Any..as_Dict!(dictOrList), Any..as_string!(index), val))
else
- from_ListAny(List_set(Any..as_ListAny!(dictOrList), Any..as_int!(index), val))
-};
+ from_ListAny(List_set(Any..as_ListAny!(dictOrList), Any..as_int!(index), val));
-function Any_set! (dictOrList: Any, index: Any, val: Any): Any
-{
- if Any..isexception(dictOrList) then dictOrList
+procedure Any_set! (dictOrList: Any, index: Any, val: Any): Any
+return if Any..isexception(dictOrList) then dictOrList
else if Any..isexception(index) then index
else if Any..isexception(val) then val
else if !(Any..isfrom_DictStrAny(dictOrList) && Any..isfrom_str(index)) && !(Any..isfrom_ListAny(dictOrList) && Any..isfrom_int(index)) then
@@ -534,66 +462,57 @@ function Any_set! (dictOrList: Any, index: Any, val: Any): Any
Any..as_int!(index) >= - List_len(Any..as_ListAny!(dictOrList)) && Any..as_int!(index) < List_len(Any..as_ListAny!(dictOrList)) then
from_ListAny(List_set(Any..as_ListAny!(dictOrList), Any..as_int!(index), val))
else
- exception (IndexError("Index out of bound"))
-};
+ exception (IndexError("Index out of bound"));
-function Any_sets! (indices: ListAny, dictOrList: Any, val: Any): Any
-{
- if ListAny..isListAny_nil(indices) then dictOrList
+procedure Any_sets! (indices: ListAny, dictOrList: Any, val: Any): Any
+return if ListAny..isListAny_nil(indices) then dictOrList
else if ListAny..isListAny_nil(ListAny..tail!(indices)) then Any_set!(dictOrList, ListAny..head!(indices), val)
else Any_set!(dictOrList, ListAny..head!(indices),
- Any_sets!(ListAny..tail!(indices), Any_get!(dictOrList, ListAny..head!(indices)), val))
-};
+ Any_sets!(ListAny..tail!(indices), Any_get!(dictOrList, ListAny..head!(indices)), val));
-function Any_len (v: Any) : int;
+procedure Any_len (v: Any) : int;
-function Any_len_to_Any (v: Any) : Any {
- from_int(Any_len(v))
-};
+procedure Any_len_to_Any (v: Any) : Any
+return from_int(Any_len(v));
procedure Any_len_pos(v: Any)
invokeOn Any_len(v)
opaque
ensures Any_len(v) >= 0;
-function Any_iter_index(iter: Any, index: int) : Any;
+procedure Any_iter_index(iter: Any, index: int) : Any;
-function PIn (v: Any, dictOrList: Any) : Any
+procedure PIn (v: Any, dictOrList: Any) : Any
requires (Any..isfrom_DictStrAny(dictOrList) && Any..isfrom_str(v)) || Any..isfrom_ListAny(dictOrList)
-{
- from_bool(
+return from_bool(
if Any..isfrom_DictStrAny(dictOrList) then
DictStrAny_contains(Any..as_Dict!(dictOrList), Any..as_string!(v))
else
List_contains(Any..as_ListAny!(dictOrList), v)
- )
-};
+ );
-function PNotIn ( v: Any, dictOrList: Any) : Any
+procedure PNotIn ( v: Any, dictOrList: Any) : Any
requires (Any..isfrom_DictStrAny(dictOrList) && Any..isfrom_str(v)) || Any..isfrom_ListAny(dictOrList)
-{
- from_bool(
+return from_bool(
if Any..isfrom_DictStrAny(dictOrList) then
!DictStrAny_contains(Any..as_Dict!(dictOrList), Any..as_string!(v))
else
!List_contains(Any..as_ListAny!(dictOrList), v)
- )
-};
+ );
// /////////////////////////////////////////////////////////////////////////////////////
// Python treats some values of different types to be equivalent
// This function models that behavior
// /////////////////////////////////////////////////////////////////////////////////////
-function is_IntReal (v: Any) : bool;
-function Any_real_to_int (v: Any) : int;
+procedure is_IntReal (v: Any) : bool;
+procedure Any_real_to_int (v: Any) : int;
-function normalize_any (v : Any) : Any {
- if v == from_bool(true) then from_int(1)
+procedure normalize_any (v : Any) : Any
+return if v == from_bool(true) then from_int(1)
else (if v == from_bool(false) then from_int(0) else
if Any..isfrom_float(v) && is_IntReal(v) then from_int(Any_real_to_int(v)) else
- v)
-};
+ v);
// /////////////////////////////////////////////////////////////////////////////////////
@@ -606,21 +525,22 @@ function normalize_any (v : Any) : Any {
// /////////////////////////////////////////////////////////////////////////////////////
// This function convert an int to a real
// Need to connect to an SMT function
-function int_to_real (i: int) : real;
+procedure int_to_real (i: int) : real;
// /////////////////////////////////////////////////////////////////////////////////////
// Converting bool to int or real
// Used to in Python binary operators' modelling
-function bool_to_int (bval: bool) : int {if bval then 1 else 0};
-function bool_to_real (b: bool) : real {if b then 1.0 else 0.0};
+procedure bool_to_int (bval: bool) : int
+return if bval then 1 else 0;
+procedure bool_to_real (b: bool) : real
+return if b then 1.0 else 0.0;
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling of Python unary operations
// /////////////////////////////////////////////////////////////////////////////////////
-function PNeg (v: Any) : Any
-{
- if Any..isexception(v) then v
+procedure PNeg (v: Any) : Any
+return if Any..isexception(v) then v
else if Any..isfrom_bool(v) then
from_int(- bool_to_int(Any..as_bool!(v)))
else if Any..isfrom_int(v) then
@@ -628,35 +548,29 @@ function PNeg (v: Any) : Any
else if Any..isfrom_float(v) then
from_float(- Any..as_float!(v))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PBitNot (v: Any) : Any
-{
- if Any..isexception(v) then v
+procedure PBitNot (v: Any) : Any
+return if Any..isexception(v) then v
else if Any..isfrom_bool(v) then
from_int(-(bool_to_int(Any..as_bool!(v)) + 1))
else if Any..isfrom_int(v) then
from_int(-(Any..as_int!(v) + 1))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PNot (v: Any) : Any
-{
- if Any..isexception(v) then v
- else from_bool(!(Any_to_bool(v)))
-};
+procedure PNot (v: Any) : Any
+return if Any..isexception(v) then v
+ else from_bool(!(Any_to_bool(v)));
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling of Python binary operations
// /////////////////////////////////////////////////////////////////////////////////////
-function Str.Concat(s: string, s2: string): string external;
+procedure Str.Concat(s: string, s2: string): string external;
-function PAdd (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PAdd (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int(bool_to_int(Any..as_bool!(v1)) + bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -680,12 +594,10 @@ function PAdd (v1: Any, v2: Any) : Any
else if Any..isfrom_datetime(v1) && Any..isfrom_int(v2) then
from_datetime((Any..as_datetime!(v1) + Any..as_int!(v2)))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PSub (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PSub (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int(bool_to_int(Any..as_bool!(v1)) - bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -709,14 +621,12 @@ function PSub (v1: Any, v2: Any) : Any
else if Any..isfrom_datetime(v1) && Any..isfrom_datetime(v2) then
from_int(Any..as_datetime!(v1) - Any..as_datetime!(v2))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function string_repeat (s: string, i: int) : string;
+procedure string_repeat (s: string, i: int) : string;
-function PMul (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PMul (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int(bool_to_int(Any..as_bool!(v1)) * bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -748,13 +658,11 @@ function PMul (v1: Any, v2: Any) : Any
else if Any..isfrom_float(v1) && Any..isfrom_float(v2) then
from_float(Any..as_float!(v1) * Any..as_float!(v2))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PFloorDiv (v1: Any, v2: Any) : Any
+procedure PFloorDiv (v1: Any, v2: Any) : Any
requires (Any..isfrom_bool(v2)==>Any..as_bool!(v2)) && (Any..isfrom_int(v2)==>Any..as_int!(v2)!=0)
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int( bool_to_int(Any..as_bool!(v1)) / bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -764,25 +672,23 @@ function PFloorDiv (v1: Any, v2: Any) : Any
else if Any..isfrom_int(v1) && Any..isfrom_int(v2) then
from_int(Any..as_int!(v1) / Any..as_int!(v2))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling of Python comparision operations
// /////////////////////////////////////////////////////////////////////////////////////
-function string_lt (s1: string, s2: string) : bool;
-function string_le (s1: string, s2: string) : bool;
-function string_gt (s1: string, s2: string) : bool;
-function string_ge (s1: string, s2: string) : bool;
-function List_lt (l1: ListAny, l2: ListAny): bool;
-function List_le (l1: ListAny, l2: ListAny): bool;
-function List_gt (l1: ListAny, l2: ListAny): bool;
-function List_ge (l1: ListAny, l2: ListAny): bool;
+procedure string_lt (s1: string, s2: string) : bool;
+procedure string_le (s1: string, s2: string) : bool;
+procedure string_gt (s1: string, s2: string) : bool;
+procedure string_ge (s1: string, s2: string) : bool;
+procedure List_lt (l1: ListAny, l2: ListAny): bool;
+procedure List_le (l1: ListAny, l2: ListAny): bool;
+procedure List_gt (l1: ListAny, l2: ListAny): bool;
+procedure List_ge (l1: ListAny, l2: ListAny): bool;
-function PLt (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PLt (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_bool(bool_to_int(Any..as_bool!(v1)) < bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -808,12 +714,10 @@ function PLt (v1: Any, v2: Any) : Any
else if Any..isfrom_datetime(v1) && Any..isfrom_datetime(v2) then
from_bool(Any..as_datetime!(v1) bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -870,12 +772,10 @@ function PGt (v1: Any, v2: Any) : Any
else if Any..isfrom_datetime(v1) && Any..isfrom_datetime(v2) then
from_bool(Any..as_datetime!(v1) >Any..as_datetime!(v2))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PGe (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PGe (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_bool(bool_to_int(Any..as_bool!(v1)) >= bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -901,32 +801,25 @@ function PGe (v1: Any, v2: Any) : Any
else if Any..isfrom_datetime(v1) && Any..isfrom_datetime(v2) then
from_bool(Any..as_datetime!(v1) >=Any..as_datetime!(v2))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PEq (v: Any, v': Any) : Any {
- from_bool(normalize_any(v) == normalize_any (v'))
-};
+procedure PEq (v: Any, v': Any) : Any
+return from_bool(normalize_any(v) == normalize_any (v'));
-function PNEq (v: Any, v': Any) : Any {
- from_bool(normalize_any(v) != normalize_any (v'))
-};
+procedure PNEq (v: Any, v': Any) : Any
+return from_bool(normalize_any(v) != normalize_any (v'));
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling of Python Boolean operations And and Or
// /////////////////////////////////////////////////////////////////////////////////////
-function PAnd (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else
- if ! Any_to_bool (v1) then v1 else v2
-};
+procedure PAnd (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else
+ if ! Any_to_bool (v1) then v1 else v2;
-function POr (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else
- if Any_to_bool (v1) then v1 else v2
-};
+procedure POr (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else
+ if Any_to_bool (v1) then v1 else v2;
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling of Python arithmetic and bitwise operations
@@ -934,16 +827,15 @@ function POr (v1: Any, v2: Any) : Any
// int_pow, int_rshift, and float_pow are provided by the factory (PyFactory.lean) with concreteEval.
// Declared here as external so PPow/PRShift can reference them; they are filtered
// during Laurel-to-Core translation and the factory provides the Core versions.
-function int_pow (base: int, exp: int) : int
+procedure int_pow (base: int, exp: int) : int
external;
-function int_rshift (x: int, n: int) : int
+procedure int_rshift (x: int, n: int) : int
external;
-function float_pow (base: real, exp: real) : real
+procedure float_pow (base: real, exp: real) : real
external;
-function PPow (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PPow (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if (Any..isfrom_int(v1) && Any..isfrom_int(v2) && Any..as_int!(v2) >= 0) then
from_int(int_pow(Any..as_int!(v1), Any..as_int!(v2)))
else if (Any..isfrom_int(v1) && Any..isfrom_int(v2)) then
@@ -955,13 +847,11 @@ function PPow (v1: Any, v2: Any) : Any
else if (Any..isfrom_int(v1) && Any..isfrom_bool(v2)) then
from_int(int_pow(Any..as_int!(v1), bool_to_int(Any..as_bool!(v2))))
else
- exception(UnimplementedError("Pow is not defined on these input types"))
-};
+ exception(UnimplementedError("Pow is not defined on these input types"));
-function PMod (v1: Any, v2: Any) : Any
+procedure PMod (v1: Any, v2: Any) : Any
requires (Any..isfrom_bool(v2)==>Any..as_bool!(v2)) && (Any..isfrom_int(v2)==>Any..as_int!(v2)!=0)
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int( bool_to_int(Any..as_bool!(v1)) % bool_to_int(Any..as_bool!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) then
@@ -971,16 +861,14 @@ function PMod (v1: Any, v2: Any) : Any
else if Any..isfrom_int(v1) && Any..isfrom_int(v2) then
from_int(Any..as_int!(v1) % Any..as_int!(v2))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling of Python bitwise shift operations
// /////////////////////////////////////////////////////////////////////////////////////
-function PLShift (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PLShift (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_int(v1) && Any..isfrom_int(v2) && Any..as_int!(v2) >= 0 then
from_int(Any..as_int!(v1) * int_pow(2, Any..as_int!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) && Any..as_int!(v2) >= 0 then
@@ -990,12 +878,10 @@ function PLShift (v1: Any, v2: Any) : Any
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int(bool_to_int(Any..as_bool!(v1)) * int_pow(2, bool_to_int(Any..as_bool!(v2))))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
-function PRShift (v1: Any, v2: Any) : Any
-{
- if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
+procedure PRShift (v1: Any, v2: Any) : Any
+return if Any..isexception(v1) then v1 else if Any..isexception(v2) then v2
else if Any..isfrom_int(v1) && Any..isfrom_int(v2) && Any..as_int!(v2) >= 0 then
from_int(int_rshift(Any..as_int!(v1), Any..as_int!(v2)))
else if Any..isfrom_bool(v1) && Any..isfrom_int(v2) && Any..as_int!(v2) >= 0 then
@@ -1005,20 +891,18 @@ function PRShift (v1: Any, v2: Any) : Any
else if Any..isfrom_bool(v1) && Any..isfrom_bool(v2) then
from_int(int_rshift(bool_to_int(Any..as_bool!(v1)), bool_to_int(Any..as_bool!(v2))))
else
- exception(UndefinedError ("Operand Type is not defined"))
-};
+ exception(UndefinedError ("Operand Type is not defined"));
// /////////////////////////////////////////////////////////////////////////////////////
// Modelling some datetime-related Python operations, for testing purpose
// /////////////////////////////////////////////////////////////////////////////////////
-function to_string(a: Any) : string;
+procedure to_string(a: Any) : string;
-function to_string_any(a: Any) : Any {
- from_str(to_string(a))
-};
+procedure to_string_any(a: Any) : Any
+return from_str(to_string(a));
-function datetime_strptime(dtstring: Any, format: Any) : Any;
+procedure datetime_strptime(dtstring: Any, format: Any) : Any;
procedure datetime_tostring_cancel(dt: Any)
invokeOn datetime_strptime(to_string_any(dt), from_str ("%Y-%m-%d"))
diff --git a/StrataPython/StrataPython/PythonToLaurel.lean b/StrataPython/StrataPython/PythonToLaurel.lean
index c01919b418..fc5c4eb6e5 100644
--- a/StrataPython/StrataPython/PythonToLaurel.lean
+++ b/StrataPython/StrataPython/PythonToLaurel.lean
@@ -1811,11 +1811,16 @@ partial def translateStmt (ctx : TranslationContext) (s : stmt SourceRange)
let (_, bodyStmts) ← translateStmtList loopCtx body.val.toList
let bodyBlock := mkStmtExprMdWithLoc (StmtExpr.Block bodyStmts (some continueLabel)) md
let (preamble, condRef) := getExceptionCheckPreamble ctx condExpr s!"$while_cond_{test.toAst.ann.start.byteIdx}"
- let whileStmt := mkStmtExprMdWithLoc (StmtExpr.While (Any_to_bool condRef) [] none bodyBlock) md
+ let whileStmt := mkStmtExprMdWithLoc (StmtExpr.While (Any_to_bool condRef) [] none bodyBlock false) md
let whileWrapped := mkStmtExprMdWithLoc (StmtExpr.Block [whileStmt] (some breakLabel)) md
return (loopCtx, preamble ++ [whileWrapped])
- -- Return statement: assign to the LaurelResult output parameter, then exit the body block.
+ -- Return statement: assign to the LaurelResult output parameter, then emit a
+ -- valueless `Return`. We deliberately do *not* jump to `bodyLabel` directly:
+ -- emitting a `.Return` lets `EliminateReturnStatements` redirect it to the
+ -- `$return` block it wraps the body in, so that postcondition assertions the
+ -- ContractPass appends after the body (e.g. return-type `ensures`) remain
+ -- reachable. Jumping straight to `$body` would skip them.
| .Return _ value => do
let stmts ← match value.val with
| some expr => do
@@ -1824,8 +1829,8 @@ partial def translateStmt (ctx : TranslationContext) (s : stmt SourceRange)
-- Coerce Composite return values to Any for LaurelResult : Any
let eRef ← coerceToAny ctx expr eRef
let assign := mkStmtExprMdWithLoc (StmtExpr.Assign [mkVariableMd (.Local PyLauFuncReturnVar)] eRef) md
- .ok $ preamble ++ [assign, mkStmtExprMdWithLoc (StmtExpr.Exit bodyLabel) md]
- | none => .ok [mkStmtExprMdWithLoc (StmtExpr.Exit bodyLabel) md]
+ .ok $ preamble ++ [assign, mkStmtExprMdWithLoc (StmtExpr.Return none) md]
+ | none => .ok [mkStmtExprMdWithLoc (StmtExpr.Return none) md]
return (ctx, stmts)
-- Assert statement
@@ -1921,7 +1926,7 @@ partial def translateStmt (ctx : TranslationContext) (s : stmt SourceRange)
| .Block stmts _ => stmts.any fun s => modifiesMaybeExceptVal s.val
| .IfThenElse _ t e => modifiesMaybeExceptVal t.val ||
(e.map (modifiesMaybeExceptVal ·.val)).getD false
- | .While _ _ _ body => modifiesMaybeExceptVal body.val
+ | .While _ _ _ body postTest => modifiesMaybeExceptVal body.val
| _ => false
let modifiesMaybeExcept (stmt : StmtExprMd) : Bool :=
modifiesMaybeExceptVal stmt.val
@@ -2085,7 +2090,7 @@ partial def translateStmt (ctx : TranslationContext) (s : stmt SourceRange)
let continueBlock := mkStmtExprMd (StmtExpr.Block bodyInner (some continueLabel))
let bodyStmts := [continueBlock, counterIncrease]
let whileBody := mkStmtExprMd (StmtExpr.Block bodyStmts none)
- let loopStmt := mkStmtExprMdWithLoc (StmtExpr.While counterLtLen [] none whileBody) md
+ let loopStmt := mkStmtExprMdWithLoc (StmtExpr.While counterLtLen [] none whileBody false) md
let loopBlock := mkStmtExprMdWithLoc (StmtExpr.Block [loopStmt] (some breakLabel)) md
let (preamble, _) := getExceptionCheckPreamble ctx iterExpr s!"$for_iter_{iter.toAst.ann.start.byteIdx}"
return (finalCtx, iterPreamble ++ preamble ++ [counterDecl] ++ [loopBlock])
@@ -2383,7 +2388,6 @@ def translateFunction (ctx : TranslationContext) (sourceRange: SourceRange) (fun
preconditions := typeConstraintPreconditions
decreases := none
body := bodyTrans
- isFunctional := false
}
return (proc, {newCtx with variableTypes := []})
@@ -2560,7 +2564,6 @@ def mkDefaultInitDecl (className : String) : PythonFunctionDecl × Procedure :=
inputs := inputs
outputs := [{name := "LaurelResult", type := AnyTy}]
preconditions := [{ condition := mkStmtExprMd (StmtExpr.LiteralBool true) }]
- isFunctional := false
decreases := none
body := .Opaque [] .none wildcardModifies
}
@@ -2753,7 +2756,7 @@ def PreludeInfo.ofLaurelProgram (prog : Laurel.Program) : PreludeInfo where
| _ => s
procedures :=
prog.staticProcedures.foldl (init := {}) fun m p =>
- if p.body.isExternal || p.isFunctional then m
+ if p.body.isExternal then m
else
-- Use "Any" for all parameter types to match the Python→Laurel
-- pipeline's Any-wrapping convention at call sites.
@@ -2776,8 +2779,6 @@ def PreludeInfo.ofLaurelProgram (prog : Laurel.Program) : PreludeInfo where
typeTesters := pyLauTypeTesters tys : PyRetInfo }
some { name := p.name.text, args := args, kwargsName := none, ret := ret }
functions :=
- let funcNames := prog.staticProcedures.filterMap fun p =>
- if p.body.isExternal || !p.isFunctional then none else some p.name.text
let dtFuncs := prog.types.flatMap fun td =>
match td with
| .Datatype dt =>
@@ -2789,12 +2790,12 @@ def PreludeInfo.ofLaurelProgram (prog : Laurel.Program) : PreludeInfo where
let testers := dt.constructors.map fun c => "is" ++ c.name.text
ctors ++ destrs ++ testers
| _ => []
- funcNames ++ dtFuncs
+ dtFuncs
maybeExceptionFunctions := prog.staticProcedures.filterMap fun p =>
if p.name.text ∈ AnyMaybeExceptionList then some p.name.text else none
procedureNames :=
prog.staticProcedures.filterMap fun p =>
- if p.body.isExternal || p.isFunctional then none else some p.name.text
+ if p.body.isExternal then none else some p.name.text
callableProcedures :=
prog.staticProcedures.foldl (init := {}) fun s p =>
match p.body with
@@ -2978,7 +2979,6 @@ def pythonToLaurel (info : PreludeInfo)
preconditions := [],
decreases := none,
body := .Opaque [] (some bodyBlock) wildcardModifies
- isFunctional := false
}
-- Generate $composite_to_string_ and $composite_to_string_any_
@@ -2996,16 +2996,14 @@ def pythonToLaurel (info : PreludeInfo)
outputs := [{ name := "result", type := mkHighTypeMd .TString }]
preconditions := []
decreases := none
- body := .Opaque [] none wildcardModifies
- isFunctional := false }
+ body := .Opaque [] none wildcardModifies }
procedures := procedures.push
{ name := { text := compositeToStringAnyName ct.name.text }
inputs := [selfParam]
outputs := [{ name := "result", type := AnyTy }]
preconditions := []
decreases := none
- body := .Opaque [] none wildcardModifies
- isFunctional := false }
+ body := .Opaque [] none wildcardModifies }
let program : Laurel.Program := {
staticProcedures := (procedures.push mainProc).toList
diff --git a/StrataPython/StrataPython/Specs/ToLaurel.lean b/StrataPython/StrataPython/Specs/ToLaurel.lean
index d73b80f5e8..afd546ecb1 100644
--- a/StrataPython/StrataPython/Specs/ToLaurel.lean
+++ b/StrataPython/StrataPython/Specs/ToLaurel.lean
@@ -562,7 +562,6 @@ def funcDeclToLaurel (procName : String) (func : FunctionDecl)
outputs := outputs
preconditions := []
decreases := none
- isFunctional := false
body := body
}
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_datetime_now_tz.expected b/StrataPython/StrataPythonTest/expected_interpret/test_datetime_now_tz.expected
index 4fdbad5449..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_datetime_now_tz.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_datetime_now_tz.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(162\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_float_literal.expected b/StrataPython/StrataPythonTest/expected_interpret/test_float_literal.expected
index e4dbc1f5dd..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_float_literal.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_float_literal.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(36\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_for_range.expected b/StrataPython/StrataPythonTest/expected_interpret/test_for_range.expected
index d7b87db228..5ec230bf2f 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_for_range.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_for_range.expected
@@ -1 +1 @@
-\[ERROR\] assume \(assume\(23\)\) condition did not reduce to bool
+\[ERROR\] assume \(assume\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_fstrings.expected b/StrataPython/StrataPythonTest/expected_interpret/test_fstrings.expected
index bc73866ddc..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_fstrings.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_fstrings.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(196\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_int_pow.expected b/StrataPython/StrataPythonTest/expected_interpret/test_int_pow.expected
index fdfd8eb9f5..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_int_pow.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_int_pow.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(75\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_lshift.expected b/StrataPython/StrataPythonTest/expected_interpret/test_lshift.expected
index aab564ef27..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_lshift.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_lshift.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(51\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_missing_models.expected b/StrataPython/StrataPythonTest/expected_interpret/test_missing_models.expected
index 6d3acd12ef..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_missing_models.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_missing_models.expected
@@ -1 +1 @@
-\[ERROR\] expression contains stuck redex
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_param_reassign_cross_module.expected b/StrataPython/StrataPythonTest/expected_interpret/test_param_reassign_cross_module.expected
index e5bfdd2c78..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_param_reassign_cross_module.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_param_reassign_cross_module.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(59\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_power.expected b/StrataPython/StrataPythonTest/expected_interpret/test_power.expected
index c633e6e0ed..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_power.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_power.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(65\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_regex_negative.expected b/StrataPython/StrataPythonTest/expected_interpret/test_regex_negative.expected
index 4b1cfd8776..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_regex_negative.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_regex_negative.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(272\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_regex_positive.expected b/StrataPython/StrataPythonTest/expected_interpret/test_regex_positive.expected
index 886f2f679d..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_regex_positive.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_regex_positive.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(215\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_rshift.expected b/StrataPython/StrataPythonTest/expected_interpret/test_rshift.expected
index 38def5ef4b..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_rshift.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_rshift.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(52\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_timedelta_expr.expected b/StrataPython/StrataPythonTest/expected_interpret/test_timedelta_expr.expected
index 148c22345a..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_timedelta_expr.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_timedelta_expr.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(140\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_bool_eq.expected b/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_bool_eq.expected
index cdbd192a4f..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_bool_eq.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_bool_eq.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(673\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_float.expected b/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_float.expected
index e6750a00d3..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_float.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_float.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(187\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_not_eq.expected b/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_not_eq.expected
index 3186479c6d..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_not_eq.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_truthiness_not_eq.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(288\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_interpret/test_unsupported_config.expected b/StrataPython/StrataPythonTest/expected_interpret/test_unsupported_config.expected
index f5f86ae44d..e08d8d6b3a 100644
--- a/StrataPython/StrataPythonTest/expected_interpret/test_unsupported_config.expected
+++ b/StrataPython/StrataPythonTest/expected_interpret/test_unsupported_config.expected
@@ -1 +1 @@
-\[ERROR\] assert \(assert\(178\)\) condition did not reduce to bool
+\[ERROR\] assert \(assert\([0-9]+\)\) condition did not reduce to bool
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_any_dict.expected b/StrataPython/StrataPythonTest/expected_laurel/test_any_dict.expected
index 56fc49687d..250cba478e 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_any_dict.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_any_dict.expected
@@ -1,4 +1,4 @@
-test_any_dict.py(5, 4): ✅ pass - assert_assert(71)_calls_Any_get_0
+test_any_dict.py(5, 11): ✅ pass - precondition
test_any_dict.py(5, 4): ✅ pass - Any holds dict
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_any_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_any_list.expected
index af813bcfcd..396b5d165f 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_any_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_any_list.expected
@@ -1,4 +1,4 @@
-test_any_list.py(5, 4): ✅ pass - assert_assert(72)_calls_Any_get_0
+test_any_list.py(5, 11): ✅ pass - precondition
test_any_list.py(5, 4): ✅ pass - Any holds list
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_arithmetic.expected b/StrataPython/StrataPythonTest/expected_laurel/test_arithmetic.expected
index 5d2aac95de..9d339f8e2f 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_arithmetic.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_arithmetic.expected
@@ -14,19 +14,19 @@ test_arithmetic.py(16, 4): ✅ pass - addition implemented incorrectly
test_arithmetic.py(19, 16): ✅ pass - Check PSub exception
test_arithmetic.py(19, 4): ✅ pass - assert(436)
test_arithmetic.py(20, 4): ✅ pass - subtraction implemented incorrectly
-test_arithmetic.py(23, 16): ✅ pass - assert_assert(556)_calls_PFloorDiv_0
+test_arithmetic.py(23, 16): ✅ pass - precondition
test_arithmetic.py(23, 16): ✅ pass - Check PFloorDiv exception
-test_arithmetic.py(23, 4): ✅ pass - set_quot_calls_PFloorDiv_0
+test_arithmetic.py(23, 4): ✅ pass - precondition
test_arithmetic.py(23, 4): ✅ pass - assert(544)
test_arithmetic.py(24, 4): ✅ pass - floor division implemented incorrectly
-test_arithmetic.py(27, 15): ✅ pass - assert_assert(652)_calls_PMod_0
+test_arithmetic.py(27, 15): ✅ pass - precondition
test_arithmetic.py(27, 15): ✅ pass - Check PMod exception
-test_arithmetic.py(27, 4): ✅ pass - set_rem_calls_PMod_0
+test_arithmetic.py(27, 4): ✅ pass - precondition
test_arithmetic.py(27, 4): ✅ pass - assert(641)
test_arithmetic.py(28, 4): ✅ pass - mod implemented incorrectly
-test_arithmetic.py(31, 20): ✅ pass - assert_assert(749)_calls_PMod_0
+test_arithmetic.py(31, 20): ✅ pass - precondition
test_arithmetic.py(31, 20): ✅ pass - Check PMod exception
-test_arithmetic.py(31, 4): ✅ pass - set_neg_rem1_calls_PMod_0
+test_arithmetic.py(31, 4): ✅ pass - precondition
test_arithmetic.py(31, 4): ✅ pass - assert(733)
test_arithmetic.py(32, 4): ✅ pass - negative mod should follow Python floored semantics
DETAIL: 31 passed, 0 failed, 0 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_augadd_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_augadd_list.expected
index b9ee61e68b..b3a378455c 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_augadd_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_augadd_list.expected
@@ -1,7 +1,7 @@
test_augadd_list.py(3, 4): ✅ pass - Check PAdd exception
-test_augadd_list.py(4, 4): ✅ pass - assert_assert(61)_calls_Any_get_0
+test_augadd_list.py(4, 11): ✅ pass - precondition
test_augadd_list.py(4, 4): ✅ pass - augmented add list
-test_augadd_list.py(5, 4): ✅ pass - assert_assert(105)_calls_Any_get_0
+test_augadd_list.py(5, 11): ✅ pass - precondition
test_augadd_list.py(5, 4): ✅ pass - augmented add list last
DETAIL: 5 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_augfloordiv.expected b/StrataPython/StrataPythonTest/expected_laurel/test_augfloordiv.expected
index a5f20ce182..4707c47873 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_augfloordiv.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_augfloordiv.expected
@@ -1,7 +1,6 @@
test_augfloordiv.py(2, 4): ✅ pass - assert(28)
-test_augfloordiv.py(3, 4): ✅ pass - assert_assert(44)_calls_PFloorDiv_0
+test_augfloordiv.py(3, 4): ✅ pass - precondition
test_augfloordiv.py(3, 4): ✅ pass - Check PFloorDiv exception
-test_augfloordiv.py(3, 4): ✅ pass - set_x_calls_PFloorDiv_0
test_augfloordiv.py(4, 4): ✅ pass - augmented floordiv
-DETAIL: 5 passed, 0 failed, 0 inconclusive
+DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_augmented_assign.expected b/StrataPython/StrataPythonTest/expected_laurel/test_augmented_assign.expected
index ad80803bf1..1df585b0c6 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_augmented_assign.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_augmented_assign.expected
@@ -4,10 +4,9 @@ test_augmented_assign.py(6, 4): ✅ pass - Check PSub exception
test_augmented_assign.py(7, 4): ✅ pass - 8 - 2 == 6
test_augmented_assign.py(8, 4): ✅ pass - Check PMul exception
test_augmented_assign.py(9, 4): ✅ pass - 6 * 2 == 12
-test_augmented_assign.py(11, 4): ✅ pass - assert_assert(219)_calls_Any_get_0
+test_augmented_assign.py(11, 4): ✅ pass - precondition
test_augmented_assign.py(11, 4): ✅ pass - Check Any_sets! exception
-test_augmented_assign.py(11, 4): ✅ pass - set_l_calls_Any_get_0
-test_augmented_assign.py(12, 4): ✅ pass - assert_assert(233)_calls_Any_get_0
+test_augmented_assign.py(12, 11): ✅ pass - precondition
test_augmented_assign.py(12, 4): ✅ pass - list element modified
-DETAIL: 11 passed, 0 failed, 0 inconclusive
+DETAIL: 10 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_augmod.expected b/StrataPython/StrataPythonTest/expected_laurel/test_augmod.expected
index c785c8575b..87549640dc 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_augmod.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_augmod.expected
@@ -1,7 +1,6 @@
test_augmod.py(2, 4): ✅ pass - assert(23)
-test_augmod.py(3, 4): ✅ pass - assert_assert(39)_calls_PMod_0
+test_augmod.py(3, 4): ✅ pass - precondition
test_augmod.py(3, 4): ✅ pass - Check PMod exception
-test_augmod.py(3, 4): ✅ pass - set_x_calls_PMod_0
test_augmod.py(4, 4): ✅ pass - augmented mod
-DETAIL: 5 passed, 0 failed, 0 inconclusive
+DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_break_continue.expected b/StrataPython/StrataPythonTest/expected_laurel/test_break_continue.expected
index 1cb5222c41..4f120f15d0 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_break_continue.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_break_continue.expected
@@ -4,9 +4,9 @@ test_break_continue.py(1, 26): ✅ pass - (test_while_break ensures) Return type
test_break_continue.py(7, 4): ✅ pass - assert(129)
test_break_continue.py(8, 10): ✅ pass - Check PNot exception
test_break_continue.py(6, 29): ✅ pass - (test_while_continue ensures) Return type constraint
-test_break_continue.py(14, 4): ✅ pass - assume_assume(267)_calls_PIn_0
+test_break_continue.py(14, 4): ✅ pass - precondition
test_break_continue.py(12, 24): ✅ pass - (test_for_break ensures) Return type constraint
-test_break_continue.py(19, 4): ✅ pass - assume_assume(362)_calls_PIn_0
+test_break_continue.py(19, 4): ✅ pass - precondition
test_break_continue.py(17, 27): ✅ pass - (test_for_continue ensures) Return type constraint
DETAIL: 10 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_bubble_sort_step.expected b/StrataPython/StrataPythonTest/expected_laurel/test_bubble_sort_step.expected
index f3de7b0866..a8516e3f8c 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_bubble_sort_step.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_bubble_sort_step.expected
@@ -1,41 +1,32 @@
-test_bubble_sort_step.py(12, 8): ✅ pass - set_t3_calls_Any_get_0
+test_bubble_sort_step.py(12, 8): ✅ pass - precondition
test_bubble_sort_step.py(12, 8): ✅ pass - assert(250)
-test_bubble_sort_step.py(13, 8): ✅ pass - assert_assert(274)_calls_Any_get_0
+test_bubble_sort_step.py(13, 16): ✅ pass - precondition
test_bubble_sort_step.py(13, 8): ✅ pass - Check Any_sets! exception
-test_bubble_sort_step.py(13, 8): ✅ pass - set_xs_calls_Any_get_0
test_bubble_sort_step.py(14, 8): ✅ pass - Check Any_sets! exception
-test_bubble_sort_step.py(3, 7): ✅ pass - assert_assert(55)_calls_Any_get_0
-test_bubble_sort_step.py(3, 7): ✅ pass - assert_assert(55)_calls_Any_get_1
+test_bubble_sort_step.py(3, 7): ✅ pass - precondition
+test_bubble_sort_step.py(3, 15): ✅ pass - precondition
test_bubble_sort_step.py(3, 7): ✅ pass - Check PGt exception
-test_bubble_sort_step.py(3, 4): ✅ pass - ite_cond_calls_Any_get_0
-test_bubble_sort_step.py(3, 4): ✅ pass - ite_cond_calls_Any_get_1
-test_bubble_sort_step.py(4, 8): ✅ pass - set_t_calls_Any_get_0
+test_bubble_sort_step.py(4, 8): ✅ pass - precondition
test_bubble_sort_step.py(4, 8): ✅ pass - assert(78)
-test_bubble_sort_step.py(5, 8): ✅ pass - assert_assert(101)_calls_Any_get_0
+test_bubble_sort_step.py(5, 16): ✅ pass - precondition
test_bubble_sort_step.py(5, 8): ✅ pass - Check Any_sets! exception
-test_bubble_sort_step.py(5, 8): ✅ pass - set_xs_calls_Any_get_0
test_bubble_sort_step.py(6, 8): ✅ pass - Check Any_sets! exception
-test_bubble_sort_step.py(7, 7): ✅ pass - assert_assert(140)_calls_Any_get_0
-test_bubble_sort_step.py(7, 7): ✅ pass - assert_assert(140)_calls_Any_get_1
+test_bubble_sort_step.py(7, 7): ✅ pass - precondition
+test_bubble_sort_step.py(7, 15): ✅ pass - precondition
test_bubble_sort_step.py(7, 7): ✅ pass - Check PGt exception
-test_bubble_sort_step.py(7, 4): ✅ pass - ite_cond_calls_Any_get_0
-test_bubble_sort_step.py(7, 4): ✅ pass - ite_cond_calls_Any_get_1
-test_bubble_sort_step.py(8, 8): ✅ pass - set_t2_calls_Any_get_0
+test_bubble_sort_step.py(8, 8): ✅ pass - precondition
test_bubble_sort_step.py(8, 8): ✅ pass - assert(163)
-test_bubble_sort_step.py(9, 8): ✅ pass - assert_assert(187)_calls_Any_get_0
+test_bubble_sort_step.py(9, 16): ✅ pass - precondition
test_bubble_sort_step.py(9, 8): ✅ pass - Check Any_sets! exception
-test_bubble_sort_step.py(9, 8): ✅ pass - set_xs_calls_Any_get_0
test_bubble_sort_step.py(10, 8): ✅ pass - Check Any_sets! exception
-test_bubble_sort_step.py(11, 7): ✅ pass - assert_assert(227)_calls_Any_get_0
-test_bubble_sort_step.py(11, 7): ✅ pass - assert_assert(227)_calls_Any_get_1
+test_bubble_sort_step.py(11, 7): ✅ pass - precondition
+test_bubble_sort_step.py(11, 15): ✅ pass - precondition
test_bubble_sort_step.py(11, 7): ✅ pass - Check PGt exception
-test_bubble_sort_step.py(11, 4): ✅ pass - ite_cond_calls_Any_get_0
-test_bubble_sort_step.py(11, 4): ✅ pass - ite_cond_calls_Any_get_1
-test_bubble_sort_step.py(15, 4): ✅ pass - assert_assert(311)_calls_Any_get_0
+test_bubble_sort_step.py(15, 11): ✅ pass - precondition
test_bubble_sort_step.py(15, 4): ✅ pass - sorted first
-test_bubble_sort_step.py(16, 4): ✅ pass - assert_assert(349)_calls_Any_get_0
+test_bubble_sort_step.py(16, 11): ✅ pass - precondition
test_bubble_sort_step.py(16, 4): ✅ pass - sorted second
-test_bubble_sort_step.py(17, 4): ✅ pass - assert_assert(388)_calls_Any_get_0
+test_bubble_sort_step.py(17, 11): ✅ pass - precondition
test_bubble_sort_step.py(17, 4): ✅ pass - sorted third
-DETAIL: 39 passed, 0 failed, 0 inconclusive
+DETAIL: 30 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_class_field_use.expected b/StrataPython/StrataPythonTest/expected_laurel/test_class_field_use.expected
index 29a689ea2c..336fdaa29a 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_class_field_use.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_class_field_use.expected
@@ -1,6 +1,7 @@
test_class_field_use.py(13, 4): ✔️ always true if reached - (CircularBuffer@__init__ requires) Type constraint of n
test_class_field_use.py(14, 4): ✔️ always true if reached - Check PMul exception
+test_class_field_use.py(14, 4): ✔️ always true if reached - (process_buffer ensures) Return type constraint
test_class_field_use.py(14, 4): ✔️ always true if reached - assert(302)
test_class_field_use.py(15, 4): ✔️ always true if reached - Doubling of buffer did not work
-DETAIL: 4 passed, 0 failed, 0 inconclusive
+DETAIL: 5 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_class_methods.expected b/StrataPython/StrataPythonTest/expected_laurel/test_class_methods.expected
index f0601e776e..10409e39e1 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_class_methods.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_class_methods.expected
@@ -1,11 +1,15 @@
test_class_methods.py(34, 4): ✔️ always true if reached - (Account@__init__ requires) Type constraint of owner
test_class_methods.py(34, 4): ✔️ always true if reached - (Account@__init__ requires) Type constraint of balance
-test_class_methods.py(34, 4): ✔️ always true if reached - main_assert(471)_32
+test_class_methods.py(21, 4): ✔️ always true if reached - (Account@get_owner ensures) Return type constraint
+test_class_methods.py(34, 4): ✔️ always true if reached - main_assert(471)_31
test_class_methods.py(34, 4): ✔️ always true if reached - get_owner should return Alice
-test_class_methods.py(34, 4): ✔️ always true if reached - main_assert(564)_35
+test_class_methods.py(24, 4): ✔️ always true if reached - (Account@get_balance ensures) Return type constraint
+test_class_methods.py(34, 4): ✔️ always true if reached - main_assert(564)_34
test_class_methods.py(34, 4): ✔️ always true if reached - get_balance should return 100
test_class_methods.py(34, 4): ✔️ always true if reached - (Account@set_balance requires) Type constraint of amount
-test_class_methods.py(34, 4): ✔️ always true if reached - main_assert(678)_40
+test_class_methods.py(27, 4): ✔️ always true if reached - (Account@set_balance ensures) Return type constraint
+test_class_methods.py(28, 4): ✔️ always true if reached - (Account@get_balance ensures) Return type constraint
+test_class_methods.py(34, 4): ✔️ always true if reached - main_assert(678)_39
test_class_methods.py(34, 4): ✔️ always true if reached - set_balance should update balance
test_class_methods.py(34, 4): ✔️ always true if reached - (Origin_test_helper_procedure_Requires)req_name_is_foo
test_class_methods.py(34, 4): ✔️ always true if reached - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
@@ -13,5 +17,6 @@ test_class_methods.py(34, 4): ✔️ always true if reached - (Origin_test_helpe
test_class_methods.py(31, 4): ✔️ always true if reached - assert_name_is_foo
test_class_methods.py(31, 4): ✔️ always true if reached - assert_opt_name_none_or_str
test_class_methods.py(31, 4): ✔️ always true if reached - assert_opt_name_none_or_bar
-DETAIL: 15 passed, 0 failed, 0 inconclusive
+test_class_methods.py(31, 4): ✔️ always true if reached - ensures_maybe_except_none
+DETAIL: 20 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_class_with_methods.expected b/StrataPython/StrataPythonTest/expected_laurel/test_class_with_methods.expected
index 3f5ddaf665..b7cc5e6e97 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_class_with_methods.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_class_with_methods.expected
@@ -1,9 +1,13 @@
test_class_with_methods.py(32, 4): ✔️ always true if reached - (DataStore@__init__ requires) Type constraint of name
test_class_with_methods.py(32, 4): ✔️ always true if reached - (DataStore@add requires) Type constraint of amount
+test_class_with_methods.py(20, 4): ✔️ always true if reached - (DataStore@add ensures) Return type constraint
test_class_with_methods.py(32, 4): ✔️ always true if reached - (DataStore@add requires) Type constraint of amount
-test_class_with_methods.py(32, 4): ✔️ always true if reached - main_assert(484)_34
+test_class_with_methods.py(21, 4): ✔️ always true if reached - (DataStore@add ensures) Return type constraint
+test_class_with_methods.py(23, 4): ✔️ always true if reached - (DataStore@get_count ensures) Return type constraint
+test_class_with_methods.py(32, 4): ✔️ always true if reached - main_assert(484)_33
test_class_with_methods.py(32, 4): ✔️ always true if reached - get_count should return 30
-test_class_with_methods.py(32, 4): ✔️ always true if reached - main_assert(569)_37
+test_class_with_methods.py(26, 4): ✔️ always true if reached - (DataStore@get_name ensures) Return type constraint
+test_class_with_methods.py(32, 4): ✔️ always true if reached - main_assert(569)_36
test_class_with_methods.py(32, 4): ✔️ always true if reached - get_name should return mystore
test_class_with_methods.py(32, 4): ✔️ always true if reached - (Origin_test_helper_procedure_Requires)req_name_is_foo
test_class_with_methods.py(32, 4): ✔️ always true if reached - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
@@ -11,5 +15,6 @@ test_class_with_methods.py(32, 4): ✔️ always true if reached - (Origin_test_
test_class_with_methods.py(29, 4): ✔️ always true if reached - assert_name_is_foo
test_class_with_methods.py(29, 4): ✔️ always true if reached - assert_opt_name_none_or_str
test_class_with_methods.py(29, 4): ✔️ always true if reached - assert_opt_name_none_or_bar
-DETAIL: 13 passed, 0 failed, 0 inconclusive
+test_class_with_methods.py(29, 4): ✔️ always true if reached - ensures_maybe_except_none
+DETAIL: 18 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_coerce_int_in_any_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_coerce_int_in_any_list.expected
index 26134635da..f92d9c87a5 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_coerce_int_in_any_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_coerce_int_in_any_list.expected
@@ -1,6 +1,6 @@
-test_coerce_int_in_any_list.py(5, 4): ✅ pass - assert_assert(103)_calls_Any_get_0
+test_coerce_int_in_any_list.py(5, 11): ✅ pass - precondition
test_coerce_int_in_any_list.py(5, 4): ✅ pass - int in Any list
-test_coerce_int_in_any_list.py(6, 4): ✅ pass - assert_assert(144)_calls_Any_get_0
+test_coerce_int_in_any_list.py(6, 11): ✅ pass - precondition
test_coerce_int_in_any_list.py(6, 4): ✅ pass - str in Any list
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_deep_inline.expected b/StrataPython/StrataPythonTest/expected_laurel/test_deep_inline.expected
index bc3395d77d..328bc0e317 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_deep_inline.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_deep_inline.expected
@@ -2,14 +2,18 @@ test_deep_inline.py(21, 4): ✔️ always true if reached - (triple_apply requir
test_deep_inline.py(15, 4): ✔️ always true if reached - (double_inc requires) Type constraint of x
test_deep_inline.py(10, 4): ✔️ always true if reached - (inc requires) Type constraint of x
test_deep_inline.py(6, 4): ✔️ always true if reached - Check PAdd exception
-test_deep_inline.py(10, 4): ✔️ always true if reached - double_inc_assert(135)_54
+test_deep_inline.py(6, 4): ✔️ always true if reached - (inc ensures) Return type constraint
+test_deep_inline.py(10, 4): ✔️ always true if reached - double_inc_assert(135)_58
test_deep_inline.py(10, 4): ✔️ always true if reached - Check PMul exception
-test_deep_inline.py(15, 4): ✔️ always true if reached - triple_apply_assert(206)_27
+test_deep_inline.py(10, 4): ✔️ always true if reached - (double_inc ensures) Return type constraint
+test_deep_inline.py(15, 4): ✔️ always true if reached - triple_apply_assert(206)_28
test_deep_inline.py(15, 4): ✔️ always true if reached - (inc requires) Type constraint of x
test_deep_inline.py(11, 4): ✔️ always true if reached - Check PAdd exception
-test_deep_inline.py(15, 4): ✔️ always true if reached - triple_apply_assert(233)_30
-test_deep_inline.py(21, 4): ✔️ always true if reached - main_assert(279)_9
+test_deep_inline.py(11, 4): ✔️ always true if reached - (inc ensures) Return type constraint
+test_deep_inline.py(15, 4): ✔️ always true if reached - triple_apply_assert(233)_31
+test_deep_inline.py(15, 4): ✔️ always true if reached - (triple_apply ensures) Return type constraint
+test_deep_inline.py(21, 4): ✔️ always true if reached - main_assert(279)_8
test_deep_inline.py(21, 4): ✔️ always true if reached - triple_apply(3) should be 9
test_deep_inline.py(21, 4): ✖️ always false if reached - triple_apply(3) should not be 10
-DETAIL: 12 passed, 1 failed, 0 inconclusive
+DETAIL: 16 passed, 1 failed, 0 inconclusive
RESULT: Failures found
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_deeply_nested_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_deeply_nested_list.expected
index 1cc971115e..c98eb1a647 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_deeply_nested_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_deeply_nested_list.expected
@@ -1,6 +1,6 @@
-test_deeply_nested_list.py(3, 4): ✅ pass - assert_assert(33)_calls_Any_get_0
-test_deeply_nested_list.py(3, 4): ✅ pass - assert_assert(33)_calls_Any_get_1
-test_deeply_nested_list.py(3, 4): ✅ pass - assert_assert(33)_calls_Any_get_2
+test_deeply_nested_list.py(3, 11): ✅ pass - precondition
+test_deeply_nested_list.py(3, 11): ✅ pass - precondition
+test_deeply_nested_list.py(3, 11): ✅ pass - precondition
test_deeply_nested_list.py(3, 4): ✅ pass - triple nested list
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_add_key.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_add_key.expected
index fe764d0f3f..5ec9ce4b73 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_add_key.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_add_key.expected
@@ -1,5 +1,5 @@
test_dict_add_key.py(3, 4): ✅ pass - Check Any_sets! exception
-test_dict_add_key.py(4, 4): ✅ pass - assert_assert(56)_calls_Any_get_0
+test_dict_add_key.py(4, 11): ✅ pass - precondition
test_dict_add_key.py(4, 4): ✅ pass - dict add key
DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_assign.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_assign.expected
index 3c6fafeb0a..f7c7754049 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_assign.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_assign.expected
@@ -1,5 +1,5 @@
test_dict_assign.py(3, 4): ✅ pass - Check Any_sets! exception
-test_dict_assign.py(4, 4): ✅ pass - assert_assert(62)_calls_Any_get_0
+test_dict_assign.py(4, 11): ✅ pass - precondition
test_dict_assign.py(4, 4): ✅ pass - dict update
DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_create.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_create.expected
index 5613842891..2f04dc4c1b 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_create.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_create.expected
@@ -1,6 +1,6 @@
-test_dict_create.py(3, 4): ✅ pass - assert_assert(53)_calls_Any_get_0
+test_dict_create.py(3, 11): ✅ pass - precondition
test_dict_create.py(3, 4): ✅ pass - dict access
-test_dict_create.py(4, 4): ✅ pass - assert_assert(91)_calls_Any_get_0
+test_dict_create.py(4, 11): ✅ pass - precondition
test_dict_create.py(4, 4): ✅ pass - dict access b
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_in.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_in.expected
index 57ab802313..b00542f78d 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_in.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_in.expected
@@ -1,6 +1,6 @@
-test_dict_in.py(3, 4): ✅ pass - assert_assert(49)_calls_PIn_0
+test_dict_in.py(3, 11): ✅ pass - precondition
test_dict_in.py(3, 4): ✅ pass - key in dict
-test_dict_in.py(4, 4): ✅ pass - assert_assert(84)_calls_PNotIn_0
+test_dict_in.py(4, 11): ✅ pass - precondition
test_dict_in.py(4, 4): ✅ pass - key not in dict
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_of_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_of_list.expected
index 7fc8fa186e..0c84934ca7 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_of_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_of_list.expected
@@ -1,5 +1,5 @@
-test_dict_of_list.py(5, 4): ✅ pass - assert_assert(91)_calls_Any_get_0
-test_dict_of_list.py(5, 4): ✅ pass - assert_assert(91)_calls_Any_get_1
+test_dict_of_list.py(5, 11): ✅ pass - precondition
+test_dict_of_list.py(5, 11): ✅ pass - precondition
test_dict_of_list.py(5, 4): ✅ pass - dict of list
DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_operations.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_operations.expected
index 98c3037b20..a13c5f395f 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_operations.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_operations.expected
@@ -1,26 +1,26 @@
-test_dict_operations.py(7, 0): ✅ pass - assert_assert(81)_calls_Any_get_0
+test_dict_operations.py(7, 7): ✅ pass - precondition
test_dict_operations.py(7, 0): ✅ pass - assert(81)
-test_dict_operations.py(8, 0): ✅ pass - assert_assert(118)_calls_Any_get_0
+test_dict_operations.py(8, 7): ✅ pass - precondition
test_dict_operations.py(8, 0): ✅ pass - assert(118)
test_dict_operations.py(10, 0): ✅ pass - Check Any_sets! exception
-test_dict_operations.py(11, 0): ✅ pass - assert_assert(172)_calls_Any_get_0
+test_dict_operations.py(11, 7): ✅ pass - precondition
test_dict_operations.py(11, 0): ✅ pass - assert(172)
-test_dict_operations.py(13, 0): ✅ pass - assert_assert(204)_calls_PIn_0
+test_dict_operations.py(13, 7): ✅ pass - precondition
test_dict_operations.py(13, 0): ✅ pass - assert(204)
-test_dict_operations.py(14, 0): ✅ pass - assert_assert(228)_calls_PNotIn_0
+test_dict_operations.py(14, 7): ✅ pass - precondition
test_dict_operations.py(14, 0): ✅ pass - assert(228)
-test_dict_operations.py(23, 0): ✅ pass - assert_assert(403)_calls_Any_get_0
-test_dict_operations.py(23, 0): ✅ pass - assert_assert(403)_calls_Any_get_1
-test_dict_operations.py(23, 0): ✅ pass - assert_assert(403)_calls_Any_get_2
+test_dict_operations.py(23, 7): ✅ pass - precondition
+test_dict_operations.py(23, 7): ✅ pass - precondition
+test_dict_operations.py(23, 7): ✅ pass - precondition
test_dict_operations.py(23, 0): ✅ pass - assert(403)
-test_dict_operations.py(24, 0): ✅ pass - assert_assert(457)_calls_Any_get_0
-test_dict_operations.py(24, 0): ✅ pass - assert_assert(457)_calls_Any_get_1
-test_dict_operations.py(24, 0): ✅ pass - assert_assert(457)_calls_Any_get_2
+test_dict_operations.py(24, 7): ✅ pass - precondition
+test_dict_operations.py(24, 7): ✅ pass - precondition
+test_dict_operations.py(24, 7): ✅ pass - precondition
test_dict_operations.py(24, 0): ✅ pass - assert(457)
test_dict_operations.py(26, 0): ✅ pass - Check Any_sets! exception
-test_dict_operations.py(27, 0): ✅ pass - assert_assert(557)_calls_Any_get_0
-test_dict_operations.py(27, 0): ✅ pass - assert_assert(557)_calls_Any_get_1
-test_dict_operations.py(27, 0): ✅ pass - assert_assert(557)_calls_Any_get_2
+test_dict_operations.py(27, 7): ✅ pass - precondition
+test_dict_operations.py(27, 7): ✅ pass - precondition
+test_dict_operations.py(27, 7): ✅ pass - precondition
test_dict_operations.py(27, 0): ✅ pass - assert(557)
DETAIL: 24 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_dict_overwrite.expected b/StrataPython/StrataPythonTest/expected_laurel/test_dict_overwrite.expected
index a326876db0..48261dcb07 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_dict_overwrite.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_dict_overwrite.expected
@@ -1,6 +1,6 @@
test_dict_overwrite.py(3, 4): ✅ pass - Check Any_sets! exception
test_dict_overwrite.py(4, 4): ✅ pass - Check Any_sets! exception
-test_dict_overwrite.py(5, 4): ✅ pass - assert_assert(78)_calls_Any_get_0
+test_dict_overwrite.py(5, 11): ✅ pass - precondition
test_dict_overwrite.py(5, 4): ✅ pass - dict overwrite
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_empty_dict_access.expected b/StrataPython/StrataPythonTest/expected_laurel/test_empty_dict_access.expected
index cb7f5dfa7d..9556c1006e 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_empty_dict_access.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_empty_dict_access.expected
@@ -1,6 +1,6 @@
-test_empty_dict_access.py(5, 8): ✅ pass - set_r_calls_Any_get_0
+test_empty_dict_access.py(5, 8): ✅ pass - precondition
test_empty_dict_access.py(3, 4): ✅ pass - assert(33)
-test_empty_dict_access.py(4, 4): ✅ pass - ite_cond_calls_PIn_0
+test_empty_dict_access.py(4, 7): ✅ pass - precondition
test_empty_dict_access.py(7, 12): ✅ pass - Check PNeg exception
test_empty_dict_access.py(8, 16): ✅ pass - Check PNeg exception
test_empty_dict_access.py(8, 4): ✅ pass - missing key guarded
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_flag_pattern.expected b/StrataPython/StrataPythonTest/expected_laurel/test_flag_pattern.expected
index 1ae36f0f29..5d9741fddb 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_flag_pattern.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_flag_pattern.expected
@@ -1,9 +1,8 @@
test_flag_pattern.py(3, 4): ✅ pass - assert(54)
-test_flag_pattern.py(4, 4): ✅ pass - assume_assume(81)_calls_PIn_0
-test_flag_pattern.py(5, 11): ✅ pass - assert_assert(108)_calls_PMod_0
+test_flag_pattern.py(4, 4): ✅ pass - precondition
+test_flag_pattern.py(5, 11): ✅ pass - precondition
test_flag_pattern.py(5, 11): ✅ pass - Check PMod exception
-test_flag_pattern.py(5, 8): ✅ pass - ite_cond_calls_PMod_0
test_flag_pattern.py(7, 11): ❓ unknown - Check PNot exception
test_flag_pattern.py(7, 4): ❓ unknown - no even numbers
-DETAIL: 5 passed, 0 failed, 2 inconclusive
+DETAIL: 4 passed, 0 failed, 2 inconclusive
RESULT: Inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_for_continue_advance.expected b/StrataPython/StrataPythonTest/expected_laurel/test_for_continue_advance.expected
index 338b2c842c..5bd62094b8 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_for_continue_advance.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_for_continue_advance.expected
@@ -1,5 +1,5 @@
test_for_continue_advance.py(3, 4): ✅ pass - assert(72)
-test_for_continue_advance.py(4, 4): ✅ pass - assume_assume(87)_calls_PIn_0
+test_for_continue_advance.py(4, 4): ✅ pass - precondition
test_for_continue_advance.py(1, 35): ✅ pass - (test_for_continue_advance ensures) Return type constraint
test_for_continue_advance.py(7, 12): ❓ unknown - Check PAdd exception
DETAIL: 3 passed, 0 failed, 1 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_for_else_break.expected b/StrataPython/StrataPythonTest/expected_laurel/test_for_else_break.expected
index 5d1dcabdd9..480b1225ab 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_for_else_break.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_for_else_break.expected
@@ -1,5 +1,5 @@
test_for_else_break.py(2, 4): ✅ pass - assert(31)
-test_for_else_break.py(3, 4): ✅ pass - assume_assume(46)_calls_PIn_0
+test_for_else_break.py(3, 4): ✅ pass - precondition
test_for_else_break.py(8, 4): ✅ pass - for else skipped on break
DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_for_loop.expected b/StrataPython/StrataPythonTest/expected_laurel/test_for_loop.expected
index 77a760ea8f..6a4e916b20 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_for_loop.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_for_loop.expected
@@ -1,14 +1,14 @@
test_for_loop.py(3, 4): ✅ pass - assert(64)
-test_for_loop.py(4, 4): ✅ pass - assume_assume(83)_calls_PIn_0
+test_for_loop.py(4, 4): ✅ pass - precondition
test_for_loop.py(5, 16): ❓ unknown - Check PAdd exception
test_for_loop.py(6, 4): ❓ unknown - sum of list should be 15
test_for_loop.py(11, 4): ✅ pass - assert(274)
-test_for_loop.py(12, 4): ✅ pass - assume_assume(293)_calls_PIn_0
+test_for_loop.py(12, 4): ✅ pass - precondition
test_for_loop.py(13, 11): ✅ pass - Check PGt exception
test_for_loop.py(14, 20): ❓ unknown - Check PAdd exception
test_for_loop.py(15, 4): ❓ unknown - should count 3 items greater than 3
test_for_loop.py(20, 4): ✅ pass - assert(512)
-test_for_loop.py(21, 4): ✅ pass - assume_assume(531)_calls_PIn_0
+test_for_loop.py(21, 4): ✅ pass - precondition
test_for_loop.py(25, 4): ❓ unknown (pass on 1 path, unknown on 2 paths) - should have found 30
DETAIL: 7 passed, 0 failed, 5 inconclusive
RESULT: Inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_for_range.expected b/StrataPython/StrataPythonTest/expected_laurel/test_for_range.expected
index 03b0272495..6807df3ff6 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_for_range.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_for_range.expected
@@ -1,10 +1,10 @@
-test_for_range.py(10, 0): ✅ pass - set_i_calls_range_0
-test_for_range.py(3, 0): ✅ pass - set_i_calls_range_0
+test_for_range.py(10, 9): ✅ pass - precondition
+test_for_range.py(3, 9): ✅ pass - precondition
test_for_range.py(4, 11): ✅ pass - Check PLt exception
test_for_range.py(4, 4): ✅ pass - assert(46)
test_for_range.py(5, 11): ✅ pass - Check PGe exception
test_for_range.py(5, 4): ✅ pass - assert(63)
-test_for_range.py(6, 4): ✅ pass - set_j_calls_Any_get_0
+test_for_range.py(6, 4): ✅ pass - precondition
test_for_range.py(7, 11): ✅ pass - Check PLt exception
test_for_range.py(7, 4): ✅ pass - assert(101)
test_for_range.py(10, 15): ✅ pass - Check PNeg exception
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_func_input_type_constraints.expected b/StrataPython/StrataPythonTest/expected_laurel/test_func_input_type_constraints.expected
index 014be579f7..434c1ce1f6 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_func_input_type_constraints.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_func_input_type_constraints.expected
@@ -2,8 +2,8 @@ test_func_input_type_constraints.py(4, 11): ✅ pass - Check PMul exception
test_func_input_type_constraints.py(3, 48): ✅ pass - (Mul ensures) Return type constraint
test_func_input_type_constraints.py(6, 62): ✅ pass - (Sum ensures) Return type constraint
test_func_input_type_constraints.py(9, 11): ✅ pass - Check PAdd exception
-test_func_input_type_constraints.py(12, 4): ❓ unknown - set_LaurelResult_calls_Any_get_0
-test_func_input_type_constraints.py(12, 4): ❓ unknown - set_LaurelResult_calls_Any_get_1
+test_func_input_type_constraints.py(12, 11): ❓ unknown - precondition
+test_func_input_type_constraints.py(12, 4): ❓ unknown - precondition
test_func_input_type_constraints.py(11, 65): ❓ unknown - (List_Dict_index ensures) Return type constraint
test_func_input_type_constraints.py(15, 0): ✅ pass - (Mul requires) Type constraint of x
test_func_input_type_constraints.py(15, 0): ✅ pass - (Mul requires) Type constraint of y
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_havoc_callee_after_hole_call.expected b/StrataPython/StrataPythonTest/expected_laurel/test_havoc_callee_after_hole_call.expected
index 85efc6288d..5a6034434d 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_havoc_callee_after_hole_call.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_havoc_callee_after_hole_call.expected
@@ -5,7 +5,7 @@ test_havoc_callee_after_hole_call.py(20, 0): ✔️ always true if reached - unr
test_havoc_callee_after_hole_call.py(22, 0): ✔️ always true if reached - (MyClass@__init__ requires) Type constraint of n
test_havoc_callee_after_hole_call.py(25, 0): ✔️ always true if reached - composite arg: heap not havocked (out of scope)
test_havoc_callee_after_hole_call.py(30, 0): ❓ unknown - expected unknown because argument locals should be havocked
-test_havoc_callee_after_hole_call.py(36, 0): ❓ unknown - assume_assume(1193)_calls_PIn_0
+test_havoc_callee_after_hole_call.py(36, 0): ❓ unknown - precondition
test_havoc_callee_after_hole_call.py(37, 4): ✔️ always true if reached - for-loop over unmodeled iterator should not crash
DETAIL: 5 passed, 0 failed, 4 inconclusive
RESULT: Inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_int_floordiv.expected b/StrataPython/StrataPythonTest/expected_laurel/test_int_floordiv.expected
index a62d7083eb..7bce6b7cd0 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_int_floordiv.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_int_floordiv.expected
@@ -1,8 +1,8 @@
test_int_floordiv.py(2, 4): ✅ pass - assert(29)
test_int_floordiv.py(3, 4): ✅ pass - assert(45)
-test_int_floordiv.py(4, 13): ✅ pass - assert_assert(69)_calls_PFloorDiv_0
+test_int_floordiv.py(4, 13): ✅ pass - precondition
test_int_floordiv.py(4, 13): ✅ pass - Check PFloorDiv exception
-test_int_floordiv.py(4, 4): ✅ pass - set_c_calls_PFloorDiv_0
+test_int_floordiv.py(4, 4): ✅ pass - precondition
test_int_floordiv.py(4, 4): ✅ pass - assert(60)
test_int_floordiv.py(5, 4): ✅ pass - int floor division
DETAIL: 7 passed, 0 failed, 0 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_int_mod.expected b/StrataPython/StrataPythonTest/expected_laurel/test_int_mod.expected
index 1a2a0276e6..c93755257b 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_int_mod.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_int_mod.expected
@@ -1,8 +1,8 @@
test_int_mod.py(2, 4): ✅ pass - assert(24)
test_int_mod.py(3, 4): ✅ pass - assert(40)
-test_int_mod.py(4, 13): ✅ pass - assert_assert(64)_calls_PMod_0
+test_int_mod.py(4, 13): ✅ pass - precondition
test_int_mod.py(4, 13): ✅ pass - Check PMod exception
-test_int_mod.py(4, 4): ✅ pass - set_c_calls_PMod_0
+test_int_mod.py(4, 4): ✅ pass - precondition
test_int_mod.py(4, 4): ✅ pass - assert(55)
test_int_mod.py(5, 4): ✅ pass - int modulo
DETAIL: 7 passed, 0 failed, 0 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_floordiv.expected b/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_floordiv.expected
index 527ca97690..e723a2ed74 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_floordiv.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_floordiv.expected
@@ -1,7 +1,6 @@
-test_int_negative_floordiv.py(2, 11): ✅ pass - assert_assert(45)_calls_PFloorDiv_0
+test_int_negative_floordiv.py(2, 11): ✅ pass - precondition
test_int_negative_floordiv.py(2, 11): ✅ pass - Check PFloorDiv exception
test_int_negative_floordiv.py(2, 24): ✅ pass - Check PNeg exception
-test_int_negative_floordiv.py(2, 4): ✅ pass - assert_assert(38)_calls_PFloorDiv_0
test_int_negative_floordiv.py(2, 4): ✅ pass - floor division rounds toward negative infinity
-DETAIL: 5 passed, 0 failed, 0 inconclusive
+DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_mod.expected b/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_mod.expected
index 6f8a8fbc25..e14ddf7e30 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_mod.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_int_negative_mod.expected
@@ -1,6 +1,5 @@
-test_int_negative_mod.py(2, 11): ✅ pass - assert_assert(40)_calls_PMod_0
+test_int_negative_mod.py(2, 11): ✅ pass - precondition
test_int_negative_mod.py(2, 11): ✅ pass - Check PMod exception
-test_int_negative_mod.py(2, 4): ✅ pass - assert_assert(33)_calls_PMod_0
test_int_negative_mod.py(2, 4): ✅ pass - python mod is always non-negative for positive divisor
-DETAIL: 4 passed, 0 failed, 0 inconclusive
+DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list.expected
index f86f7b60d6..ff3f01e3d8 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list.expected
@@ -1,23 +1,21 @@
-test_list.py(3, 0): ✅ pass - assert_assert(32)_calls_PIn_0
+test_list.py(3, 7): ✅ pass - precondition
test_list.py(3, 0): ✅ pass - assert(32)
-test_list.py(5, 0): ✅ pass - set_n_calls_Any_get_0
+test_list.py(5, 0): ✅ pass - precondition
test_list.py(7, 0): ✅ pass - assert(71)
-test_list.py(9, 0): ✅ pass - assert_assert(87)_calls_Any_get_0
+test_list.py(9, 7): ✅ pass - precondition
test_list.py(9, 0): ✅ pass - assert(87)
-test_list.py(11, 0): ✅ pass - assert_assert(113)_calls_Any_get_0
+test_list.py(11, 7): ✅ pass - precondition
test_list.py(11, 0): ✅ pass - assert(113)
test_list.py(13, 0): ✅ pass - Check Any_sets! exception
-test_list.py(15, 0): ✅ pass - assert_assert(158)_calls_Any_get_0
+test_list.py(15, 7): ✅ pass - precondition
test_list.py(15, 0): ✅ pass - assert(158)
test_list.py(19, 10): ✅ pass - Check PAdd exception
-test_list.py(21, 0): ✅ pass - assert_assert(250)_calls_Any_get_0
+test_list.py(21, 7): ✅ pass - precondition
test_list.py(21, 0): ✅ pass - assert(250)
test_list.py(23, 0): ✅ pass - Check Any_sets! exception
-test_list.py(25, 7): ✅ pass - assert_assert(305)_calls_Any_get_0
-test_list.py(25, 7): ✅ pass - assert_assert(305)_calls_Any_get_1
+test_list.py(25, 7): ✅ pass - precondition
+test_list.py(25, 20): ✅ pass - precondition
test_list.py(25, 7): ✅ pass - Check PAdd exception
-test_list.py(25, 0): ✅ pass - assert_assert(298)_calls_Any_get_0
-test_list.py(25, 0): ✅ pass - assert_assert(298)_calls_Any_get_1
test_list.py(25, 0): ✅ pass - assert(298)
-DETAIL: 21 passed, 0 failed, 0 inconclusive
+DETAIL: 19 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_assign.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_assign.expected
index 032ca9ce08..6bd8d888f6 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_assign.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_assign.expected
@@ -1,5 +1,5 @@
test_list_assign.py(3, 4): ✅ pass - Check Any_sets! exception
-test_list_assign.py(4, 4): ✅ pass - assert_assert(62)_calls_Any_get_0
+test_list_assign.py(4, 11): ✅ pass - precondition
test_list_assign.py(4, 4): ✅ pass - list element assignment
DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_concat.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_concat.expected
index bfcfa2f91d..c32762406c 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_concat.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_concat.expected
@@ -1,7 +1,7 @@
test_list_concat.py(4, 8): ✅ pass - Check PAdd exception
-test_list_concat.py(5, 4): ✅ pass - assert_assert(72)_calls_Any_get_0
+test_list_concat.py(5, 11): ✅ pass - precondition
test_list_concat.py(5, 4): ✅ pass - first
-test_list_concat.py(6, 4): ✅ pass - assert_assert(102)_calls_Any_get_0
+test_list_concat.py(6, 11): ✅ pass - precondition
test_list_concat.py(6, 4): ✅ pass - last
DETAIL: 5 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_create.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_create.expected
index 5ff4e591a6..1d405dd67b 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_create.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_create.expected
@@ -1,6 +1,6 @@
-test_list_create.py(3, 4): ✅ pass - assert_assert(47)_calls_Any_get_0
+test_list_create.py(3, 11): ✅ pass - precondition
test_list_create.py(3, 4): ✅ pass - first element
-test_list_create.py(4, 4): ✅ pass - assert_assert(86)_calls_Any_get_0
+test_list_create.py(4, 11): ✅ pass - precondition
test_list_create.py(4, 4): ✅ pass - last element
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_empty.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_empty.expected
index 3c2df0a452..84c8f55f2c 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_empty.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_empty.expected
@@ -1,5 +1,5 @@
test_list_empty.py(3, 4): ✅ pass - assert(39)
-test_list_empty.py(4, 4): ✅ pass - assume_assume(58)_calls_PIn_0
+test_list_empty.py(4, 4): ✅ pass - precondition
test_list_empty.py(5, 16): ✅ pass - Check PAdd exception
test_list_empty.py(6, 4): ✅ pass - empty list
DETAIL: 4 passed, 0 failed, 0 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_in.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_in.expected
index de531eb5d5..22ca9da73b 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_in.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_in.expected
@@ -1,6 +1,6 @@
-test_list_in.py(3, 4): ✅ pass - assert_assert(49)_calls_PIn_0
+test_list_in.py(3, 11): ✅ pass - precondition
test_list_in.py(3, 4): ✅ pass - element in list
-test_list_in.py(4, 4): ✅ pass - assert_assert(87)_calls_PNotIn_0
+test_list_in.py(4, 11): ✅ pass - precondition
test_list_in.py(4, 4): ✅ pass - element not in list
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_negative_index.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_negative_index.expected
index 7e1291e401..64b7433508 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_negative_index.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_negative_index.expected
@@ -1,4 +1,4 @@
-test_list_negative_index.py(3, 4): ✅ pass - assert_assert(58)_calls_Any_get_0
+test_list_negative_index.py(3, 11): ✅ pass - precondition
test_list_negative_index.py(3, 4): ✅ pass - negative index
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_of_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_of_list.expected
index 2a613b7d1b..cce46da9f2 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_of_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_of_list.expected
@@ -1,5 +1,5 @@
-test_list_of_list.py(5, 4): ✅ pass - assert_assert(84)_calls_Any_get_0
-test_list_of_list.py(5, 4): ✅ pass - assert_assert(84)_calls_Any_get_1
+test_list_of_list.py(5, 11): ✅ pass - precondition
+test_list_of_list.py(5, 11): ✅ pass - precondition
test_list_of_list.py(5, 4): ✅ pass - list of list
DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_list_slice.expected b/StrataPython/StrataPythonTest/expected_laurel/test_list_slice.expected
index ce237e95f1..0d662cd753 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_list_slice.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_list_slice.expected
@@ -1,22 +1,22 @@
-test_list_slice.py(3, 0): ✅ pass - assert_assert(32)_calls_Any_get_slice_0
-test_list_slice.py(3, 0): ✅ pass - assert_assert(32)_calls_Any_get_1
+test_list_slice.py(3, 7): ✅ pass - precondition
+test_list_slice.py(3, 7): ✅ pass - precondition
test_list_slice.py(3, 0): ✅ pass - assert(32)
-test_list_slice.py(7, 0): ✅ pass - assert_assert(145)_calls_Any_get_slice_0
-test_list_slice.py(7, 0): ✅ pass - assert_assert(145)_calls_Any_get_1
-test_list_slice.py(7, 0): ✅ pass - assert_assert(145)_calls_Any_get_2
+test_list_slice.py(7, 7): ✅ pass - precondition
+test_list_slice.py(7, 7): ✅ pass - precondition
+test_list_slice.py(7, 7): ✅ pass - precondition
test_list_slice.py(7, 0): ✅ pass - assert(145)
-test_list_slice.py(9, 0): ✅ pass - assert_assert(187)_calls_Any_get_slice_0
+test_list_slice.py(9, 7): ✅ pass - precondition
test_list_slice.py(9, 0): ✅ pass - assert(187)
test_list_slice.py(11, 16): ✅ pass - Check PNeg exception
test_list_slice.py(11, 19): ✅ pass - Check PNeg exception
-test_list_slice.py(11, 0): ✅ pass - assert_assert(220)_calls_Any_get_slice_0
+test_list_slice.py(11, 7): ✅ pass - precondition
test_list_slice.py(11, 0): ✅ pass - assert(220)
test_list_slice.py(13, 16): ✅ pass - Check PNeg exception
test_list_slice.py(13, 21): ✅ pass - Check PNeg exception
-test_list_slice.py(13, 0): ✅ pass - assert_assert(260)_calls_Any_get_slice_0
+test_list_slice.py(13, 7): ✅ pass - precondition
test_list_slice.py(13, 0): ✅ pass - assert(260)
test_list_slice.py(15, 18): ✅ pass - Check PNeg exception
-test_list_slice.py(15, 0): ✅ pass - assert_assert(307)_calls_Any_get_slice_0
+test_list_slice.py(15, 7): ✅ pass - precondition
test_list_slice.py(15, 0): ✅ pass - assert(307)
DETAIL: 20 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_loops.expected b/StrataPython/StrataPythonTest/expected_laurel/test_loops.expected
index 4adb7f6b70..7c53fbd38f 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_loops.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_loops.expected
@@ -1,19 +1,15 @@
test_loops.py(3, 4): ✅ pass - assert(38)
-test_loops.py(4, 4): ✅ pass - assume_assume(53)_calls_PIn_0
+test_loops.py(4, 4): ✅ pass - precondition
test_loops.py(5, 12): ❓ unknown - Check PAdd exception
test_loops.py(6, 11): ❓ unknown - Check PGt exception
test_loops.py(6, 4): ❓ unknown - simple loop incremented
test_loops.py(9, 4): ✅ pass - assert(174)
-test_loops.py(10, 4): ❓ unknown - set_a_calls_Any_get_0
-test_loops.py(10, 4): ❓ unknown - set_b_calls_Any_get_0
+test_loops.py(10, 4): ❓ unknown - precondition
test_loops.py(11, 13): ❓ unknown - Check PSub exception
test_loops.py(12, 11): ❓ unknown - Check PLt exception
test_loops.py(12, 4): ❓ unknown - tuple unpacking decremented
test_loops.py(15, 4): ✅ pass - assert(337)
-test_loops.py(16, 4): ❓ unknown - set_x_calls_Any_get_0
-test_loops.py(16, 4): ❓ unknown - set_tuple_360_calls_Any_get_0
-test_loops.py(16, 4): ❓ unknown - set_y_calls_Any_get_0
-test_loops.py(16, 4): ❓ unknown - set_z_calls_Any_get_0
+test_loops.py(16, 4): ❓ unknown - precondition
test_loops.py(17, 13): ❓ unknown - Check PAdd exception
test_loops.py(18, 11): ❓ unknown - Check PGt exception
test_loops.py(18, 4): ❓ unknown - nested unpacking incremented
@@ -22,5 +18,5 @@ test_loops.py(22, 10): ✅ pass - Check PGt exception
test_loops.py(23, 13): ❓ unknown - Check PSub exception
test_loops.py(24, 11): ❓ unknown - Check PLe exception
test_loops.py(24, 4): ❓ unknown - while loop did not increase n4
-DETAIL: 6 passed, 0 failed, 18 inconclusive
+DETAIL: 6 passed, 0 failed, 14 inconclusive
RESULT: Inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_method_kwargs_no_hierarchy.expected b/StrataPython/StrataPythonTest/expected_laurel/test_method_kwargs_no_hierarchy.expected
index 56de827e26..18d6110384 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_method_kwargs_no_hierarchy.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_method_kwargs_no_hierarchy.expected
@@ -1,8 +1,8 @@
test_method_kwargs_no_hierarchy.py(5, 41): ❓ unknown - (Calculator@add ensures) Return type constraint
test_method_kwargs_no_hierarchy.py(9, 4): ✅ pass - (Calculator@__init__ requires) Type constraint of base
-unknown location: ✅ pass - assert_assert(0)_calls_Any_get_or_none_0
+unknown location: ✅ pass - precondition
unknown location: ✅ pass - assert(0)
-test_method_kwargs_no_hierarchy.py(11, 18): ✅ pass - init_calls_Any_get_or_none_0
+unknown location: ✅ pass - precondition
test_method_kwargs_no_hierarchy.py(11, 18): ✅ pass - (Calculator@add requires) Type constraint of x
test_method_kwargs_no_hierarchy.py(11, 18): ✅ pass - (Calculator@add requires) Type constraint of y
test_method_kwargs_no_hierarchy.py(11, 4): ✅ pass - assert(254)
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_missing_models.expected b/StrataPython/StrataPythonTest/expected_laurel/test_missing_models.expected
index 0cd54248ab..c913d25b07 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_missing_models.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_missing_models.expected
@@ -1,5 +1,5 @@
-test_missing_models.py(8, 4): ❓ unknown - init_calls_Any_get_0
-test_missing_models.py(8, 4): ❓ unknown - init_calls_Any_get_1
+test_missing_models.py(8, 30): ❓ unknown - precondition
+test_missing_models.py(8, 30): ❓ unknown - precondition
test_missing_models.py(9, 4): ❓ unknown - (Origin_test_helper_procedure_Requires)req_name_is_foo
test_missing_models.py(9, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_str
test_missing_models.py(9, 4): ✅ pass - (Origin_test_helper_procedure_Requires)req_opt_name_none_or_bar
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_mixed_types_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_mixed_types_list.expected
index 6ea1964407..d320eab756 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_mixed_types_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_mixed_types_list.expected
@@ -1,6 +1,6 @@
-test_mixed_types_list.py(3, 4): ✅ pass - assert_assert(56)_calls_Any_get_0
+test_mixed_types_list.py(3, 11): ✅ pass - precondition
test_mixed_types_list.py(3, 4): ✅ pass - int element
-test_mixed_types_list.py(4, 4): ✅ pass - assert_assert(93)_calls_Any_get_0
+test_mixed_types_list.py(4, 11): ✅ pass - precondition
test_mixed_types_list.py(4, 4): ✅ pass - str element
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_module_level.expected b/StrataPython/StrataPythonTest/expected_laurel/test_module_level.expected
index d6bc9a6556..e1d5280f6e 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_module_level.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_module_level.expected
@@ -1,13 +1,13 @@
-test_module_level.py(9, 0): ✅ pass - assert_assert(115)_calls_Any_get_0
+test_module_level.py(9, 7): ✅ pass - precondition
test_module_level.py(9, 0): ✅ pass - assert(115)
-test_module_level.py(10, 0): ✅ pass - assert_assert(145)_calls_Any_get_0
+test_module_level.py(10, 7): ✅ pass - precondition
test_module_level.py(10, 0): ✅ pass - assert(145)
test_module_level.py(12, 0): ✅ pass - Check Any_sets! exception
-test_module_level.py(13, 0): ✅ pass - assert_assert(201)_calls_Any_get_0
+test_module_level.py(13, 7): ✅ pass - precondition
test_module_level.py(13, 0): ✅ pass - assert(201)
-test_module_level.py(14, 0): ✅ pass - assert_assert(236)_calls_PIn_0
+test_module_level.py(14, 7): ✅ pass - precondition
test_module_level.py(14, 0): ✅ pass - assert(236)
-test_module_level.py(15, 0): ✅ pass - assert_assert(258)_calls_PNotIn_0
+test_module_level.py(15, 7): ✅ pass - precondition
test_module_level.py(15, 0): ✅ pass - assert(258)
DETAIL: 11 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_multi_function.expected b/StrataPython/StrataPythonTest/expected_laurel/test_multi_function.expected
index 1408f7cb98..61e5d53686 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_multi_function.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_multi_function.expected
@@ -1,13 +1,13 @@
test_multi_function.py(4, 44): ✅ pass - (create_config ensures) Return type constraint
-test_multi_function.py(9, 4): ✅ pass - ite_cond_calls_PNotIn_0
+test_multi_function.py(9, 7): ✅ pass - precondition
test_multi_function.py(8, 47): ✅ pass - (validate_config ensures) Return type constraint
-test_multi_function.py(11, 4): ✅ pass - ite_cond_calls_PNotIn_0
+test_multi_function.py(11, 7): ✅ pass - precondition
test_multi_function.py(16, 4): ✅ pass - (create_config requires) Type constraint of name
test_multi_function.py(16, 4): ✅ pass - (create_config requires) Type constraint of value
test_multi_function.py(17, 4): ✅ pass - (validate_config requires) Type constraint of config
test_multi_function.py(17, 4): ✅ pass - assert(485)
test_multi_function.py(18, 7): ✅ pass - Check PNot exception
-test_multi_function.py(20, 4): ❓ unknown - set_LaurelResult_calls_Any_get_0
+test_multi_function.py(20, 4): ❓ unknown - precondition
test_multi_function.py(23, 4): ✅ pass - (process_config requires) Type constraint of name
test_multi_function.py(23, 4): ✅ pass - (process_config requires) Type constraint of value
test_multi_function.py(24, 4): ❓ unknown - process_config should return value
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_nested_dict.expected b/StrataPython/StrataPythonTest/expected_laurel/test_nested_dict.expected
index df550fc4b1..3b25bad6c2 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_nested_dict.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_nested_dict.expected
@@ -1,6 +1,6 @@
-test_nested_dict.py(3, 4): ✅ pass - assert_assert(48)_calls_Any_get_0
-test_nested_dict.py(3, 4): ✅ pass - assert_assert(48)_calls_Any_get_1
-test_nested_dict.py(3, 4): ✅ pass - assert_assert(48)_calls_Any_get_2
+test_nested_dict.py(3, 11): ✅ pass - precondition
+test_nested_dict.py(3, 11): ✅ pass - precondition
+test_nested_dict.py(3, 11): ✅ pass - precondition
test_nested_dict.py(3, 4): ✅ pass - nested dict
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_nested_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_nested_list.expected
index 9920d7a1ef..7eff94e9bd 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_nested_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_nested_list.expected
@@ -1,8 +1,8 @@
-test_nested_list.py(3, 4): ✅ pass - assert_assert(54)_calls_Any_get_0
-test_nested_list.py(3, 4): ✅ pass - assert_assert(54)_calls_Any_get_1
+test_nested_list.py(3, 11): ✅ pass - precondition
+test_nested_list.py(3, 11): ✅ pass - precondition
test_nested_list.py(3, 4): ✅ pass - nested access 0,0
-test_nested_list.py(4, 4): ✅ pass - assert_assert(100)_calls_Any_get_0
-test_nested_list.py(4, 4): ✅ pass - assert_assert(100)_calls_Any_get_1
+test_nested_list.py(4, 11): ✅ pass - precondition
+test_nested_list.py(4, 11): ✅ pass - precondition
test_nested_list.py(4, 4): ✅ pass - nested access 1,1
DETAIL: 6 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_nested_optional.expected b/StrataPython/StrataPythonTest/expected_laurel/test_nested_optional.expected
index e32ca89fd6..6aeed2d9e5 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_nested_optional.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_nested_optional.expected
@@ -1,4 +1,4 @@
-test_nested_optional.py(5, 4): ✅ pass - assert_assert(90)_calls_Any_get_0
+test_nested_optional.py(5, 11): ✅ pass - precondition
test_nested_optional.py(5, 4): ✅ pass - nested optional list
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_none_in_list.expected b/StrataPython/StrataPythonTest/expected_laurel/test_none_in_list.expected
index 11f44bb821..ce2c8b27ee 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_none_in_list.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_none_in_list.expected
@@ -1,4 +1,4 @@
-test_none_in_list.py(3, 4): ✅ pass - assert_assert(38)_calls_Any_get_0
+test_none_in_list.py(3, 11): ✅ pass - precondition
test_none_in_list.py(3, 4): ✅ pass - None in list
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_pin_any.expected b/StrataPython/StrataPythonTest/expected_laurel/test_pin_any.expected
index 8088956821..bda3033aa1 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_pin_any.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_pin_any.expected
@@ -1,4 +1,4 @@
-test_pin_any.py(4, 8): ❓ unknown - assert_assert(124)_calls_PIn_0
+test_pin_any.py(4, 15): ❓ unknown - precondition
test_pin_any.py(4, 8): ❓ unknown - key could be in results
test_pin_any.py(2, 36): ✔️ always true if reached - (test_in_on_any ensures) Return type constraint
DETAIL: 1 passed, 0 failed, 2 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_regex_negative.expected b/StrataPython/StrataPythonTest/expected_laurel/test_regex_negative.expected
index 2d85b2d64a..ffc6566e42 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_regex_negative.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_regex_negative.expected
@@ -1,51 +1,51 @@
-test_regex_negative.py(9, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(9, 4): ✅ pass - precondition
test_regex_negative.py(10, 4): ❓ unknown - EXPECTED_FAIL: fullmatch a on b
-test_regex_negative.py(12, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(12, 4): ✅ pass - precondition
test_regex_negative.py(13, 4): ❓ unknown - EXPECTED_FAIL: fullmatch abc on abd
-test_regex_negative.py(15, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(15, 4): ✅ pass - precondition
test_regex_negative.py(16, 4): ❓ unknown - EXPECTED_FAIL: fullmatch [a-z]+ on ABC
-test_regex_negative.py(19, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(19, 4): ✅ pass - precondition
test_regex_negative.py(20, 4): ❓ unknown - EXPECTED_FAIL: fullmatch ^abc$ on abcd
-test_regex_negative.py(22, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(22, 4): ✅ pass - precondition
test_regex_negative.py(23, 4): ❓ unknown - EXPECTED_FAIL: search ^abc in xabc
-test_regex_negative.py(25, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(25, 4): ✅ pass - precondition
test_regex_negative.py(26, 4): ❓ unknown - EXPECTED_FAIL: search abc$ in abcx
-test_regex_negative.py(28, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_negative.py(28, 4): ✅ pass - precondition
test_regex_negative.py(29, 4): ❓ unknown - EXPECTED_FAIL: match ^a$ in ab
-test_regex_negative.py(32, 4): ✅ pass - set_p_calls_re_compile_0
-test_regex_negative.py(33, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(32, 4): ✅ pass - precondition
+test_regex_negative.py(33, 4): ✅ pass - precondition
test_regex_negative.py(34, 4): ❓ unknown - EXPECTED_FAIL: compiled ^abc$ search xabc
-test_regex_negative.py(36, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_negative.py(36, 4): ✅ pass - precondition
test_regex_negative.py(37, 4): ❓ unknown - EXPECTED_FAIL: compiled ^abc$ match abcx
-test_regex_negative.py(44, 8): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(44, 8): ✅ pass - precondition
test_regex_negative.py(47, 4): ❓ unknown - malformed: unmatched paren should raise
-test_regex_negative.py(51, 8): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(51, 8): ✅ pass - precondition
test_regex_negative.py(54, 4): ❓ unknown - malformed: nothing to repeat should raise
-test_regex_negative.py(58, 8): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(58, 8): ✅ pass - precondition
test_regex_negative.py(61, 4): ❓ unknown - malformed: bad bounds should raise
-test_regex_negative.py(65, 8): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(65, 8): ✅ pass - precondition
test_regex_negative.py(68, 4): ❓ unknown - malformed: search with bad pattern should raise
-test_regex_negative.py(72, 8): ✅ pass - set_m_calls_re_match_0
+test_regex_negative.py(72, 8): ✅ pass - precondition
test_regex_negative.py(75, 4): ❓ unknown - malformed: match with bad pattern should raise
-test_regex_negative.py(83, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(83, 4): ✅ pass - precondition
test_regex_negative.py(84, 4): ❓ unknown - unsupported: search \S+ should match non-empty non-whitespace
-test_regex_negative.py(86, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(86, 4): ✅ pass - precondition
test_regex_negative.py(87, 4): ❓ unknown - unsupported: fullmatch \d+ on digit string
-test_regex_negative.py(89, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(89, 4): ✅ pass - precondition
test_regex_negative.py(90, 4): ❓ unknown - unsupported: fullmatch \w+ on word string
-test_regex_negative.py(92, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(92, 4): ✅ pass - precondition
test_regex_negative.py(93, 4): ❓ unknown - unsupported: search \s+ finds whitespace
-test_regex_negative.py(96, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(96, 4): ✅ pass - precondition
test_regex_negative.py(97, 4): ❓ unknown - unsupported: fullmatch [a-z\d]+ on alphanumeric
-test_regex_negative.py(99, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(99, 4): ✅ pass - precondition
test_regex_negative.py(100, 4): ❓ unknown - unsupported: fullmatch [\w\-]+ on word with dash
-test_regex_negative.py(103, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(103, 4): ✅ pass - precondition
test_regex_negative.py(104, 4): ❓ unknown - unsupported: search \t+ on tab string
-test_regex_negative.py(106, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_negative.py(106, 4): ✅ pass - precondition
test_regex_negative.py(107, 4): ❓ unknown - unsupported: fullmatch [^\n]+ on non-newline string
-test_regex_negative.py(110, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(110, 4): ✅ pass - precondition
test_regex_negative.py(111, 4): ❓ unknown - unsupported: non-greedy .*? quantifier
-test_regex_negative.py(113, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_negative.py(113, 4): ✅ pass - precondition
test_regex_negative.py(114, 4): ❓ unknown - unsupported: positive lookahead (?=foo)
DETAIL: 25 passed, 0 failed, 24 inconclusive
RESULT: Inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_regex_positive.expected b/StrataPython/StrataPythonTest/expected_laurel/test_regex_positive.expected
index 58993070b1..bdafa7fb98 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_regex_positive.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_regex_positive.expected
@@ -1,284 +1,284 @@
-test_regex_positive.py(7, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(7, 4): ✅ pass - precondition
test_regex_positive.py(8, 4): ✅ pass - fullmatch literal should match
-test_regex_positive.py(10, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(10, 4): ✅ pass - precondition
test_regex_positive.py(11, 4): ✅ pass - fullmatch literal should reject extra chars
-test_regex_positive.py(14, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(14, 4): ✅ pass - precondition
test_regex_positive.py(15, 4): ✅ pass - fullmatch char class should match
-test_regex_positive.py(17, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(17, 4): ✅ pass - precondition
test_regex_positive.py(18, 4): ✅ pass - fullmatch char class should reject uppercase
-test_regex_positive.py(21, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(21, 4): ✅ pass - precondition
test_regex_positive.py(22, 4): ✅ pass - fullmatch negated class should match non-digits
-test_regex_positive.py(24, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(24, 4): ✅ pass - precondition
test_regex_positive.py(25, 4): ✅ pass - fullmatch negated class should reject digits
-test_regex_positive.py(28, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(28, 4): ✅ pass - precondition
test_regex_positive.py(29, 4): ✅ pass - fullmatch dot-plus should match non-empty
-test_regex_positive.py(31, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(31, 4): ✅ pass - precondition
test_regex_positive.py(32, 4): ✅ pass - fullmatch single dot should reject two chars
-test_regex_positive.py(35, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(35, 4): ✅ pass - precondition
test_regex_positive.py(36, 4): ✅ pass - fullmatch a* should match empty
-test_regex_positive.py(38, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(38, 4): ✅ pass - precondition
test_regex_positive.py(39, 4): ✅ pass - fullmatch a* should match repeated
-test_regex_positive.py(41, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(41, 4): ✅ pass - precondition
test_regex_positive.py(42, 4): ✅ pass - fullmatch a* should reject non-a
-test_regex_positive.py(45, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(45, 4): ✅ pass - precondition
test_regex_positive.py(46, 4): ✅ pass - fullmatch a+ should reject empty
-test_regex_positive.py(48, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(48, 4): ✅ pass - precondition
test_regex_positive.py(49, 4): ✅ pass - fullmatch a+ should match one-or-more
-test_regex_positive.py(52, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(52, 4): ✅ pass - precondition
test_regex_positive.py(53, 4): ✅ pass - fullmatch ab?c should match without b
-test_regex_positive.py(55, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(55, 4): ✅ pass - precondition
test_regex_positive.py(56, 4): ✅ pass - fullmatch ab?c should match with b
-test_regex_positive.py(58, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(58, 4): ✅ pass - precondition
test_regex_positive.py(59, 4): ✅ pass - fullmatch ab?c should reject two b's
-test_regex_positive.py(62, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(62, 4): ✅ pass - precondition
test_regex_positive.py(63, 4): ✅ pass - fullmatch alternation should match first
-test_regex_positive.py(65, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(65, 4): ✅ pass - precondition
test_regex_positive.py(66, 4): ✅ pass - fullmatch alternation should match second
-test_regex_positive.py(68, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(68, 4): ✅ pass - precondition
test_regex_positive.py(69, 4): ✅ pass - fullmatch alternation should reject other
-test_regex_positive.py(72, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(72, 4): ✅ pass - precondition
test_regex_positive.py(73, 4): ✅ pass - fullmatch concat should match
-test_regex_positive.py(75, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(75, 4): ✅ pass - precondition
test_regex_positive.py(76, 4): ✅ pass - fullmatch concat should reject wrong order
-test_regex_positive.py(80, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(80, 4): ✅ pass - precondition
test_regex_positive.py(81, 4): ✅ pass - match should match at start
-test_regex_positive.py(83, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(83, 4): ✅ pass - precondition
test_regex_positive.py(84, 4): ✅ pass - match should reject when not at start
-test_regex_positive.py(86, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(86, 4): ✅ pass - precondition
test_regex_positive.py(87, 4): ✅ pass - match should match prefix
-test_regex_positive.py(89, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(89, 4): ✅ pass - precondition
test_regex_positive.py(90, 4): ✅ pass - match should reject non-prefix
-test_regex_positive.py(94, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(94, 4): ✅ pass - precondition
test_regex_positive.py(95, 4): ✅ pass - search should find digits in middle
-test_regex_positive.py(97, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(97, 4): ✅ pass - precondition
test_regex_positive.py(98, 4): ✅ pass - search should reject when no digits
-test_regex_positive.py(100, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(100, 4): ✅ pass - precondition
test_regex_positive.py(101, 4): ✅ pass - search should find substring
-test_regex_positive.py(103, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(103, 4): ✅ pass - precondition
test_regex_positive.py(104, 4): ✅ pass - search should reject missing substring
-test_regex_positive.py(108, 4): ✅ pass - set_p_calls_re_compile_0
-test_regex_positive.py(110, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(108, 4): ✅ pass - precondition
+test_regex_positive.py(110, 4): ✅ pass - precondition
test_regex_positive.py(111, 4): ✅ pass - compiled fullmatch should match
-test_regex_positive.py(113, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(113, 4): ✅ pass - precondition
test_regex_positive.py(114, 4): ✅ pass - compiled fullmatch should reject uppercase
-test_regex_positive.py(116, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(116, 4): ✅ pass - precondition
test_regex_positive.py(117, 4): ✅ pass - compiled match should match prefix
-test_regex_positive.py(119, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(119, 4): ✅ pass - precondition
test_regex_positive.py(120, 4): ✅ pass - compiled search should find in middle
-test_regex_positive.py(125, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(125, 4): ✅ pass - precondition
test_regex_positive.py(126, 4): ✅ pass - fullmatch empty pattern on empty string
-test_regex_positive.py(128, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(128, 4): ✅ pass - precondition
test_regex_positive.py(129, 4): ✅ pass - fullmatch empty pattern on non-empty string
-test_regex_positive.py(132, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(132, 4): ✅ pass - precondition
test_regex_positive.py(133, 4): ✅ pass - fullmatch single char
-test_regex_positive.py(135, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(135, 4): ✅ pass - precondition
test_regex_positive.py(136, 4): ✅ pass - fullmatch single char mismatch
-test_regex_positive.py(139, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(139, 4): ✅ pass - precondition
test_regex_positive.py(140, 4): ✅ pass - fullmatch nested group-plus
-test_regex_positive.py(142, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(142, 4): ✅ pass - precondition
test_regex_positive.py(143, 4): ✅ pass - fullmatch nested group-plus mismatch
-test_regex_positive.py(146, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(146, 4): ✅ pass - precondition
test_regex_positive.py(147, 4): ✅ pass - fullmatch loop min
-test_regex_positive.py(149, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(149, 4): ✅ pass - precondition
test_regex_positive.py(150, 4): ✅ pass - fullmatch loop max
-test_regex_positive.py(152, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(152, 4): ✅ pass - precondition
test_regex_positive.py(153, 4): ✅ pass - fullmatch loop below min
-test_regex_positive.py(155, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(155, 4): ✅ pass - precondition
test_regex_positive.py(156, 4): ✅ pass - fullmatch loop above max
-test_regex_positive.py(159, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(159, 4): ✅ pass - precondition
test_regex_positive.py(160, 4): ✅ pass - fullmatch group loop match
-test_regex_positive.py(162, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(162, 4): ✅ pass - precondition
test_regex_positive.py(163, 4): ✅ pass - fullmatch group loop too few
-test_regex_positive.py(165, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(165, 4): ✅ pass - precondition
test_regex_positive.py(166, 4): ✅ pass - fullmatch group loop 3 reps
-test_regex_positive.py(168, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(168, 4): ✅ pass - precondition
test_regex_positive.py(169, 4): ✅ pass - fullmatch group loop 1 rep
-test_regex_positive.py(174, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(174, 4): ✅ pass - precondition
test_regex_positive.py(175, 4): ✅ pass - fullmatch ^a match
-test_regex_positive.py(177, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(177, 4): ✅ pass - precondition
test_regex_positive.py(178, 4): ✅ pass - fullmatch ^a reject
-test_regex_positive.py(180, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(180, 4): ✅ pass - precondition
test_regex_positive.py(181, 4): ✅ pass - fullmatch a$ match
-test_regex_positive.py(183, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(183, 4): ✅ pass - precondition
test_regex_positive.py(184, 4): ✅ pass - fullmatch a$ reject
-test_regex_positive.py(186, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(186, 4): ✅ pass - precondition
test_regex_positive.py(187, 4): ✅ pass - fullmatch ^a$ match
-test_regex_positive.py(189, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(189, 4): ✅ pass - precondition
test_regex_positive.py(190, 4): ✅ pass - fullmatch ^a$ reject trailing
-test_regex_positive.py(192, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(192, 4): ✅ pass - precondition
test_regex_positive.py(193, 4): ✅ pass - fullmatch ^a$ reject leading
-test_regex_positive.py(196, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(196, 4): ✅ pass - precondition
test_regex_positive.py(197, 4): ✅ pass - fullmatch ^$ on empty
-test_regex_positive.py(199, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(199, 4): ✅ pass - precondition
test_regex_positive.py(200, 4): ✅ pass - fullmatch ^$ on non-empty
-test_regex_positive.py(202, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(202, 4): ✅ pass - precondition
test_regex_positive.py(203, 4): ✅ pass - match ^$ on empty
-test_regex_positive.py(205, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(205, 4): ✅ pass - precondition
test_regex_positive.py(206, 4): ✅ pass - match ^$ on non-empty
-test_regex_positive.py(208, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(208, 4): ✅ pass - precondition
test_regex_positive.py(209, 4): ✅ pass - search ^$ on empty
-test_regex_positive.py(211, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(211, 4): ✅ pass - precondition
test_regex_positive.py(212, 4): ✅ pass - search ^$ on non-empty
-test_regex_positive.py(217, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(217, 4): ✅ pass - precondition
test_regex_positive.py(218, 4): ✅ pass - match ^a
-test_regex_positive.py(220, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(220, 4): ✅ pass - precondition
test_regex_positive.py(221, 4): ✅ pass - match ^a trailing ok
-test_regex_positive.py(223, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(223, 4): ✅ pass - precondition
test_regex_positive.py(224, 4): ✅ pass - match ^a reject
-test_regex_positive.py(227, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(227, 4): ✅ pass - precondition
test_regex_positive.py(228, 4): ✅ pass - match ^a$ exact
-test_regex_positive.py(230, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(230, 4): ✅ pass - precondition
test_regex_positive.py(231, 4): ✅ pass - match ^a$ reject trailing
-test_regex_positive.py(233, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(233, 4): ✅ pass - precondition
test_regex_positive.py(234, 4): ✅ pass - match a$ exact
-test_regex_positive.py(236, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(236, 4): ✅ pass - precondition
test_regex_positive.py(237, 4): ✅ pass - match a$ reject trailing
-test_regex_positive.py(239, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(239, 4): ✅ pass - precondition
test_regex_positive.py(240, 4): ✅ pass - match a.*$ accepts
-test_regex_positive.py(242, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(242, 4): ✅ pass - precondition
test_regex_positive.py(243, 4): ✅ pass - match a.*$ rejects
-test_regex_positive.py(248, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(248, 4): ✅ pass - precondition
test_regex_positive.py(249, 4): ✅ pass - search a in middle
-test_regex_positive.py(251, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(251, 4): ✅ pass - precondition
test_regex_positive.py(252, 4): ✅ pass - search a not found
-test_regex_positive.py(255, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(255, 4): ✅ pass - precondition
test_regex_positive.py(256, 4): ✅ pass - search ^a at start
-test_regex_positive.py(258, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(258, 4): ✅ pass - precondition
test_regex_positive.py(259, 4): ✅ pass - search ^a reject non-start
-test_regex_positive.py(261, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(261, 4): ✅ pass - precondition
test_regex_positive.py(262, 4): ✅ pass - search ^a exact
-test_regex_positive.py(265, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(265, 4): ✅ pass - precondition
test_regex_positive.py(266, 4): ✅ pass - search a$ at end
-test_regex_positive.py(268, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(268, 4): ✅ pass - precondition
test_regex_positive.py(269, 4): ✅ pass - search a$ reject non-end
-test_regex_positive.py(271, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(271, 4): ✅ pass - precondition
test_regex_positive.py(272, 4): ✅ pass - search a$ deep end
-test_regex_positive.py(274, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(274, 4): ✅ pass - precondition
test_regex_positive.py(275, 4): ✅ pass - search a$ reject trailing
-test_regex_positive.py(278, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(278, 4): ✅ pass - precondition
test_regex_positive.py(279, 4): ✅ pass - search ^a$ exact
-test_regex_positive.py(281, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(281, 4): ✅ pass - precondition
test_regex_positive.py(282, 4): ✅ pass - search ^a$ reject prefix
-test_regex_positive.py(284, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(284, 4): ✅ pass - precondition
test_regex_positive.py(285, 4): ✅ pass - search ^a$ reject suffix
-test_regex_positive.py(289, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(289, 4): ✅ pass - precondition
test_regex_positive.py(290, 4): ✅ pass - search ^abc at start
-test_regex_positive.py(292, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(292, 4): ✅ pass - precondition
test_regex_positive.py(293, 4): ✅ pass - search ^abc reject non-start
-test_regex_positive.py(295, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(295, 4): ✅ pass - precondition
test_regex_positive.py(296, 4): ✅ pass - search abc$ at end
-test_regex_positive.py(298, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(298, 4): ✅ pass - precondition
test_regex_positive.py(299, 4): ✅ pass - search abc$ reject non-end
-test_regex_positive.py(301, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(301, 4): ✅ pass - precondition
test_regex_positive.py(302, 4): ✅ pass - search ^abc$ exact
-test_regex_positive.py(304, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(304, 4): ✅ pass - precondition
test_regex_positive.py(305, 4): ✅ pass - search ^abc$ reject prefix
-test_regex_positive.py(307, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(307, 4): ✅ pass - precondition
test_regex_positive.py(308, 4): ✅ pass - search ^abc$ reject suffix
-test_regex_positive.py(312, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(312, 4): ✅ pass - precondition
test_regex_positive.py(313, 4): ✅ pass - fullmatch ^a{3}$ match
-test_regex_positive.py(315, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(315, 4): ✅ pass - precondition
test_regex_positive.py(316, 4): ✅ pass - fullmatch ^a{3}$ too few
-test_regex_positive.py(318, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(318, 4): ✅ pass - precondition
test_regex_positive.py(319, 4): ✅ pass - fullmatch ^a{3}$ too many
-test_regex_positive.py(321, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(321, 4): ✅ pass - precondition
test_regex_positive.py(322, 4): ✅ pass - match ^a{3}$ exact
-test_regex_positive.py(324, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(324, 4): ✅ pass - precondition
test_regex_positive.py(325, 4): ✅ pass - match ^a{3}$ reject trailing
-test_regex_positive.py(327, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(327, 4): ✅ pass - precondition
test_regex_positive.py(328, 4): ✅ pass - match a{3} trailing ok
-test_regex_positive.py(332, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(332, 4): ✅ pass - precondition
test_regex_positive.py(333, 4): ✅ pass - escaped dot matches literal
-test_regex_positive.py(335, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(335, 4): ✅ pass - precondition
test_regex_positive.py(336, 4): ✅ pass - escaped dot rejects non-dot
-test_regex_positive.py(338, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(338, 4): ✅ pass - precondition
test_regex_positive.py(339, 4): ✅ pass - escaped plus matches literal
-test_regex_positive.py(341, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(341, 4): ✅ pass - precondition
test_regex_positive.py(342, 4): ✅ pass - escaped plus rejects
-test_regex_positive.py(344, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(344, 4): ✅ pass - precondition
test_regex_positive.py(345, 4): ✅ pass - escaped star matches literal
-test_regex_positive.py(347, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(347, 4): ✅ pass - precondition
test_regex_positive.py(348, 4): ✅ pass - escaped star rejects
-test_regex_positive.py(350, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(350, 4): ✅ pass - precondition
test_regex_positive.py(351, 4): ✅ pass - escaped question matches literal
-test_regex_positive.py(353, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(353, 4): ✅ pass - precondition
test_regex_positive.py(354, 4): ✅ pass - escaped question rejects
-test_regex_positive.py(356, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(356, 4): ✅ pass - precondition
test_regex_positive.py(357, 4): ✅ pass - escaped parens match literal
-test_regex_positive.py(359, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(359, 4): ✅ pass - precondition
test_regex_positive.py(360, 4): ✅ pass - escaped parens reject
-test_regex_positive.py(362, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(362, 4): ✅ pass - precondition
test_regex_positive.py(363, 4): ✅ pass - escaped backslash matches literal
-test_regex_positive.py(365, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(365, 4): ✅ pass - precondition
test_regex_positive.py(366, 4): ✅ pass - escaped backslash rejects
-test_regex_positive.py(369, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(369, 4): ✅ pass - precondition
test_regex_positive.py(370, 4): ✅ pass - search escaped dot
-test_regex_positive.py(372, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(372, 4): ✅ pass - precondition
test_regex_positive.py(373, 4): ✅ pass - search escaped backslash
-test_regex_positive.py(375, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(375, 4): ✅ pass - precondition
test_regex_positive.py(376, 4): ✅ pass - search escaped backslash reject
-test_regex_positive.py(380, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(380, 4): ✅ pass - precondition
test_regex_positive.py(381, 4): ✅ pass - colon literal match
-test_regex_positive.py(383, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(383, 4): ✅ pass - precondition
test_regex_positive.py(384, 4): ✅ pass - colon literal reject
-test_regex_positive.py(386, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(386, 4): ✅ pass - precondition
test_regex_positive.py(387, 4): ✅ pass - colon class match
-test_regex_positive.py(389, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(389, 4): ✅ pass - precondition
test_regex_positive.py(390, 4): ✅ pass - colon class reject
-test_regex_positive.py(392, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(392, 4): ✅ pass - precondition
test_regex_positive.py(393, 4): ✅ pass - search colon class
-test_regex_positive.py(395, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(395, 4): ✅ pass - precondition
test_regex_positive.py(396, 4): ✅ pass - match anchored colon
-test_regex_positive.py(398, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(398, 4): ✅ pass - precondition
test_regex_positive.py(399, 4): ✅ pass - match anchored colon reject trailing
-test_regex_positive.py(403, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(403, 4): ✅ pass - precondition
test_regex_positive.py(404, 4): ✅ pass - wildcard empty middle
-test_regex_positive.py(406, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(406, 4): ✅ pass - precondition
test_regex_positive.py(407, 4): ✅ pass - wildcard non-empty middle
-test_regex_positive.py(409, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(409, 4): ✅ pass - precondition
test_regex_positive.py(410, 4): ✅ pass - wildcard wrong ending
-test_regex_positive.py(412, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(412, 4): ✅ pass - precondition
test_regex_positive.py(413, 4): ✅ pass - search wildcard
-test_regex_positive.py(416, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(416, 4): ✅ pass - precondition
test_regex_positive.py(417, 4): ✅ pass - multi-char alt first
-test_regex_positive.py(419, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(419, 4): ✅ pass - precondition
test_regex_positive.py(420, 4): ✅ pass - multi-char alt second
-test_regex_positive.py(422, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(422, 4): ✅ pass - precondition
test_regex_positive.py(423, 4): ✅ pass - multi-char alt reject concat
-test_regex_positive.py(425, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(425, 4): ✅ pass - precondition
test_regex_positive.py(426, 4): ✅ pass - search multi-char alt
-test_regex_positive.py(430, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(430, 4): ✅ pass - precondition
test_regex_positive.py(431, 4): ✅ pass - fullmatch ^a|b$ first branch
-test_regex_positive.py(433, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(433, 4): ✅ pass - precondition
test_regex_positive.py(434, 4): ✅ pass - fullmatch ^a|b$ second branch
-test_regex_positive.py(436, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(436, 4): ✅ pass - precondition
test_regex_positive.py(437, 4): ✅ pass - fullmatch ^a|b$ reject
-test_regex_positive.py(439, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(439, 4): ✅ pass - precondition
test_regex_positive.py(440, 4): ✅ pass - search ^a|b$ start anchor
-test_regex_positive.py(442, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(442, 4): ✅ pass - precondition
test_regex_positive.py(443, 4): ✅ pass - search ^a|b$ end anchor
-test_regex_positive.py(445, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(445, 4): ✅ pass - precondition
test_regex_positive.py(446, 4): ✅ pass - search ^a|b$ neither
-test_regex_positive.py(450, 4): ✅ pass - set_p_calls_re_compile_0
-test_regex_positive.py(452, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(450, 4): ✅ pass - precondition
+test_regex_positive.py(452, 4): ✅ pass - precondition
test_regex_positive.py(453, 4): ✅ pass - compiled ^abc$ fullmatch
-test_regex_positive.py(455, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(455, 4): ✅ pass - precondition
test_regex_positive.py(456, 4): ✅ pass - compiled ^abc$ search exact
-test_regex_positive.py(458, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(458, 4): ✅ pass - precondition
test_regex_positive.py(459, 4): ✅ pass - compiled ^abc$ search reject prefix
-test_regex_positive.py(461, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(461, 4): ✅ pass - precondition
test_regex_positive.py(462, 4): ✅ pass - compiled ^abc$ match exact
-test_regex_positive.py(464, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(464, 4): ✅ pass - precondition
test_regex_positive.py(465, 4): ✅ pass - compiled ^abc$ match reject trailing
-test_regex_positive.py(472, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(472, 4): ✅ pass - precondition
test_regex_positive.py(473, 4): ✅ pass - malformed: unmatched paren is exception, not None
-test_regex_positive.py(475, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(475, 4): ✅ pass - precondition
test_regex_positive.py(476, 4): ✅ pass - malformed: nothing to repeat is exception, not None
-test_regex_positive.py(478, 4): ✅ pass - set_m_calls_re_fullmatch_0
+test_regex_positive.py(478, 4): ✅ pass - precondition
test_regex_positive.py(479, 4): ✅ pass - malformed: bad bounds is exception, not None
-test_regex_positive.py(481, 4): ✅ pass - set_m_calls_re_search_0
+test_regex_positive.py(481, 4): ✅ pass - precondition
test_regex_positive.py(482, 4): ✅ pass - malformed: search with bad pattern is exception, not None
-test_regex_positive.py(484, 4): ✅ pass - set_m_calls_re_match_0
+test_regex_positive.py(484, 4): ✅ pass - precondition
test_regex_positive.py(485, 4): ✅ pass - malformed: match with bad pattern is exception, not None
DETAIL: 282 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_subscription.expected b/StrataPython/StrataPythonTest/expected_laurel/test_subscription.expected
index a2764b2a02..e0f39a6100 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_subscription.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_subscription.expected
@@ -1,16 +1,16 @@
test_subscription.py(12, 0): ✅ pass - Check Any_sets! exception
test_subscription.py(14, 0): ✅ pass - Check Any_sets! exception
-test_subscription.py(16, 0): ✅ pass - assert_assert(421)_calls_Any_get_0
-test_subscription.py(16, 0): ✅ pass - assert_assert(421)_calls_Any_get_1
-test_subscription.py(16, 0): ✅ pass - assert_assert(421)_calls_Any_get_2
-test_subscription.py(16, 0): ✅ pass - assert_assert(421)_calls_Any_get_3
+test_subscription.py(16, 7): ✅ pass - precondition
+test_subscription.py(16, 7): ✅ pass - precondition
+test_subscription.py(16, 7): ✅ pass - precondition
+test_subscription.py(16, 7): ✅ pass - precondition
test_subscription.py(16, 0): ✅ pass - assert(421)
-test_subscription.py(18, 0): ✅ pass - assert_assert(489)_calls_Any_get_0
-test_subscription.py(18, 0): ✅ pass - assert_assert(489)_calls_Any_get_1
-test_subscription.py(18, 0): ✅ pass - assert_assert(489)_calls_Any_get_2
-test_subscription.py(18, 0): ✅ pass - assert_assert(489)_calls_PIn_3
+test_subscription.py(18, 18): ✅ pass - precondition
+test_subscription.py(18, 18): ✅ pass - precondition
+test_subscription.py(18, 18): ✅ pass - precondition
+test_subscription.py(18, 7): ✅ pass - precondition
test_subscription.py(18, 0): ✅ pass - assert(489)
-test_subscription.py(20, 0): ✅ pass - assert_assert(554)_calls_PIn_0
+test_subscription.py(20, 7): ✅ pass - precondition
test_subscription.py(20, 0): ✅ pass - assert(554)
DETAIL: 14 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_try_except_modeled.expected b/StrataPython/StrataPythonTest/expected_laurel/test_try_except_modeled.expected
index b7eb16a95b..6eb8a2cfac 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_try_except_modeled.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_try_except_modeled.expected
@@ -1,5 +1,5 @@
test_try_except_modeled.py(8, 4): ✅ pass - assert(337)
-test_try_except_modeled.py(10, 8): ✅ pass - set_result_calls_Any_get_0
+test_try_except_modeled.py(10, 8): ✅ pass - precondition
test_try_except_modeled.py(13, 4): ✅ pass - dict access should succeed
test_try_except_modeled.py(6, 30): ✅ pass - (test_try_dict_access ensures) Return type constraint
test_try_except_modeled.py(17, 4): ✅ pass - assert(541)
@@ -9,7 +9,7 @@ test_try_except_modeled.py(21, 17): ✅ pass - Check PAdd exception
test_try_except_modeled.py(24, 4): ✅ pass - addition should succeed
test_try_except_modeled.py(16, 29): ✅ pass - (test_try_arithmetic ensures) Return type constraint
test_try_except_modeled.py(32, 4): ✅ pass - assert(950)
-test_try_except_modeled.py(35, 12): ✅ pass - set_result_calls_Any_get_0
+test_try_except_modeled.py(35, 12): ✅ pass - precondition
test_try_except_modeled.py(38, 4): ✅ pass - nested dict access should succeed
test_try_except_modeled.py(30, 37): ✅ pass - (test_try_nested_dict_access ensures) Return type constraint
DETAIL: 14 passed, 0 failed, 0 inconclusive
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_create.expected b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_create.expected
index eb841a55ce..e6e7836d25 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_create.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_create.expected
@@ -1,6 +1,6 @@
-test_tuple_create.py(3, 4): ✅ pass - assert_assert(47)_calls_Any_get_0
+test_tuple_create.py(3, 11): ✅ pass - precondition
test_tuple_create.py(3, 4): ✅ pass - tuple first
-test_tuple_create.py(4, 4): ✅ pass - assert_assert(83)_calls_Any_get_0
+test_tuple_create.py(4, 11): ✅ pass - precondition
test_tuple_create.py(4, 4): ✅ pass - tuple last
DETAIL: 4 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_swap.expected b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_swap.expected
index bce7234e3f..10317bc474 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_swap.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_swap.expected
@@ -1,8 +1,7 @@
test_tuple_swap.py(2, 4): ✅ pass - assert(27)
test_tuple_swap.py(3, 4): ✅ pass - assert(42)
-test_tuple_swap.py(4, 4): ✅ pass - set_a_calls_Any_get_0
-test_tuple_swap.py(4, 4): ✅ pass - set_b_calls_Any_get_0
+test_tuple_swap.py(4, 4): ✅ pass - precondition
test_tuple_swap.py(5, 11): ✅ pass - Check PAnd exception
test_tuple_swap.py(5, 4): ✅ pass - tuple swap
-DETAIL: 6 passed, 0 failed, 0 inconclusive
+DETAIL: 5 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_type.expected b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_type.expected
index 211a0b9df0..1b3a39bfa0 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_type.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_type.expected
@@ -1,4 +1,4 @@
-test_tuple_type.py(5, 4): ✅ pass - assert_assert(80)_calls_Any_get_0
+test_tuple_type.py(5, 11): ✅ pass - precondition
test_tuple_type.py(5, 4): ✅ pass - typed tuple
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_unpack.expected b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_unpack.expected
index 2e58c8bb13..5a47e1ae13 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_tuple_unpack.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_tuple_unpack.expected
@@ -1,6 +1,5 @@
-test_tuple_unpack.py(3, 4): ✅ pass - set_a_calls_Any_get_0
-test_tuple_unpack.py(3, 4): ✅ pass - set_b_calls_Any_get_0
+test_tuple_unpack.py(3, 4): ✅ pass - precondition
test_tuple_unpack.py(4, 4): ✅ pass - unpack first
test_tuple_unpack.py(5, 4): ✅ pass - unpack second
-DETAIL: 4 passed, 0 failed, 0 inconclusive
+DETAIL: 3 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_type_dict_annotation.expected b/StrataPython/StrataPythonTest/expected_laurel/test_type_dict_annotation.expected
index 2b25064dde..38622ce174 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_type_dict_annotation.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_type_dict_annotation.expected
@@ -1,4 +1,4 @@
-test_type_dict_annotation.py(5, 4): ✅ pass - assert_assert(95)_calls_Any_get_0
+test_type_dict_annotation.py(5, 11): ✅ pass - precondition
test_type_dict_annotation.py(5, 4): ✅ pass - typed dict
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_type_list_annotation.expected b/StrataPython/StrataPythonTest/expected_laurel/test_type_list_annotation.expected
index db7eb8de67..6680893b7f 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_type_list_annotation.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_type_list_annotation.expected
@@ -1,4 +1,4 @@
-test_type_list_annotation.py(5, 4): ✅ pass - assert_assert(92)_calls_Any_get_0
+test_type_list_annotation.py(5, 11): ✅ pass - precondition
test_type_list_annotation.py(5, 4): ✅ pass - typed list
DETAIL: 2 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataPython/StrataPythonTest/expected_laurel/test_with_statement.expected b/StrataPython/StrataPythonTest/expected_laurel/test_with_statement.expected
index 8948ea9e4e..db27423c4b 100644
--- a/StrataPython/StrataPythonTest/expected_laurel/test_with_statement.expected
+++ b/StrataPython/StrataPythonTest/expected_laurel/test_with_statement.expected
@@ -1,13 +1,21 @@
test_with_statement.py(16, 4): ✔️ always true if reached - (Resource@__init__ requires) Type constraint of n
test_with_statement.py(17, 4): ✔️ always true if reached - assert(364)
+unknown location: ✔️ always true if reached - (Resource@__enter__ ensures) Return type constraint
+test_with_statement.py(18, 4): ✔️ always true if reached - (Resource@__exit__ ensures) Return type constraint
test_with_statement.py(20, 4): ✔️ always true if reached - assert(426)
test_with_statement.py(23, 4): ✔️ always true if reached - (Resource@__init__ requires) Type constraint of n
+test_with_statement.py(24, 4): ✔️ always true if reached - (Resource@__enter__ ensures) Return type constraint
+test_with_statement.py(25, 8): ✔️ always true if reached - (Resource@get_value ensures) Return type constraint
test_with_statement.py(25, 8): ✔️ always true if reached - assert(525)
test_with_statement.py(26, 8): ✔️ always true if reached - assert(558)
+test_with_statement.py(24, 4): ✔️ always true if reached - (Resource@__exit__ ensures) Return type constraint
test_with_statement.py(29, 4): ✔️ always true if reached - (Resource@__init__ requires) Type constraint of n
test_with_statement.py(30, 4): ✔️ always true if reached - (Resource@__init__ requires) Type constraint of n
+unknown location: ✔️ always true if reached - (Resource@__enter__ ensures) Return type constraint
+unknown location: ✔️ always true if reached - (Resource@__enter__ ensures) Return type constraint
test_with_statement.py(32, 21): ✔️ always true if reached - Check PAdd exception
test_with_statement.py(32, 8): ✔️ always true if reached - assert(697)
test_with_statement.py(33, 8): ✔️ always true if reached - assert(724)
-DETAIL: 11 passed, 0 failed, 0 inconclusive
+test_with_statement.py(31, 4): ✔️ always true if reached - (Resource@__exit__ ensures) Return type constraint
+DETAIL: 19 passed, 0 failed, 0 inconclusive
RESULT: Analysis success
diff --git a/StrataTest/Languages/Core/Tests/CoreOpTests.lean b/StrataTest/Languages/Core/Tests/CoreOpTests.lean
index 30202f74e6..e0a673f1e9 100644
--- a/StrataTest/Languages/Core/Tests/CoreOpTests.lean
+++ b/StrataTest/Languages/Core/Tests/CoreOpTests.lean
@@ -67,7 +67,7 @@ private def checkRoundTrip (name : String) : Bool :=
#guard checkRoundTrip "Re.None"
-- Map ops
-#guard checkRoundTrip "const"
+#guard checkRoundTrip "mapConst"
#guard checkRoundTrip "select"
#guard checkRoundTrip "update"
diff --git a/StrataTest/Languages/Core/Tests/ProgramEvalTests.lean b/StrataTest/Languages/Core/Tests/ProgramEvalTests.lean
index 0446dcb4b6..1cac76a90e 100644
--- a/StrataTest/Languages/Core/Tests/ProgramEvalTests.lean
+++ b/StrataTest/Languages/Core/Tests/ProgramEvalTests.lean
@@ -84,7 +84,7 @@ func Re.Union : ((x : regex) (y : regex)) → regex;
func Re.Inter : ((x : regex) (y : regex)) → regex;
func Re.Comp : ((x : regex)) → regex;
func Re.None : () → regex;
-func const : ∀[k, v]. ((d : v)) → (Map k v);
+func mapConst : ∀[k, v]. ((d : v)) → (Map k v);
func select : ∀[k, v]. ((m : (Map k v)) (i : k)) → v;
func update : ∀[k, v]. ((m : (Map k v)) (i : k) (x : v)) → (Map k v);
func Sequence.length : ∀[a]. ((s : (Sequence a))) → int;
diff --git a/StrataTest/Languages/Laurel/AbstractToConcreteTreeTranslatorTest.lean b/StrataTest/Languages/Laurel/AbstractToConcreteTreeTranslatorTest.lean
index cfc0740d51..9beb0e824c 100644
--- a/StrataTest/Languages/Laurel/AbstractToConcreteTreeTranslatorTest.lean
+++ b/StrataTest/Languages/Laurel/AbstractToConcreteTreeTranslatorTest.lean
@@ -91,7 +91,7 @@ procedure add(x: int, y: int): int
#end)
/--
-info: function aFunction(x: int): int
+info: procedure aFunction(x: int): int
{
x
};
@@ -100,7 +100,7 @@ info: function aFunction(x: int): int
#eval do IO.println (← roundtrip
#strata
program Laurel;
-function aFunction(x: int): int
+procedure aFunction(x: int): int
{ x };
#end)
@@ -122,8 +122,8 @@ info: procedure test(x: int): int
opaque
{
if x > 0
- then x
- else 0 - x
+ then x
+ else 0 - x
};
-/
#guard_msgs in
@@ -335,9 +335,9 @@ info: procedure earlyExit(b: bool)
opaque
{
if b
- then {
- return
- };
+ then {
+ return
+ };
assert true
};
-/
diff --git a/StrataTest/Languages/Laurel/ConstrainedTypeElimTest.lean b/StrataTest/Languages/Laurel/ConstrainedTypeElimTest.lean
index 9580ece5d6..396761d550 100644
--- a/StrataTest/Languages/Laurel/ConstrainedTypeElimTest.lean
+++ b/StrataTest/Languages/Laurel/ConstrainedTypeElimTest.lean
@@ -29,8 +29,8 @@ private def printElim (program : StrataDDM.Program) : IO Unit := do
IO.println (toString (Std.Format.pretty (Std.ToFormat.format proc)))
/--
-info: function nat$constraint(x: int): bool
-x >= 0;
+info: procedure nat$constraint(x: int): bool
+return x >= 0;
procedure test(n: int)
returns (r: int)
requires nat$constraint(n)
@@ -63,16 +63,16 @@ procedure test(n: nat) returns (r: nat) opaque {
-- Scope management: constrained variable in if-branch must not leak into sibling block
/--
-info: function pos$constraint(v: int): bool
-v > 0;
+info: procedure pos$constraint(v: int): bool
+return v > 0;
procedure test(b: bool)
opaque
{
if b
- then {
- var x: int := 1;
- assert pos$constraint(x)
- };
+ then {
+ var x: int := 1;
+ assert pos$constraint(x)
+ };
{
var x: int := -5;
x := -10
@@ -104,8 +104,8 @@ procedure test(b: bool) opaque {
-- Uninitialized constrained variable: havoc + assume constraint.
-- The variable has no known value, only the type constraint is assumed.
/--
-info: function posint$constraint(x: int): bool
-x > 0;
+info: procedure posint$constraint(x: int): bool
+return x > 0;
procedure f()
opaque
{
diff --git a/StrataTest/Languages/Laurel/DivisionByZeroCheckTest.lean b/StrataTest/Languages/Laurel/DivisionByZeroCheckTest.lean
index 8f652186e9..b01994ff00 100644
--- a/StrataTest/Languages/Laurel/DivisionByZeroCheckTest.lean
+++ b/StrataTest/Languages/Laurel/DivisionByZeroCheckTest.lean
@@ -18,6 +18,7 @@ generates verification conditions for these preconditions.
/-! ### Safe paths verify cleanly -/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -30,10 +31,10 @@ procedure safeDivision()
assert z == 5
};
-function pureDiv(x: int, y: int): int
+procedure pureDiv(x: int, y: int): int
requires y != 0
{
- x / y
+ return x / y
};
procedure callPureDivSafe()
@@ -47,6 +48,7 @@ procedure callPureDivSafe()
/-! ### Unsafe division: divisor not constrained, fails verification -/
-- Error ranges are too wide because Core does not use expression locations.
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -60,19 +62,20 @@ procedure unsafeDivision(x: int)
/-! ### Unsafe call to function with `requires y != 0` -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
-function pureDiv(x: int, y: int): int
+procedure pureDiv(x: int, y: int): int
requires y != 0
{
- x / y
+ return x / y
};
procedure callPureDivUnsafe(x: int)
opaque
{
var z: int := pureDiv(10, x)
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: precondition does not hold
};
#end
diff --git a/StrataTest/Languages/Laurel/EliminateDoWhileTest.lean b/StrataTest/Languages/Laurel/EliminateDoWhileTest.lean
index 548073bb0a..136ad79209 100644
--- a/StrataTest/Languages/Laurel/EliminateDoWhileTest.lean
+++ b/StrataTest/Languages/Laurel/EliminateDoWhileTest.lean
@@ -60,7 +60,7 @@ info: procedure basic()
x := x + 1
};
if !(x < 3)
- then exit $dowhile_exit_0
+ then exit $dowhile_exit_0
}
}$dowhile_exit_0;
assert x == 3
@@ -108,13 +108,13 @@ info: procedure nested()
y := y + 1
};
if !(y < 3)
- then exit $dowhile_exit_0
+ then exit $dowhile_exit_0
}
}$dowhile_exit_0;
x := x + 1
};
if !(x < 3)
- then exit $dowhile_exit_1
+ then exit $dowhile_exit_1
}
}$dowhile_exit_1;
assert x == 3
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypes.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypes.lean
index 0d15885bc9..63976b9bdb 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypes.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypes.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypesError.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypesError.lean
deleted file mode 100644
index 7f523c987b..0000000000
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T10_ConstrainedTypesError.lean
+++ /dev/null
@@ -1,32 +0,0 @@
-/-
- Copyright Strata Contributors
-
- SPDX-License-Identifier: Apache-2.0 OR MIT
--/
-
-import StrataTest.Util.TestLaurel
-
-open StrataTest.Util
-open Strata
-
-#eval testLaurel <|
-#strata
-program Laurel;
-constrained nat = x: int where x >= 0 witness 0
-
-// Function with valid constrained return — constraint not checked (not yet supported)
-function goodFunc(): nat { 3 };
-// ^^^^^^^^ error: constrained return types on functions are not yet supported
-
-// Function with invalid constrained return — constraint not checked (not yet supported)
-function badFunc(): nat { -1 };
-// ^^^^^^^ error: constrained return types on functions are not yet supported
-
-// Caller of constrained function — body is inlined, caller sees actual value
-procedure callerGood()
- opaque
-{
- var x: int := goodFunc();
- assert x >= 0
-};
-#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T12_Operators.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T12_Operators.lean
index bdfb797d68..de110a7973 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T12_Operators.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T12_Operators.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoops.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoops.lean
index 0d7b193c9f..be8c930189 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoops.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoops.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoopsError.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoopsError.lean
index e55297f647..d660bebe8e 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoopsError.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T13_WhileLoopsError.lean
@@ -15,6 +15,7 @@ These negative tests pin each failing loop invariant's diagnostic to that
invariant's own source range (per-invariant source ranges threaded through
loop elimination), rather than the whole loop. -/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -31,6 +32,7 @@ procedure badInitialInvariant()
};
#end
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -50,6 +52,7 @@ procedure secondInvariantFails()
};
#end
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T14_Quantifiers.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T14_Quantifiers.lean
index 9b5f93f15d..cfa2df1935 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T14_Quantifiers.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T14_Quantifiers.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -31,8 +32,8 @@ procedure testQuantifierInContract(n: int)
{
};
-function P(x: int): int;
-function Q(): int;
+procedure P(x: int): int;
+procedure Q(): int;
procedure triggers()
opaque
{
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T15_ShortCircuit.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T15_ShortCircuit.lean
index eb7562fe68..0b43e4f267 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T15_ShortCircuit.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T15_ShortCircuit.lean
@@ -9,12 +9,10 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
-function mustNotCallFunc(x: int): int
- requires false
-{ x };
procedure mustNotCallProc(): int
requires false
@@ -23,28 +21,6 @@ procedure mustNotCallProc(): int
return 0
};
-// Pure path: function with requires false
-procedure testAndThenFunc()
- opaque
-{
- var b: bool := false && mustNotCallFunc(0) > 0;
- assert !b
-};
-
-procedure testOrElseFunc()
- opaque
-{
- var b: bool := true || mustNotCallFunc(0) > 0;
- assert b
-};
-
-procedure testImpliesFunc()
- opaque
-{
- var b: bool := false ==> mustNotCallFunc(0) > 0;
- assert b
-};
-
// Pure path: division by zero
procedure testAndThenDivByZero()
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T16_PropertySummary.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T16_PropertySummary.lean
index 9e063f036d..81111abefe 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T16_PropertySummary.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T16_PropertySummary.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T17_ForLoop.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T17_ForLoop.lean
index bb4358e997..0685370325 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T17_ForLoop.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T17_ForLoop.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T18_RecursiveProcedure.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T18_RecursiveProcedure.lean
index 9a97632738..5c845fc31e 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T18_RecursiveProcedure.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T18_RecursiveProcedure.lean
@@ -14,6 +14,7 @@ A recursive function over a recursive datatype.
The `isRecursive` flag should be inferred automatically from the self-call.
-/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_BitvectorTypes.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_BitvectorTypes.lean
index 11b55b9dcf..73c6017c87 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_BitvectorTypes.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_BitvectorTypes.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_InvokeOn.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_InvokeOn.lean
index 32efb65c0c..e3f9ad577c 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_InvokeOn.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T19_InvokeOn.lean
@@ -9,16 +9,17 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
(options := { verifyOptions := { Core.VerifyOptions.quiet with solver := "z3" } })
#strata
program Laurel;
-function P(x: int): bool;
-function Q(x: int): bool;
+procedure P(x: int): bool;
+procedure Q(x: int): bool;
-function assertP(x: int): int requires P(x);
-function needsPAndQsInvoke1(): int {
- assertP(3)
+procedure assertP(x: int): int requires P(x);
+procedure needsPAndQsInvoke1(): int {
+ return assertP(3)
};
procedure PAndQ(x: int)
@@ -26,8 +27,8 @@ procedure PAndQ(x: int)
opaque
ensures P(x) && Q(x);
-function needsPAndQsInvoke2(): int {
- assertP(3)
+procedure needsPAndQsInvoke2(): int {
+ return assertP(3)
};
// The axiom fires because P(x) appears in the goal.
@@ -44,8 +45,8 @@ procedure axiomDoesNotFireBecauseOfPattern(x: int)
//^^^^^^^^^^^ error: assertion could not be proved
};
-function A(x: int, y: real): bool;
-function B(x: real): bool;
+procedure A(x: int, y: real): bool;
+procedure B(x: real): bool;
procedure AAndB(x: int, y: real)
invokeOn A(x, y)
opaque
@@ -64,7 +65,7 @@ procedure invokeB(x: int, y :real)
//^^^^^^^^^^^ error: assertion could not be proved
};
-function R(x: int): bool;
+procedure R(x: int): bool;
procedure badPostcondition(x: int)
invokeOn R(x)
opaque
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T1_AssertFalse.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T1_AssertFalse.lean
index c509426b73..18fa27d0a6 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T1_AssertFalse.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T1_AssertFalse.lean
@@ -11,6 +11,7 @@ open Strata
/-! ## Failing asserts -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -27,6 +28,7 @@ procedure foo()
/-! ## Assume false makes assert false trivially provable -/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T20_TransparentBody.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T20_TransparentBody.lean
index 1b3ae3aa75..1a8db064e8 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T20_TransparentBody.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T20_TransparentBody.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T21_ExitMultiPathAssert.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T21_ExitMultiPathAssert.lean
index 855f23eb43..a4041dbec4 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T21_ExitMultiPathAssert.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T21_ExitMultiPathAssert.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_ArityMismatch.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_ArityMismatch.lean
index 1b4a0ef1a7..6aa57a9703 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_ArityMismatch.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_ArityMismatch.lean
@@ -24,7 +24,7 @@ info: 32:16-23 error: call to 'f' expects 1 argument(s) but 2 were provided
#eval testLaurel (showLocations := true) <|
#strata
program Laurel;
-function f(x: int): int { x };
+procedure f(x: int): int { return x };
procedure caller()
opaque
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_MultipleReturns.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_MultipleReturns.lean
index 3897cae872..5fac9f2338 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_MultipleReturns.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T22_MultipleReturns.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T23_IncrDecr.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T23_IncrDecr.lean
index 12871b4353..e77ba754de 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T23_IncrDecr.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T23_IncrDecr.lean
@@ -32,6 +32,7 @@ parameterization which interacts poorly with counterexample search
for the failing tests in this file).
-/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -132,7 +133,7 @@ procedure forLoopStep()
// --- More complex scenarios -------------------------------------------------
-function double(n: int): int { 2 * n };
+procedure double(n: int): int { return 2 * n };
procedure incrementAsFunctionArgument()
opaque
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T23b_IncrDecrField.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T23b_IncrDecrField.lean
index 4e76ccd357..05854f5c3a 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T23b_IncrDecrField.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T23b_IncrDecrField.lean
@@ -37,6 +37,7 @@ postfix incr/decr ops (`prec(90)`), so `#` binds tighter than `++` and
valid; `parenFreeFieldIncrDecr` below covers the paren-free form.
-/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressions.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressions.lean
index 23885ec665..226c3a7412 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressions.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressions.lean
@@ -192,4 +192,17 @@ procedure liftWithMultipleOutputs() opaque {
var x: int := { assign var y: int, var z: int := hasMultipleOutputs() ; y + z }
};
+// Regression: When `LiftImperativeExpressions`
+// hoists the imperative `impLen` call out of the then-branch comparison, it
+// must keep the comparison as the branch's value (a `bool`); dropping it leaves
+// the lifted temp as the branch value, which then mismatches the `else true`
+// (`bool`) branch and produces an internal `'if' branches have incompatible
+// types` resolution error after the pass.
+procedure imperativeCall(l: int) returns (r: int)
+ opaque;
+
+procedure imperativeCallInThenBranch(l: int) returns (r: bool)
+ opaque
+if l >= 0 then imperativeCall(l) == l else true;
+
#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError.lean
index 5862e9b93f..c27da6dc63 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError.lean
@@ -15,7 +15,7 @@ open Strata
-- pipeline helper because the expected diagnostics are not pure resolution
-- errors.
-#eval testLaurel <|
+#eval testLaurelKeepIntermediates <|
#strata
program Laurel;
procedure hasMutatingAssignment(): int
@@ -26,21 +26,16 @@ procedure hasMutatingAssignment(): int
x
};
-function functionWithMutatingAssignment(x: int): int
-{
- x := x + 1
-//^^^^^^^^^^ error: destructive assignments are not supported in transparent bodies or contracts
-};
-
-function functionWithWhile(x: int): int
+procedure functionWithWhile(x: int): int
{
while(false) {};
-//^^^^^^^^^^^^^^^ error: loops are not supported in functions or contracts
- 3
+//^^^^^^^^^^^^^^^ error: loops are not supported in transparent bodies or contracts
+ return 3
};
-function functionCallingHasMutationAssignment(x: int): int
+
+procedure callsHasMutatingAssignment(x: int): int
{
- hasMutatingAssignment()
+ return hasMutatingAssignment()
};
procedure impureContractIsLegal1(x: int)
@@ -49,12 +44,4 @@ procedure impureContractIsLegal1(x: int)
{
assert hasMutatingAssignment() == 1
};
-
-procedure impureContractIsNotLegal2(x: int)
- requires (x := 2) == 2
-// ^^^^^^ error: destructive assignments are not supported in transparent bodies or contracts
- opaque
-{
- assert (x := 2) == 2
-};
#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError2.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError2.lean
new file mode 100644
index 0000000000..0af4f2e3fc
--- /dev/null
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T2_ImpureExpressionsError2.lean
@@ -0,0 +1,35 @@
+/-
+ Copyright Strata Contributors
+
+ SPDX-License-Identifier: Apache-2.0 OR MIT
+-/
+module
+
+meta import StrataTest.Util.TestLaurel
+
+open StrataTest.Util
+
+meta section
+
+#eval testLaurel <|
+#strata
+program Laurel;
+
+procedure transparentWithMutatingAssignment(x: int): int
+{
+ x := x + 1;
+//^^^^^^^^^^ error: destructive assignments are not supported in transparent bodies or contracts
+ return 3
+};
+
+procedure impureContractIsNotLegal2(x: int)
+ requires (x := 2) == 2
+// ^^^^^^ error: destructive assignments are not supported in transparent bodies or contracts
+ opaque
+{
+ assert (x := 2) == 2
+};
+
+#end
+
+end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlow.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlow.lean
index 83f3a702a2..28e37c0296 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlow.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlow.lean
@@ -9,9 +9,31 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
+
+procedure letExpressionsInTransparent() returns (r: int) {
+ var x: int := 0;
+ var y: int := x + 1;
+ var z: int := y + 1;
+ return z
+};
+
+procedure callLetExpressionsInTransparent() opaque {
+ var x: int := letExpressionsInTransparent();
+ assert x == 2
+};
+
+procedure assertAndAssumeInTransparent(a: int) returns (r: int)
+{
+ assert 2 == 3;
+//^^^^^^^^^^^^^ error: assertion does not hold
+ assume true;
+ return a
+};
+
procedure returnAtEnd(x: int) returns (r: int) {
if x > 0 then {
if x == 1 then {
@@ -24,9 +46,9 @@ procedure returnAtEnd(x: int) returns (r: int) {
}
};
-function elseWithCall(): int
+procedure elseWithCall(): int
{
- if true then 3 else returnAtEnd(3)
+ return if true then 3 else returnAtEnd(3)
};
procedure testFunctions()
@@ -34,11 +56,11 @@ procedure testFunctions()
{
assert returnAtEnd(1) == 1;
assert returnAtEnd(1) == 2;
-//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion could not be proved
+//^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
assert guardInFunction(1) == 1;
assert guardInFunction(1) == 2
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion could not be proved
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
};
procedure guardInFunction(x: int) returns (r: int)
@@ -56,22 +78,36 @@ procedure guardInFunction(x: int) returns (r: int)
procedure guards(a: int) returns (r: int)
{
- var b: int := a + 2;
- if b > 2 then {
- var c: int := b + 3;
- if c > 3 then {
- return c + 4
+ if a > 2 then {
+ if a > 3 then {
+ return 4
};
- var d: int := c + 5;
- return d + 6
+ return 6
};
- var e: int := b + 1;
- assert e <= 3;
- assert e < 3;
+ assert a <= 2;
+ assert a < 2;
//^^^^^^^^^^^^ error: assertion does not hold
- return e
+ return 5
};
+//
+// procedure guards(a: int) returns (r: int)
+// {
+// var b: int := a + 2;
+// if b > 2 then {
+// var c: int := b + 3;
+// if c > 3 then {
+// return c + 4
+// };
+// //var d: int := c + 5;
+// return d + 6
+// };
+// //var e: int := b + 1;
+// assert e <= 3;
+// assert e < 3;
+// return e
+// };
+
procedure dag(a: int) returns (r: int)
opaque
{
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlowError.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlowError.lean
index a1d8f03ad7..850b05e499 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlowError.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T3_ControlFlowError.lean
@@ -12,30 +12,10 @@ open Strata
#eval testLaurel <|
#strata
program Laurel;
-function assertAndAssumeInFunctions(a: int) returns (r: int)
-{
- assert 2 == 3;
-//^^^^^^^^^^^^^ error: asserts are not YET supported in functions or contracts
- assume true;
-//^^^^^^^^^^^ error: assumes are not YET supported in functions or contracts
- a
-};
-
-function letsInFunction() returns (r: int) {
- var x: int := 0;
- var y: int := x + 1;
- var z: int := y + 1;
- z
-};
-
-procedure callLetsInFunction() opaque {
- var x: int := letsInFunction();
- assert x == 2
-};
-function localVariableWithoutInitializer(): int {
+procedure localVariableWithoutInitializer(): int {
var x: int;
-//^^^^^^^^^^ error: local variables in functions must have initializers
- 3
+//^^^^^^^^^^ error: local variables must have initializers in transparent bodies or contracts
+ return 3
};
#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T4b_Exit.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T4b_Exit.lean
index 50d7e25c96..ef6d5f8e29 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T4b_Exit.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T4b_Exit.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T5_ProcedureCalls.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T5_ProcedureCalls.lean
index 7f68e3caf6..be801af5cb 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T5_ProcedureCalls.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T5_ProcedureCalls.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -40,15 +41,4 @@ procedure fooProof()
// assert x == y;
};
-function aFunction(x: int): int
-{
- x
-};
-
-procedure aFunctionCaller()
- opaque
-{
- var x: int := aFunction(3);
- assert x == 3
-};
#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T6_Preconditions.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T6_Preconditions.lean
index da9a3d6713..d2ec938a0d 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T6_Preconditions.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T6_Preconditions.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -33,20 +34,6 @@ procedure caller()
var y: int := hasRequires(3)
};
-function aFunctionWithPrecondition(x: int): int
- requires x == 10
-{
- x
-};
-
-procedure aFunctionWithPreconditionCaller()
- opaque
-{
- var x: int := aFunctionWithPrecondition(0)
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
-// Error ranges are too wide because Core does not use expression locations
-};
-
procedure multipleRequires(x: int, y: int) returns (r: int)
requires x > 0
requires y > 0
@@ -63,18 +50,4 @@ procedure multipleRequiresCaller()
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: precondition could not be proved
};
-function funcMultipleRequires(x: int, y: int): int
- requires x > 0
- requires y > 0
-{
- x + y
-};
-
-procedure funcMultipleRequiresCaller()
- opaque
-{
- var a: int := funcMultipleRequires(1, 2);
- var b: int := funcMultipleRequires(1, -1)
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
-};
#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_Postconditions.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_Postconditions.lean
index 08e32bff17..877dc94c9f 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_Postconditions.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_Postconditions.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_PostconditionsErrors.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_PostconditionsErrors.lean
deleted file mode 100644
index c344719e35..0000000000
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T8_PostconditionsErrors.lean
+++ /dev/null
@@ -1,36 +0,0 @@
-/-
- Copyright Strata Contributors
-
- SPDX-License-Identifier: Apache-2.0 OR MIT
--/
-
-import StrataTest.Util.TestLaurel
-
-open StrataTest.Util
-open Strata
-
-/-! ## Functions with postconditions are not yet supported -/
-
-#eval testLaurel <|
-#strata
-program Laurel;
-
-function opaqueFunction(x: int) returns (r: int)
-// ^^^^^^^^^^^^^^ error: functions with postconditions are not yet supported
-// The above limitation is because Core does not yet support functions with postconditions
- requires x > 0
- opaque
- ensures r > 0
-{
- x
-};
-
-procedure callerOfOpaqueFunction()
- opaque
-{
- var x: int := opaqueFunction(3);
- assert x > 0;
-// The following assertion should fail but does not
- assert x == 3
-};
-#end
diff --git a/StrataTest/Languages/Laurel/Examples/Fundamentals/T8b_EarlyReturnPostconditions.lean b/StrataTest/Languages/Laurel/Examples/Fundamentals/T8b_EarlyReturnPostconditions.lean
index 66a71def1c..7e2ab91e17 100644
--- a/StrataTest/Languages/Laurel/Examples/Fundamentals/T8b_EarlyReturnPostconditions.lean
+++ b/StrataTest/Languages/Laurel/Examples/Fundamentals/T8b_EarlyReturnPostconditions.lean
@@ -11,6 +11,7 @@ open Strata
/-! ## Correct early return -/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -27,6 +28,7 @@ procedure earlyReturnCorrect(x: int) returns (r: int)
/-! ## Buggy early return: postcondition fails -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T10_CompositeBvField.lean b/StrataTest/Languages/Laurel/Examples/Objects/T10_CompositeBvField.lean
index d60053efef..c0e0d40289 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T10_CompositeBvField.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T10_CompositeBvField.lean
@@ -14,6 +14,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T1_MutableFields.lean b/StrataTest/Languages/Laurel/Examples/Objects/T1_MutableFields.lean
index 4bca875744..a2b1333a16 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T1_MutableFields.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T1_MutableFields.lean
@@ -9,7 +9,8 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
-#eval testLaurelKeepIntermediates
+#guard_msgs (drop info) in
+#eval testLaurel
#strata
program Laurel;
composite Container {
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T1b_HeapMutatingValueReturn.lean b/StrataTest/Languages/Laurel/Examples/Objects/T1b_HeapMutatingValueReturn.lean
index 1a18972604..21f3dbd4a9 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T1b_HeapMutatingValueReturn.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T1b_HeapMutatingValueReturn.lean
@@ -11,6 +11,7 @@ open Strata
/-! ## Correct heap mutating value return -/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -30,6 +31,7 @@ procedure setAndReturn(c: Container, x: int) returns (r: int)
/-! ## Buggy: postcondition r == x + 1 cannot hold when r := x -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T2_ModifiesClauses.lean b/StrataTest/Languages/Laurel/Examples/Objects/T2_ModifiesClauses.lean
index ed9bfc9301..fd7f32dd0b 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T2_ModifiesClauses.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T2_ModifiesClauses.lean
@@ -19,6 +19,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T2c_ModifiesClausesArrayTheory.lean b/StrataTest/Languages/Laurel/Examples/Objects/T2c_ModifiesClausesArrayTheory.lean
index b6c820e22d..598ddba1a5 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T2c_ModifiesClausesArrayTheory.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T2c_ModifiesClausesArrayTheory.lean
@@ -103,7 +103,7 @@ procedure modifyContainerWithoutPermission2(c: Container, d: Container)
};
procedure modifyContainerWithoutPermission3(c: Container, d: Container)
-// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: modifies clause could not be proved
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: modifies clause does not hold
opaque
modifies d
{
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T2d_ModifiesFrameSoundness.lean b/StrataTest/Languages/Laurel/Examples/Objects/T2d_ModifiesFrameSoundness.lean
index a4eb274bd9..4af91f844b 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T2d_ModifiesFrameSoundness.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T2d_ModifiesFrameSoundness.lean
@@ -183,7 +183,7 @@ procedure callerModifiedNotPreserved()
var x: int := c#value;
var b: bool := bodyModifier(c);
assert x == c#value
-//^^^^^^^^^^^^^^^^^^^ error: assertion could not be proved
+//^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
};
procedure callerExactValuePreserved()
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T2e_ModifiesArrayTheoryPerf.lean b/StrataTest/Languages/Laurel/Examples/Objects/T2e_ModifiesArrayTheoryPerf.lean
index 9b1acea61d..01047ebabb 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T2e_ModifiesArrayTheoryPerf.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T2e_ModifiesArrayTheoryPerf.lean
@@ -32,7 +32,9 @@ open Strata
-- ∀ frame (array theory off): the chain defeats quantifier instantiation.
#eval testLaurel
(options := { defaultLaurelTestOptions with
- translateOptions := { defaultLaurelTestOptions.translateOptions with enumeratedModifiesClauses := false },
+ translateOptions := { defaultLaurelTestOptions.translateOptions with
+ enumeratedModifiesClauses := false
+ alwaysCallCoreFunctions := false },
verifyOptions := { defaultLaurelTestOptions.verifyOptions with useArrayTheory := false } }) <|
#strata
program Laurel;
@@ -78,7 +80,10 @@ procedure stress()
-- Quantifier-free frame (--use-array-theory): the same program verifies.
#eval testLaurel
(options := { defaultLaurelTestOptions with
- translateOptions := { defaultLaurelTestOptions.translateOptions with enumeratedModifiesClauses := true },
+ translateOptions := { defaultLaurelTestOptions.translateOptions with
+ enumeratedModifiesClauses := true
+ alwaysCallCoreFunctions := false
+ },
verifyOptions := { defaultLaurelTestOptions.verifyOptions with useArrayTheory := true } }) <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T5_inheritance.lean b/StrataTest/Languages/Laurel/Examples/Objects/T5_inheritance.lean
index 3899f9e3a1..80b1d97207 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T5_inheritance.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T5_inheritance.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T6_Datatypes.lean b/StrataTest/Languages/Laurel/Examples/Objects/T6_Datatypes.lean
index 896474404b..a0bc1e12e0 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T6_Datatypes.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T6_Datatypes.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -51,14 +52,13 @@ procedure unsafeDestructor() opaque {
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assertion does not hold
};
-// Datatype in function
-function listHead(xs: IntList): int
+procedure listHead(xs: IntList): int
requires IntList..isCons(xs)
{
- IntList..head(xs)
+ return IntList..head(xs)
};
-procedure testFunction() opaque {
+procedure testListHead() opaque {
var xs: IntList := Cons(10, Nil());
var h: int := listHead(xs);
assert h == 10
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T7_InstanceProcedures.lean b/StrataTest/Languages/Laurel/Examples/Objects/T7_InstanceProcedures.lean
index 08638d1c7f..beec222d33 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T7_InstanceProcedures.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T7_InstanceProcedures.lean
@@ -22,6 +22,7 @@ open Strata
/-! ## 1. Basic instance method call: `c#reset()` -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -49,6 +50,7 @@ procedure useCounter()
Without per-composite scoping, `tick` would collide in the global scope
during pre-registration. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -93,6 +95,7 @@ procedure runClock()
/-! ## 3. Method with multiple parameters: `c#setTo(v)` -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -119,6 +122,7 @@ procedure useCell(x: int)
/-! ## 4. Boolean-typed field updated through an instance method, and read
back via field access in the caller's `assert`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -147,6 +151,7 @@ procedure useWidget()
only `a`; the unused `b` parameter is included to confirm method
dispatch picks the right receiver. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -174,6 +179,7 @@ procedure resetTwoCounters(a: Counter, b: Counter)
confirms an extra (unused) method parameter doesn't break call
dispatch or framing. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -200,6 +206,7 @@ procedure useAccount()
/-! ## 7. Instance method called through a field-selected receiver:
`obj#field#method()`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -225,6 +232,7 @@ procedure useOuter()
/-! ## 8. Chained field read: `obj#field#x`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T8_NonCompositeModifies.lean b/StrataTest/Languages/Laurel/Examples/Objects/T8_NonCompositeModifies.lean
index 87a6e2424d..3c491e1c73 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T8_NonCompositeModifies.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T8_NonCompositeModifies.lean
@@ -16,6 +16,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/Objects/T9_ValueReturningInstanceMethods.lean b/StrataTest/Languages/Laurel/Examples/Objects/T9_ValueReturningInstanceMethods.lean
index fb7249ddb7..d9ebc7d6d5 100644
--- a/StrataTest/Languages/Laurel/Examples/Objects/T9_ValueReturningInstanceMethods.lean
+++ b/StrataTest/Languages/Laurel/Examples/Objects/T9_ValueReturningInstanceMethods.lean
@@ -32,6 +32,7 @@ open Strata
/-! ## 1. Basic: instance method body returns a field via `return expr`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -56,6 +57,7 @@ procedure useGet()
/-! ## 2. Return a computed expression. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -80,6 +82,7 @@ procedure useIncd()
/-! ## 3. Return an expression that uses a (non-self) parameter. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -105,6 +108,7 @@ procedure useAddTo()
/-! ## 4. Conditional / early returns: a valued `return` in each branch of an
if-then-else inside the method body. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -133,6 +137,7 @@ procedure useClampPos()
/-! ## 5. Method that mutates a field (modifies clause) and then returns. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -161,6 +166,7 @@ procedure useSetAndGet()
/-! ## 6. Boolean return type. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -186,6 +192,7 @@ procedure useIsPos()
/-! ## 7. Two composites sharing a method name, both with value-returning
bodies. Confirms lifting + value-return elimination keep them distinct. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -224,6 +231,7 @@ procedure useBoth()
/-! ## 8. Value-returning method invoked through a field-selected receiver:
`o#inner#getX()`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -252,6 +260,7 @@ procedure useOuter()
/-! ## 9. Local variable in the body before a valued return. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -278,6 +287,7 @@ procedure useDoubleV()
/-! ## 10. Negative: valued return in an instance method with NO output
parameter is rejected by `EliminateValueInReturns`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
@@ -295,6 +305,7 @@ composite C {
/-! ## 11. Negative: valued return in an instance method with MULTIPLE output
parameters is rejected by `EliminateValueInReturns`. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T1_Decimals.lean b/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T1_Decimals.lean
index 0a1280653c..b574c79b31 100644
--- a/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T1_Decimals.lean
+++ b/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T1_Decimals.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_String.lean b/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_String.lean
index 0528672ee8..0a718b7b92 100644
--- a/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_String.lean
+++ b/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_String.lean
@@ -10,6 +10,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_StringConcatLifting.lean b/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_StringConcatLifting.lean
index cc3756bed8..c223b3e3bd 100644
--- a/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_StringConcatLifting.lean
+++ b/StrataTest/Languages/Laurel/Examples/PrimitiveTypes/T2_StringConcatLifting.lean
@@ -9,6 +9,7 @@ import StrataTest.Util.TestLaurel
open StrataTest.Util
open Strata
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/IncrDecrLiftTest.lean b/StrataTest/Languages/Laurel/IncrDecrLiftTest.lean
index 44731ab35d..3dbdf23a29 100644
--- a/StrataTest/Languages/Laurel/IncrDecrLiftTest.lean
+++ b/StrataTest/Languages/Laurel/IncrDecrLiftTest.lean
@@ -46,7 +46,8 @@ def parseLowerIncrDecr (input : String) : IO Program := do
/-- Statement form: `x++;` and `--x` as statements. Prefix (`--x`) produces
a clean assignment. Postfix (`x++`) emits the same assignment-based form as
the expression position; the lift pass snapshots the pre-assignment value
- even though it is unused here. -/
+ even though it is unused here. The unused postfix result (`x - 1`) is kept
+ as a pure expression statement and discarded later by the Core translator. -/
def stmtFormProgram : String := r"
procedure stmtForm()
opaque
@@ -64,6 +65,7 @@ info: procedure stmtForm()
var x: int := 0;
var $x_0: int := x;
x := x + 1;
+ x - 1;
x := x - 1
};
-/
diff --git a/StrataTest/Languages/Laurel/IncrDecrTypeRejectionTest.lean b/StrataTest/Languages/Laurel/IncrDecrTypeRejectionTest.lean
index 33c166b4c9..ad6cda63b6 100644
--- a/StrataTest/Languages/Laurel/IncrDecrTypeRejectionTest.lean
+++ b/StrataTest/Languages/Laurel/IncrDecrTypeRejectionTest.lean
@@ -21,6 +21,7 @@ open Strata
/-! ## Rejected: `++`/`--` on unsupported element types -/
+#guard_msgs (drop info) in
#eval testLaurelResolution <|
#strata
program Laurel;
@@ -43,6 +44,7 @@ procedure incrFloat(g: float64) opaque {
/-! ## Accepted: `++`/`--` on an int-based constrained type (e.g. `nat`) -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/LiftHolesTest.lean b/StrataTest/Languages/Laurel/LiftHolesTest.lean
index a7d9879b9d..680c1353b6 100644
--- a/StrataTest/Languages/Laurel/LiftHolesTest.lean
+++ b/StrataTest/Languages/Laurel/LiftHolesTest.lean
@@ -6,7 +6,7 @@
/-
Tests that the eliminateHoles pass correctly replaces `.Hole` nodes with calls
-to freshly generated uninterpreted functions, with types inferred from context.
+to freshly generated uninterpreted procedures, with types inferred from context.
-/
import StrataTest.Util.TestLaurel
@@ -32,7 +32,7 @@ private def parseElimAndPrint (program : StrataDDM.Program) : IO Unit := do
-- Hole in Add arg inside typed local variable → int.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
@@ -52,7 +52,7 @@ procedure test()
-- Bare Hole as Assign Declare initializer → replaced with call (no longer preserved as havoc).
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
@@ -72,7 +72,7 @@ procedure test()
-- Hole in comparison arg inside assert → int (inferred from sibling literal).
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
@@ -92,7 +92,7 @@ procedure test()
-- Hole directly as assert condition → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
@@ -112,7 +112,7 @@ procedure test()
-- Hole directly as assume condition → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
@@ -132,16 +132,16 @@ procedure test()
-- Hole as if-then-else condition → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
opaque
{
if $hole_0()
- then {
- assert true
- }
+ then {
+ assert true
+ }
};
-/
#guard_msgs in
@@ -155,15 +155,15 @@ procedure test()
-- Hole in then-branch of if-then-else inside typed local variable → int.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
opaque
{
var x: int := if true
- then $hole_0()
- else 0
+ then $hole_0()
+ else 0
};
-/
#guard_msgs in
@@ -177,7 +177,7 @@ procedure test()
-- Hole as while-loop condition → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
@@ -199,7 +199,7 @@ procedure test()
-- Hole as while-loop invariant → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
@@ -224,7 +224,7 @@ procedure test()
-- Hole in And arg inside assert → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
@@ -244,7 +244,7 @@ procedure test()
-- Hole in Neg inside typed local variable → int.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
@@ -264,7 +264,7 @@ procedure test()
-- Hole in StrConcat inside typed local variable → string.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: string)
opaque;
procedure test()
@@ -282,12 +282,12 @@ procedure test()
/-! ## Multiple holes -/
--- Two holes in Add → both int, separate functions.
+-- Two holes in Add → both int, separate procedures.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
-function $hole_1()
+procedure $hole_1()
returns ($result: int)
opaque;
procedure test()
@@ -307,10 +307,10 @@ procedure test()
-- Holes across statements: Mul arg (int) then assert condition (bool).
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
-function $hole_1()
+procedure $hole_1()
returns ($result: bool)
opaque;
procedure test()
@@ -333,16 +333,16 @@ procedure test()
-- Hole in Add inside Gt inside if condition → int (inferred from sibling literal 0).
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
opaque
{
if 1 + $hole_0() > 0
- then {
- assert true
- }
+ then {
+ assert true
+ }
};
-/
#guard_msgs in
@@ -356,7 +356,7 @@ procedure test()
-- Hole in Implies inside while invariant → bool.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: bool)
opaque;
procedure test()
@@ -380,7 +380,7 @@ procedure test()
-- Hole in Mul inside typed local variable with real type → real.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: real)
opaque;
procedure test()
@@ -400,9 +400,9 @@ procedure test()
/-! ## Call argument and return type inference -/
--- Hole in comparison with variable sibling → hole function takes the procedure's params.
+-- Hole in comparison with variable sibling → hole procedure takes the procedure's params.
/--
-info: function $hole_0(n: int)
+info: procedure $hole_0(n: int)
returns ($result: int)
opaque;
procedure test(n: int)
@@ -420,14 +420,14 @@ procedure test(n: int)
{ assert n > > };
#end
-/-! ## Holes in functions -/
+/-! ## Holes in procedures -/
--- Hole in function body → same treatment as procedures.
+-- Hole in procedure body → same treatment as procedures.
/--
-info: function $hole_0(x: int)
+info: procedure $hole_0(x: int)
returns ($result: int)
opaque;
-function test(x: int): int
+procedure test(x: int): int
{
$hole_0(x)
};
@@ -436,7 +436,7 @@ function test(x: int): int
#eval! parseElimAndPrint
#strata
program Laurel;
-function test(x: int): int
+procedure test(x: int): int
{ > };
#end
@@ -461,7 +461,7 @@ procedure test()
-- Mixed: det hole eliminated, nondet hole preserved.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: int)
opaque;
procedure test()
@@ -480,7 +480,7 @@ procedure test()
{ var x: int := >; assert ?> };
#end
--- Nondet hole in function → should be rejected (not tested here since
+-- Nondet hole in procedure → should be rejected (not tested here since
-- the error occurs at Core translation time, which requires the full pipeline).
/-! ## Holes inside datatype destructor / tester arguments -/
@@ -491,7 +491,7 @@ procedure test()
-- parent datatype's resolved Identifier (with `uniqueId`), so this works
-- without textual decoding of the override name.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: IntList)
opaque;
procedure test()
@@ -509,7 +509,7 @@ procedure test() { var x: int := IntList..head(>) };
-- Hole as argument to an unsafe `!` destructor → same datatype recovery.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: IntList)
opaque;
procedure test()
@@ -527,7 +527,7 @@ procedure test() { var x: int := IntList..head!(>) };
-- Hole as argument to a tester → typed as the parent datatype.
/--
-info: function $hole_0()
+info: procedure $hole_0()
returns ($result: IntList)
opaque;
procedure test()
diff --git a/StrataTest/Languages/Laurel/MapStmtExprTest.lean b/StrataTest/Languages/Laurel/MapStmtExprTest.lean
index eee5f05fa0..32656be094 100644
--- a/StrataTest/Languages/Laurel/MapStmtExprTest.lean
+++ b/StrataTest/Languages/Laurel/MapStmtExprTest.lean
@@ -8,16 +8,21 @@
Tests for the generic `mapStmtExprM` traversal. Verifies that `mapStmtExpr id`
is the identity: applying it to a parsed program produces identical output.
-/
+module
-import StrataTest.Util.TestLaurel
-import Strata.Languages.Laurel.MapStmtExpr
-import Strata.Languages.Laurel.Resolution
+meta import StrataTest.Util.TestLaurel
+meta import Strata.Languages.Laurel.MapStmtExpr
+meta import Strata.Languages.Laurel.Resolution
+meta import Strata.Languages.Laurel.Grammar
+meta import StrataDDM.Integration.Lean.HashCommands
open Strata
open StrataTest.Util
namespace Strata.Laurel
+meta section
+
private def parseAndResolve (program : StrataDDM.Program) : IO Program := do
let laurelProgram ← translateLaurel program
pure (resolve laurelProgram).program
@@ -75,4 +80,6 @@ procedure test(x: int, b: bool) returns (r: int)
};
#end
+end
+
end Strata.Laurel
diff --git a/StrataTest/Languages/Laurel/ModifiesFrameExitChecksTest.lean b/StrataTest/Languages/Laurel/ModifiesFrameExitChecksTest.lean
index 51087b7eec..d7b7ea2b34 100644
--- a/StrataTest/Languages/Laurel/ModifiesFrameExitChecksTest.lean
+++ b/StrataTest/Languages/Laurel/ModifiesFrameExitChecksTest.lean
@@ -23,10 +23,6 @@ private def assertCount (e : StmtExprMd) : Nat :=
(reprStr e |>.splitOn "assert").length - 1
#guard assertCount (insertFrameChecks default frame (node (.Return none))) == 2
-#guard assertCount (insertFrameChecks default frame (node (.Exit bodyLabel))) == 2
#guard assertCount (insertFrameChecks default frame (node (.Exit "loop"))) == 1
-#guard assertCount
- (insertFrameChecks default frame
- (node (.Block [node (.Exit bodyLabel), node (.Exit "loop")] none))) == 2
end StrataTest.Laurel.ModifiesFrameExitChecks
diff --git a/StrataTest/Languages/Laurel/ResolutionTypeCheckTests.lean b/StrataTest/Languages/Laurel/ResolutionTypeCheckTests.lean
index e3888db451..32b94132a1 100644
--- a/StrataTest/Languages/Laurel/ResolutionTypeCheckTests.lean
+++ b/StrataTest/Languages/Laurel/ResolutionTypeCheckTests.lean
@@ -31,7 +31,7 @@ procedure voidReturn(x: int)
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(x: int): int {
+procedure foo(x: int): int {
if x then 1 else 0
// ^ error: expected 'bool', got 'int'
};
@@ -72,7 +72,7 @@ procedure wh() opaque {
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(x: int, y: bool): bool {
+procedure foo(x: int, y: bool): bool {
x && y
//^ error: expected 'bool', got 'int'
};
@@ -83,7 +83,7 @@ function foo(x: int, y: bool): bool {
#eval testLaurelResolution <|
#strata
program Laurel;
-function cmp(x: string, y: int): bool {
+procedure cmp(x: string, y: int): bool {
x < y
//^ error: '<' expected a numeric type, got 'string'
};
@@ -116,8 +116,8 @@ procedure foo(): int {
#eval testLaurelResolution <|
#strata
program Laurel;
-function bar(x: int): int { x };
-function foo(): int {
+procedure bar(x: int): int { x };
+procedure foo(): int {
bar(true)
// ^^^^ error: expected 'int', got 'bool'
};
@@ -128,7 +128,7 @@ function foo(): int {
#eval testLaurelResolution <|
#strata
program Laurel;
-function cmp(x: int, y: string): bool {
+procedure cmp(x: int, y: string): bool {
x == y
//^^^^^^ error: cannot compare 'int' with 'string' using '=='
};
@@ -225,7 +225,7 @@ cleanly (no diagnostics). -/
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(c: bool): bool {
+procedure foo(c: bool): bool {
(if c then 1 else 2) == 3
};
#end
@@ -233,7 +233,7 @@ function foo(c: bool): bool {
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(): bool {
+procedure foo(): bool {
{ 1 } == 1
};
#end
@@ -247,7 +247,7 @@ and synthesizes `Unknown` to suppress cascading errors. -/
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(c: bool): bool {
+procedure foo(c: bool): bool {
(if c then 1 else true) == 3
// ^^^^^^^^^^^^^^^^^^^^^ error: 'if' branches have incompatible types 'int' and 'bool'
};
@@ -269,7 +269,7 @@ errored.) -/
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(c: bool): bool {
+procedure foo(c: bool): bool {
(if c then > else "x") < 1
// ^^^^^^^^^^^^^^^^^^^^^^ error: '<' expected a numeric type, got 'string'
};
@@ -278,7 +278,7 @@ function foo(c: bool): bool {
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(c: bool): bool {
+procedure foo(c: bool): bool {
(if c then "x" else >) < 1
// ^^^^^^^^^^^^^^^^^^^^^^ error: '<' expected a numeric type, got 'string'
};
@@ -294,7 +294,7 @@ than collapsing to `Unknown`. So `if c then > else 5` synthesizes a usable
#eval testLaurelResolution <|
#strata
program Laurel;
-function bar(c: bool): int {
+procedure bar(c: bool): int {
if c then > else 5
};
#end
@@ -328,7 +328,7 @@ rejecting `TBv` and emitting a spurious "expected a numeric type" error.) -/
#eval testLaurelResolution <|
#strata
program Laurel;
-function cmp(x: bv 32, y: bv 32): bool {
+procedure cmp(x: bv 32, y: bv 32): bool {
x < y
};
#end
@@ -343,8 +343,8 @@ arguments) is deliberately not flagged. -/
#eval testLaurelResolution <|
#strata
program Laurel;
-function foo(x: int): int { x };
-function bar(): int {
+procedure foo(x: int): int { x };
+procedure bar(): int {
foo(1, 2)
//^^^^^^^^^ error: call to 'foo' expects 1 argument(s) but 2 were provided
};
@@ -362,7 +362,7 @@ behavior.) -/
#eval testLaurelResolution <|
#strata
program Laurel;
-function bar(): int {
+procedure bar(): int {
nope(1, 2)
//^^^^^^^^^^ error: 'nope' is not defined
};
diff --git a/StrataTest/Languages/Laurel/StrataExpectSmokeTest.lean b/StrataTest/Languages/Laurel/StrataExpectSmokeTest.lean
index ea6ba94d08..e12c9b603b 100644
--- a/StrataTest/Languages/Laurel/StrataExpectSmokeTest.lean
+++ b/StrataTest/Languages/Laurel/StrataExpectSmokeTest.lean
@@ -16,6 +16,7 @@ open Strata
/-! ## Positive smoke test -/
+#guard_msgs (drop info) in
#eval testLaurel
#strata
program Laurel;
@@ -37,6 +38,7 @@ procedure foo() opaque {
/-! ## Negative smoke test: a verifier-level diagnostic. -/
+#guard_msgs (drop info) in
#eval testLaurel <|
#strata
program Laurel;
diff --git a/StrataTest/Languages/Laurel/TypeAliasElimTest.lean b/StrataTest/Languages/Laurel/TypeAliasElimTest.lean
index 6295539cab..2a613012ca 100644
--- a/StrataTest/Languages/Laurel/TypeAliasElimTest.lean
+++ b/StrataTest/Languages/Laurel/TypeAliasElimTest.lean
@@ -32,8 +32,7 @@ private def mkTy (ty : HighType) : HighTypeMd := { val := ty, source := none }
/-- Helper: construct a minimal procedure. -/
private def mkProc (name : String) (inputs : List Parameter) (outputs : List Parameter)
(body : Body := .Transparent ⟨.Block [] none, none⟩) : Procedure :=
- { name := mkId name, inputs, outputs, preconditions := [], decreases := none,
- isFunctional := false, body }
+ { name := mkId name, inputs, outputs, preconditions := [], decreases := none, body }
/-- Helper: run resolve + typeAliasElim on a program. -/
private def resolveAndElim (program : Program) : Program :=
diff --git a/StrataTest/Util/TestLaurel.lean b/StrataTest/Util/TestLaurel.lean
index 718a38bbaa..bc36b7eb8b 100644
--- a/StrataTest/Util/TestLaurel.lean
+++ b/StrataTest/Util/TestLaurel.lean
@@ -3,14 +3,16 @@
SPDX-License-Identifier: Apache-2.0 OR MIT
-/
+module
+public import Strata.Languages.Laurel.Grammar.LaurelGrammar
+public import Strata.Languages.Laurel.LaurelCompilationPipeline
+public import StrataDDM.Integration.Lean.HashCommands
import StrataDDM.Integration.Lean.HashCommands
import StrataDDM.Elab
import StrataDDM.BuiltinDialects.Init
-import Strata.Languages.Laurel.Grammar.LaurelGrammar
import Strata.Languages.Laurel.Grammar.ConcreteToAbstractTreeTranslator
import Strata.Languages.Laurel.Resolution
-import Strata.Languages.Laurel.LaurelCompilationPipeline
import Strata.Languages.Laurel
open Strata
@@ -22,7 +24,7 @@ namespace StrataTest.Util
/-- Translate a `StrataDDM.Program` (typically produced by `#strata`) to a Laurel
`Program`. Used by tests that need to plug in a custom post-translation
pipeline stage; throws if translation fails. -/
-def translateLaurel (program : StrataDDM.Program) : IO Laurel.Program := do
+public def translateLaurel (program : StrataDDM.Program) : IO Laurel.Program := do
match Laurel.TransM.run (Strata.Uri.file "<#strata>") (Laurel.parseProgram program) with
| .error e => throw (IO.userError s!"Translation errors: {e}")
| .ok laurelProgram => pure laurelProgram
@@ -48,7 +50,7 @@ private def renderSnippetLocal (basePos : Nat) (snippet : String)
/-- Default options used by `testLaurel` when the caller doesn't override:
quiet verifier, default solver. Override by passing
`(options := …)` to `testLaurel`. -/
-def defaultLaurelTestOptions : LaurelVerifyOptions :=
+public def defaultLaurelTestOptions : LaurelVerifyOptions :=
{ verifyOptions := .quiet }
/-- Run translate + resolve only on a parsed program. Skips SMT verification.
@@ -342,7 +344,7 @@ private def runAndCheck (block : SourcedProgram)
file-relative `line:col` range (so a `#guard_msgs` golden can pin the
localization), and `showSnippet := true` to also append the snippet-relative
range. (Failure reports always use the file-relative format regardless.) -/
-def testLaurel (block : SourcedProgram)
+public def testLaurel (block : SourcedProgram)
(options : LaurelVerifyOptions := defaultLaurelTestOptions)
(showLocations : Bool := false) (showSnippet : Bool := false) : IO Unit :=
runAndCheck block (runLaurelPipelineRaw · options) showLocations showSnippet
@@ -353,7 +355,7 @@ def buildDir : IO String := do
let cwd ← IO.currentDir
return s!"{cwd}/.lake/build/intermediatePrograms/"
-def testLaurelKeepIntermediates (block : SourcedProgram) : IO Unit := do
+public def testLaurelKeepIntermediates (block : SourcedProgram) : IO Unit := do
let dir ← buildDir
runAndCheck block (runLaurelPipelineRaw · { translateOptions := { keepAllFilesPrefix := dir}})
@@ -365,7 +367,7 @@ def testLaurelKeepIntermediates (block : SourcedProgram) : IO Unit := do
As with `testLaurel`, succeeds silently by default; `showLocations := true`
echoes each diagnostic's file-relative `line:col` range and
`showSnippet := true` appends the snippet-relative range. -/
-def testLaurelResolution (block : SourcedProgram)
+public def testLaurelResolution (block : SourcedProgram)
(showLocations : Bool := false) (showSnippet : Bool := false) : IO Unit :=
runAndCheck block runLaurelResolutionRaw showLocations showSnippet