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