diff --git a/Compiler/ABI.lean b/Compiler/ABI.lean index c85c6a80d..ca1b8922b 100644 --- a/Compiler/ABI.lean +++ b/Compiler/ABI.lean @@ -22,6 +22,8 @@ private def abiTypeString : ParamType → String | .tuple _ => "tuple" | .array t => abiTypeString t ++ "[]" | .fixedArray t n => abiTypeString t ++ "[" ++ toString n ++ "]" + | .adt _ _ => "tuple" -- ADTs are ABI-encoded as static tuples + | .newtypeOf _ baseType => abiTypeString baseType -- Erased to base type -- Uses `fieldTypeToParamType` from CompilationModel (shared, not duplicated). -- Uses `isInteropEntrypointName` from CompilationModel for consistent filtering. @@ -39,6 +41,13 @@ mutual | .tuple elems => let rendered := elems.map (fun ty => renderParam "" ty none) some ("[" ++ String.intercalate ", " rendered ++ "]") + | .adt _ maxFields => + -- ADTs encode as (uint8 tag, uint256 field₀, ..., uint256 fieldₙ) + let tagComponent := renderParam "tag" .uint8 none + let fieldComponents := List.range maxFields |>.map fun i => + renderParam s!"field{i}" .uint256 none + some ("[" ++ String.intercalate ", " (tagComponent :: fieldComponents) ++ "]") + | .newtypeOf _ baseType => abiComponents? baseType | .array t => abiComponents? t | .fixedArray t _ => abiComponents? t | _ => none @@ -126,4 +135,50 @@ def writeContractABIFile (outDir : String) (spec : CompilationModel) : IO Unit : let path := s!"{outDir}/{spec.name}.abi.json" IO.FS.writeFile path (emitContractABIJson spec) +/-- Render the storage layout for a contract as a JSON object. + Includes EIP-7201 namespace when present (#1730, Axis 4 Step 4d). + The output is a JSON object with `"contract"`, `"storageNamespace"`, + and `"fields"` keys. -/ +def emitContractStorageLayoutJson (spec : CompilationModel) : String := + let nsTerm := match spec.storageNamespace with + | some ns => jsonString (toString ns) + | none => "null" + let fieldEntries := renderFields spec.fields 0 + "{" ++ joinJsonFields [ + s!"\"contract\": {jsonString spec.name}", + s!"\"storageNamespace\": {nsTerm}", + s!"\"fields\": [{String.intercalate ", " fieldEntries}]" + ] ++ "}\n" +where + renderFieldType : FieldType → String + | .uint256 => "uint256" + | .address => "address" + | .adt name maxFields => s!"adt({name},{maxFields})" + | .dynamicArray elemType => renderStorageArrayElemType elemType ++ "[]" + | .mappingTyped _ => "mapping" + | .mappingStruct _ _ => "mapping" + | .mappingStruct2 _ _ _ => "mapping" + renderStorageArrayElemType : StorageArrayElemType → String + | .uint256 => "uint256" + | .address => "address" + | .bool => "bool" + | .uint8 => "uint8" + | .bytes32 => "bytes32" + renderFields (fields : List Field) (idx : Nat) : List String := + match fields with + | [] => [] + | f :: rest => + let slot := f.slot.getD idx + let entry := "{" ++ joinJsonFields [ + s!"\"name\": {jsonString f.name}", + s!"\"slot\": {jsonString (toString slot)}", + s!"\"type\": {jsonString (renderFieldType f.ty)}" + ] ++ "}" + entry :: renderFields rest (idx + 1) + +def writeContractStorageLayoutFile (outDir : String) (spec : CompilationModel) : IO Unit := do + IO.FS.createDirAll outDir + let path := s!"{outDir}/{spec.name}.storage.json" + IO.FS.writeFile path (emitContractStorageLayoutJson spec) + end Compiler.ABI diff --git a/Compiler/CompilationModel.lean b/Compiler/CompilationModel.lean index 5c597345a..0da1f3367 100644 --- a/Compiler/CompilationModel.lean +++ b/Compiler/CompilationModel.lean @@ -7,6 +7,7 @@ import Compiler.CompilationModel.Types import Compiler.CompilationModel.AbiHelpers import Compiler.CompilationModel.AbiTypeLayout +import Compiler.CompilationModel.AdtStorageLayout import Compiler.CompilationModel.AbiEncoding import Compiler.CompilationModel.DynamicData import Compiler.CompilationModel.EcmAxiomCollection diff --git a/Compiler/CompilationModel/AbiEncoding.lean b/Compiler/CompilationModel/AbiEncoding.lean index 94a0feec4..30daf4eb2 100644 --- a/Compiler/CompilationModel/AbiEncoding.lean +++ b/Compiler/CompilationModel/AbiEncoding.lean @@ -250,6 +250,25 @@ partial def compileUnindexedAbiEncode ]) ], YulExpr.call "add" [YulExpr.lit 32, YulExpr.ident paddedName]) + | ParamType.adt _ maxFields => + -- ADTs are ABI-encoded as (uint8 tag, uint256 field0, ..., uint256 fieldN) + -- Tag word: load and mask to uint8 + let tagLoaded := dynamicWordLoad dynamicSource srcBase + let tagStore := YulStmt.expr (YulExpr.call "mstore" [ + dstBase, YulExpr.call "and" [tagLoaded, YulExpr.lit 0xFF] + ]) + -- Field words: load consecutive words from source + let fieldStores := (List.range maxFields).map fun i => + let srcOff := YulExpr.call "add" [srcBase, YulExpr.lit ((i + 1) * 32)] + let dstOff := YulExpr.call "add" [dstBase, YulExpr.lit ((i + 1) * 32)] + YulStmt.expr (YulExpr.call "mstore" [dstOff, dynamicWordLoad dynamicSource srcOff]) + let totalBytes := 32 * (1 + maxFields) + pure (tagStore :: fieldStores, YulExpr.lit totalBytes) + + | ParamType.newtypeOf _ baseType => + -- Newtypes erased to base type (#1727 Step 3b) + compileUnindexedAbiEncode dynamicSource baseType srcBase dstBase stem + def revertWithCustomError (dynamicSource : DynamicDataSource) (errorDef : ErrorDef) (sourceArgs : List Expr) (args : List YulExpr) : Except String (List YulStmt) := do @@ -282,6 +301,25 @@ def revertWithCustomError (dynamicSource : DynamicDataSource) | ParamType.uint256 | ParamType.int256 | ParamType.uint8 | ParamType.address | ParamType.bool | ParamType.bytes32 => let encoded ← encodeStaticCustomErrorArg errorDef.name ty argExpr pure [YulStmt.expr (YulExpr.call "mstore" [YulExpr.lit headOffset, encoded])] + | ParamType.adt _ maxFields => + match srcExpr with + | Expr.param name => + let tagStore := YulStmt.expr (YulExpr.call "mstore" [ + YulExpr.lit headOffset, + YulExpr.call "and" [argExpr, YulExpr.lit 0xFF] + ]) + let fieldStores := (List.range maxFields).map fun fieldIdx => + YulStmt.expr (YulExpr.call "mstore" [ + YulExpr.lit (headOffset + (fieldIdx + 1) * 32), + YulExpr.ident s!"{name}_f{fieldIdx}" + ]) + pure (tagStore :: fieldStores) + | _ => + throw s!"Compilation error: custom error '{errorDef.name}' parameter of type {repr ty} currently requires direct parameter reference ({issue586Ref})." + | ParamType.newtypeOf _ baseType => + -- Newtypes erased to base type (#1727 Step 3b) + let encoded ← encodeStaticCustomErrorArg errorDef.name baseType argExpr + pure [YulStmt.expr (YulExpr.call "mstore" [YulExpr.lit headOffset, encoded])] | ParamType.tuple _ | ParamType.fixedArray _ _ => match srcExpr with | Expr.param name => diff --git a/Compiler/CompilationModel/AbiHelpers.lean b/Compiler/CompilationModel/AbiHelpers.lean index ccec333a2..bcd8743d6 100644 --- a/Compiler/CompilationModel/AbiHelpers.lean +++ b/Compiler/CompilationModel/AbiHelpers.lean @@ -56,6 +56,10 @@ mutual | ParamType.array t => paramTypeToSolidityString t ++ "[]" | ParamType.fixedArray t n => paramTypeToSolidityString t ++ "[" ++ toString n ++ "]" | ParamType.bytes => "bytes" + | ParamType.adt _name maxFields => + -- ABI-encoded as static tuple: (uint8, uint256, ..., uint256) + "(" ++ String.intercalate "," ("uint8" :: List.replicate maxFields "uint256") ++ ")" + | ParamType.newtypeOf _ baseType => paramTypeToSolidityString baseType -- Erased to base type private def paramTypeListToSolidityStrings : List ParamType → List String | [] => [] @@ -79,6 +83,7 @@ def storageArrayElemTypeToParamType : StorageArrayElemType → ParamType def fieldTypeToParamType : FieldType → ParamType | FieldType.uint256 => ParamType.uint256 | FieldType.address => ParamType.address + | FieldType.adt name maxFields => ParamType.adt name maxFields | FieldType.dynamicArray elemType => ParamType.array (storageArrayElemTypeToParamType elemType) | FieldType.mappingTyped _ => ParamType.uint256 | FieldType.mappingStruct _ _ => ParamType.uint256 diff --git a/Compiler/CompilationModel/AbiTypeLayout.lean b/Compiler/CompilationModel/AbiTypeLayout.lean index e89fa6cbd..03a912c93 100644 --- a/Compiler/CompilationModel/AbiTypeLayout.lean +++ b/Compiler/CompilationModel/AbiTypeLayout.lean @@ -17,6 +17,8 @@ mutual | ParamType.bytes => true | ParamType.fixedArray elemTy _ => isDynamicParamType elemTy | ParamType.tuple elemTys => isDynamicParamTypeList elemTys + | ParamType.adt _ _ => false -- ADTs are statically-sized tagged unions + | ParamType.newtypeOf _ baseType => isDynamicParamType baseType -- Erased to base type termination_by ty => sizeOf ty def isDynamicParamTypeList : List ParamType → Bool @@ -43,6 +45,8 @@ mutual if isDynamicParamType elemTy then 32 else n * paramHeadSize elemTy | ParamType.tuple elemTys => if isDynamicParamTypeList elemTys then 32 else paramHeadSizeList elemTys + | ParamType.adt _ maxFields => 32 * (1 + maxFields) -- ADTs: uint8 tag word + maxFields field words (#1727 Step 5e) + | ParamType.newtypeOf _ baseType => paramHeadSize baseType -- Erased to base type termination_by ty => sizeOf ty def paramHeadSizeList : List ParamType → Nat diff --git a/Compiler/CompilationModel/AdtStorageLayout.lean b/Compiler/CompilationModel/AdtStorageLayout.lean new file mode 100644 index 000000000..08591dca2 --- /dev/null +++ b/Compiler/CompilationModel/AdtStorageLayout.lean @@ -0,0 +1,75 @@ +/- + Compiler.CompilationModel.AdtStorageLayout: Storage layout helpers for ADTs + + ADT storage encoding uses a tagged-union layout: + - Slot 0 (relative): tag byte (uint8, identifies the variant) + - Slots 1..N (relative): max-width fields in consecutive slots + + The total storage footprint is 1 + max(variant field counts). + All variants share the same field slots — unused trailing slots + are simply not read/written for shorter variants. + + (#1727, Phase 5 Steps 5c+5d) +-/ +import Compiler.CompilationModel.Types + +namespace Compiler.CompilationModel + +open Compiler.Yul + +/-- Look up an ADT type definition by name. -/ +def lookupAdtTypeDef (adtTypes : List AdtTypeDef) (name : String) : + Except String AdtTypeDef := + match adtTypes.find? (·.name == name) with + | some def_ => pure def_ + | none => throw s!"Compilation error: unknown ADT type '{name}'" + +/-- Look up a variant within an ADT type definition by name. -/ +def lookupAdtVariant (def_ : AdtTypeDef) (variantName : String) : + Except String AdtVariant := + match def_.variants.find? (·.name == variantName) with + | some v => pure v + | none => throw s!"Compilation error: unknown variant '{variantName}' in ADT '{def_.name}'" + +/-- Maximum number of field slots across all variants. -/ +def adtMaxFieldSlots (def_ : AdtTypeDef) : Nat := + def_.variants.foldl (fun acc v => max acc v.fields.length) 0 + +/-! ### Yul compilation helpers for ADT storage operations + +These generate Yul AST fragments for reading/writing ADT values +stored in contract storage. The caller provides the base slot +(resolved from the contract's field list) and the ADT type info. +-/ + +/-- Read the tag byte from an ADT's storage slot. + `baseSlot` is the Yul expression for the ADT field's first slot. -/ +def compileAdtTagRead (baseSlot : YulExpr) : YulExpr := + -- Tag is stored as a full word in the base slot (mask to uint8) + YulExpr.call "and" [ + YulExpr.call "sload" [baseSlot], + YulExpr.lit 0xFF + ] + +/-- Write the tag byte to an ADT's storage base slot. -/ +def compileAdtTagWrite (baseSlot : YulExpr) (tagValue : Nat) : YulStmt := + YulStmt.expr (YulExpr.call "sstore" [baseSlot, YulExpr.lit tagValue]) + +/-- Read a field from an ADT variant in storage. + `baseSlot` is the ADT's first slot, `fieldIndex` is 0-based within the variant. + Fields occupy slots base+1, base+2, ... -/ +def compileAdtFieldRead (baseSlot : YulExpr) (fieldIndex : Nat) : YulExpr := + YulExpr.call "sload" [ + YulExpr.call "add" [baseSlot, YulExpr.lit (fieldIndex + 1)] + ] + +/-- Write a field value into an ADT variant's storage slot. + `baseSlot` is the ADT's first slot, `fieldIndex` is 0-based. -/ +def compileAdtFieldWrite (baseSlot : YulExpr) (fieldIndex : Nat) + (valueExpr : YulExpr) : YulStmt := + YulStmt.expr (YulExpr.call "sstore" [ + YulExpr.call "add" [baseSlot, YulExpr.lit (fieldIndex + 1)], + valueExpr + ]) + +end Compiler.CompilationModel diff --git a/Compiler/CompilationModel/Compile.lean b/Compiler/CompilationModel/Compile.lean index 2b265ca44..1ad9b137b 100644 --- a/Compiler/CompilationModel/Compile.lean +++ b/Compiler/CompilationModel/Compile.lean @@ -35,12 +35,49 @@ import Compiler.CompilationModel.SelectorInteropHelpers import Compiler.CompilationModel.ExpressionCompile import Compiler.CompilationModel.StorageWrites import Compiler.CompilationModel.Validation +import Compiler.CompilationModel.AdtStorageLayout namespace Compiler.CompilationModel open Compiler open Compiler.Yul +private def compileAdtStorageWrite (fields : List Field) + (dynamicSource : DynamicDataSource) (adtTypes : List AdtTypeDef) + (storageField adtName variantName : String) (args : List Expr) : + Except String (List YulStmt) := do + let adt ← lookupAdtTypeDef adtTypes adtName + let variant ← lookupAdtVariant adt variantName + if args.length != variant.fields.length then + throw s!"Compilation error: ADT construct '{adtName}.{variantName}' expects {variant.fields.length} payload value(s), got {args.length}" + let (baseSlot, aliasSlots) ← + match findFieldWithResolvedSlot fields storageField with + | some (field, slot) => + match field.ty with + | .adt fieldAdtName fieldMaxFields => + if fieldAdtName != adtName then + throw s!"Compilation error: storage field '{storageField}' stores ADT '{fieldAdtName}', not '{adtName}'" + else if fieldMaxFields < adtMaxFieldSlots adt then + throw s!"Compilation error: storage field '{storageField}' reserves {fieldMaxFields} ADT payload slot(s), but ADT '{adtName}' needs {adtMaxFieldSlots adt}" + else + pure (slot, field.aliasSlots) + | _ => + throw s!"Compilation error: storage field '{storageField}' is not ADT-typed" + | none => throw s!"Compilation error: unknown storage field '{storageField}' for ADT construct '{adtName}.{variantName}'" + let baseSlots := baseSlot :: aliasSlots + let argExprs ← compileExprList fields dynamicSource args + let tagStores := baseSlots.map fun slot => + compileAdtTagWrite (YulExpr.lit slot) variant.tag + let payloadStores := + baseSlots.flatMap fun slot => + argExprs.zipIdx.map fun (argExpr, idx) => + compileAdtFieldWrite (YulExpr.lit slot) idx argExpr + let clearStores := + baseSlots.flatMap fun slot => + (List.range (adtMaxFieldSlots adt)).drop args.length |>.map fun idx => + compileAdtFieldWrite (YulExpr.lit slot) idx (YulExpr.lit 0) + pure (tagStores ++ payloadStores ++ clearStores) + -- Compile statement to Yul (using mutual recursion for lists). -- When isInternal=true, Stmt.return compiles to `__ret := value; leave` so internal -- function execution terminates immediately without exiting the outer EVM call. @@ -50,13 +87,14 @@ def compileStmtList (fields : List Field) (events : List EventDef := []) (dynamicSource : DynamicDataSource := .calldata) (internalRetNames : List String := []) (isInternal : Bool := false) - (inScopeNames : List String := []) : + (inScopeNames : List String := []) + (adtTypes : List AdtTypeDef := []) : List Stmt → Except String (List YulStmt) | [] => pure [] | s :: ss => do - let head ← compileStmt fields events errors dynamicSource internalRetNames isInternal inScopeNames s + let head ← compileStmt fields events errors dynamicSource internalRetNames isInternal inScopeNames adtTypes s let nextScopeNames := collectStmtNames s ++ inScopeNames - let tail ← compileStmtList fields events errors dynamicSource internalRetNames isInternal nextScopeNames ss + let tail ← compileStmtList fields events errors dynamicSource internalRetNames isInternal nextScopeNames adtTypes ss pure (head ++ tail) def compileStmt (fields : List Field) (events : List EventDef := []) @@ -64,14 +102,22 @@ def compileStmt (fields : List Field) (events : List EventDef := []) (dynamicSource : DynamicDataSource := .calldata) (internalRetNames : List String := []) (isInternal : Bool := false) - (inScopeNames : List String := []) : + (inScopeNames : List String := []) + (adtTypes : List AdtTypeDef := []) : Stmt → Except String (List YulStmt) | Stmt.letVar name value => do pure [YulStmt.let_ name (← compileExpr fields dynamicSource value)] | Stmt.assignVar name value => do pure [YulStmt.assign name (← compileExpr fields dynamicSource value)] | Stmt.setStorage field value => - compileSetStorage fields dynamicSource field value + match adtTypes with + | [] => compileSetStorage fields dynamicSource field value + | _ => + match value with + | Expr.adtConstruct adtName variantName args => + compileAdtStorageWrite fields dynamicSource adtTypes field adtName variantName args + | _ => + compileSetStorage fields dynamicSource field value | Stmt.setStorageAddr field value => compileSetStorage fields dynamicSource field value true | Stmt.storageArrayPush field value => @@ -152,8 +198,8 @@ def compileStmt (fields : List Field) (events : List EventDef := []) | Stmt.ite cond thenBranch elseBranch => do -- If/else: compile to Yul if + negated if (#179) let condExpr ← compileExpr fields dynamicSource cond - let thenStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal inScopeNames thenBranch - let elseStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal inScopeNames elseBranch + let thenStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal inScopeNames adtTypes thenBranch + let elseStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal inScopeNames adtTypes elseBranch if elseBranch.isEmpty then -- Simple if (no else) pure [YulStmt.if_ condExpr thenStmts] @@ -173,12 +219,16 @@ def compileStmt (fields : List Field) (events : List EventDef := []) | Stmt.forEach varName count body => do -- Bounded loop: for { let i := 0 } lt(i, count) { i := add(i, 1) } { body } (#179) let countExpr ← compileExpr fields dynamicSource count - let bodyStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal (varName :: inScopeNames) body + let bodyStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal (varName :: inScopeNames) adtTypes body let initStmts := [YulStmt.let_ varName (YulExpr.lit 0)] let condExpr := YulExpr.call "lt" [YulExpr.ident varName, countExpr] let postStmts := [YulStmt.assign varName (YulExpr.call "add" [YulExpr.ident varName, YulExpr.lit 1])] pure [YulStmt.for_ initStmts condExpr postStmts bodyStmts] + | Stmt.unsafeBlock _ body => do + -- Unsafe block: transparent wrapper, compile inner body directly (#1728, Axis 6 Step 6a) + compileStmtList fields events errors dynamicSource internalRetNames isInternal inScopeNames adtTypes body + | Stmt.emit eventName args => do compileEmit fields events dynamicSource eventName args @@ -195,6 +245,12 @@ def compileStmt (fields : List Field) (events : List EventDef := []) pure [YulStmt.expr (YulExpr.call externalName argExprs)] else pure [YulStmt.letMany resultVars (YulExpr.call externalName argExprs)] + -- Try-call variant: calls {externalName}_try which returns (success, result...) + -- instead of reverting on failure. (#1727, Axis 1 Step 5f) + | Stmt.tryExternalCallBind successVar resultVars externalName args => do + let argExprs ← compileExprList fields dynamicSource args + let tryFnName := s!"{externalName}_try" + pure [YulStmt.letMany (successVar :: resultVars) (YulExpr.call tryFnName argExprs)] -- NOTE: safeTransfer, safeTransferFrom, externalCallWithReturn, callback, ecrecover -- have been removed. Use Stmt.ecm with the appropriate module from Compiler/Modules/. | Stmt.ecm mod args => do @@ -315,6 +371,48 @@ def compileStmt (fields : List Field) (events : List EventDef := []) let sizeExpr ← compileExpr fields dynamicSource dataSize let logFn := s!"log{topics.length}" pure [YulStmt.expr (YulExpr.call logFn ([offsetExpr, sizeExpr] ++ topicExprs))] + -- ADT pattern match: compile to YulStmt.switch on tag value (#1727 Steps 5c/5d) + | Stmt.matchAdt adtName scrutinee branches => do + let def_ ← lookupAdtTypeDef adtTypes adtName + -- Compile the scrutinee (tag value expression) + let scrutineeExpr ← compileExpr fields dynamicSource scrutinee + -- Extract storage field name from scrutinee for field bindings + let storageFieldName ← match scrutinee with + | Expr.adtTag scrutineeAdtName fieldName => + if scrutineeAdtName == adtName then + pure fieldName + else + throw s!"Compilation error: matchAdt declared for '{adtName}' but scrutinee reads ADT '{scrutineeAdtName}'" + | _ => throw s!"Compilation error: matchAdt scrutinee for '{adtName}' must be an adtTag expression" + let baseSlot ← match findFieldSlot fields storageFieldName with + | some s => pure s + | none => throw s!"Compilation error: unknown storage field '{storageFieldName}' for matchAdt on '{adtName}'" + -- Build switch cases: each branch matches on the variant's tag + let cases ← compileMatchAdtBranches fields events errors dynamicSource internalRetNames isInternal + inScopeNames adtTypes def_ baseSlot branches + -- Default case: revert (should be unreachable for exhaustive matches) + let defaultCase := [YulStmt.expr (YulExpr.call "revert" [YulExpr.lit 0, YulExpr.lit 0])] + pure [YulStmt.switch scrutineeExpr cases (some defaultCase)] + +def compileMatchAdtBranches (fields : List Field) (events : List EventDef) + (errors : List ErrorDef) (dynamicSource : DynamicDataSource) + (internalRetNames : List String) (isInternal : Bool) + (inScopeNames : List String) (adtTypes : List AdtTypeDef) + (def_ : AdtTypeDef) (baseSlot : Nat) : + List (String × List String × List Stmt) → Except String (List (Nat × List YulStmt)) + | [] => pure [] + | (variantName, boundVarNames, body) :: rest => do + let variant ← lookupAdtVariant def_ variantName + if boundVarNames.length != variant.fields.length then + throw s!"Compilation error: match branch '{variantName}' of ADT '{def_.name}' binds {boundVarNames.length} variables, but the variant has {variant.fields.length} fields" + -- Bind each variable to sload(baseSlot + 1 + idx) + let fieldBindings := boundVarNames.zipIdx.map fun (varName, idx) => + YulStmt.let_ varName (compileAdtFieldRead (YulExpr.lit baseSlot) idx) + let bodyStmts ← compileStmtList fields events errors dynamicSource internalRetNames isInternal + (boundVarNames.reverse ++ inScopeNames) adtTypes body + let restCases ← compileMatchAdtBranches fields events errors dynamicSource internalRetNames isInternal + inScopeNames adtTypes def_ baseSlot rest + pure ((variant.tag, fieldBindings ++ bodyStmts) :: restCases) end end Compiler.CompilationModel diff --git a/Compiler/CompilationModel/Dispatch.lean b/Compiler/CompilationModel/Dispatch.lean index b4a333ce4..33413d73c 100644 --- a/Compiler/CompilationModel/Dispatch.lean +++ b/Compiler/CompilationModel/Dispatch.lean @@ -28,7 +28,7 @@ def freshInternalRetNames (returns : List ParamType) (usedNames : List String) : -- Compile internal function to a Yul function definition (#181) def compileInternalFunction (fields : List Field) (events : List EventDef) (errors : List ErrorDef) - (spec : FunctionSpec) : + (adtTypes : List AdtTypeDef := []) (spec : FunctionSpec) : Except String YulStmt := do validateFunctionSpec spec let returns ← functionReturns spec @@ -36,18 +36,18 @@ def compileInternalFunction (fields : List Field) (events : List EventDef) (erro let usedNames := paramNames ++ collectStmtListBindNames spec.body let retNames := freshInternalRetNames returns usedNames let bodyStmts ← compileStmtList fields events errors .calldata retNames true - (paramNames ++ retNames) spec.body + (paramNames ++ retNames) adtTypes spec.body pure (YulStmt.funcDef (internalFunctionYulName spec.name) paramNames retNames bodyStmts) theorem compileInternalFunction_ok_components (fields : List Field) (events : List EventDef) (errors : List ErrorDef) (spec : FunctionSpec) (stmt : YulStmt) - (hcompile : compileInternalFunction fields events errors spec = Except.ok stmt) : + (hcompile : compileInternalFunction fields events errors [] spec = Except.ok stmt) : ∃ returns retNames bodyStmts, validateFunctionSpec spec = Except.ok () ∧ functionReturns spec = Except.ok returns ∧ compileStmtList fields events errors .calldata retNames true - (spec.params.map (·.name) ++ retNames) spec.body = Except.ok bodyStmts ∧ + (spec.params.map (·.name) ++ retNames) [] spec.body = Except.ok bodyStmts ∧ stmt = YulStmt.funcDef (internalFunctionYulName spec.name) (spec.params.map (·.name)) @@ -72,6 +72,7 @@ theorem compileInternalFunction_ok_components (spec.params.map (·.name) ++ freshInternalRetNames returns (spec.params.map (·.name) ++ collectStmtListBindNames spec.body)) + [] spec.body · rw [hbody] at hcompile cases hcompile @@ -99,8 +100,8 @@ theorem compileInternalFunction_some_ok_of_components (spec.params.map (·.name) ++ collectStmtListBindNames spec.body)) (hbody : compileStmtList fields events errors .calldata retNames true - (spec.params.map (·.name) ++ retNames) spec.body = Except.ok bodyStmts) : - compileInternalFunction fields events errors spec = + (spec.params.map (·.name) ++ retNames) [] spec.body = Except.ok bodyStmts) : + compileInternalFunction fields events errors [] spec = Except.ok (YulStmt.funcDef (internalFunctionYulName spec.name) @@ -115,6 +116,7 @@ theorem compileInternalFunction_some_ok_of_components (spec.params.map (·.name) ++ freshInternalRetNames returns (spec.params.map (·.name) ++ collectStmtListBindNames spec.body)) + [] spec.body = Except.ok bodyStmts := by simpa [hretNames] using hbody let paramNames := spec.params.map (·.name) @@ -128,6 +130,7 @@ theorem compileInternalFunction_some_ok_of_components (freshInternalRetNames returns (paramNames ++ collectStmtListBindNames spec.body)) true (paramNames ++ freshInternalRetNames returns (paramNames ++ collectStmtListBindNames spec.body)) + [] spec.body = Except.ok (YulStmt.funcDef @@ -153,13 +156,13 @@ theorem compileInternalFunction_some_ok_of_components -- Compile function spec to IR function def compileFunctionSpec (fields : List Field) (events : List EventDef) (errors : List ErrorDef) - (selector : Nat) (spec : FunctionSpec) : + (adtTypes : List AdtTypeDef := []) (selector : Nat) (spec : FunctionSpec) : Except String IRFunction := do validateFunctionSpec spec let returns ← functionReturns spec let paramLoads := genParamLoads spec.params let bodyStmts ← compileStmtList fields events errors .calldata [] false - (spec.params.map (·.name)) spec.body + (spec.params.map (·.name)) adtTypes spec.body let allStmts := paramLoads ++ bodyStmts let retType := match returns with | [single] => single.toIRType @@ -174,9 +177,9 @@ def compileFunctionSpec (fields : List Field) (events : List EventDef) (errors : } private def compileSpecialEntrypoint (fields : List Field) (events : List EventDef) - (errors : List ErrorDef) (spec : FunctionSpec) : + (errors : List ErrorDef) (adtTypes : List AdtTypeDef := []) (spec : FunctionSpec) : Except String IREntrypoint := do - let bodyChunks ← compileStmtList fields events errors .calldata [] false [] spec.body + let bodyChunks ← compileStmtList fields events errors .calldata [] false [] adtTypes spec.body pure { payable := spec.isPayable body := bodyChunks @@ -196,14 +199,14 @@ def usesMapping (fields : List Field) : Bool := -- Compile deploy code (constructor) -- Note: Don't append datacopy/return here - Codegen.deployCode does that def compileConstructor (fields : List Field) (events : List EventDef) (errors : List ErrorDef) - (ctor : Option ConstructorSpec) : + (adtTypes : List AdtTypeDef := []) (ctor : Option ConstructorSpec) : Except String (List YulStmt) := do match ctor with | none => return [] | some spec => let argLoads := genConstructorArgLoads spec.params let bodyChunks ← compileStmtList fields events errors .memory [] false - (spec.params.map (·.name)) spec.body + (spec.params.map (·.name)) adtTypes spec.body return argLoads ++ bodyChunks -- Main compilation function @@ -235,11 +238,26 @@ private def validateCompileInputsBeforeFieldWriteConflict throw s!"Compilation error: duplicate parameter name '{dup}' in function '{fnName}'" | none => pure () + for fn in spec.functions do + match firstDuplicateName (fn.params.flatMap paramBindingNames) with + | some dup => + throw s!"Compilation error: function parameter binding name '{dup}' collides with a compiler-generated parameter local in function '{fn.name}'" + | none => + pure () match firstDuplicateConstructorParamName spec.constructor with | some dup => throw s!"Compilation error: duplicate parameter name '{dup}' in constructor" | none => pure () + match spec.constructor with + | some ctor => + match firstDuplicateName (ctor.params.flatMap paramBindingNames) with + | some dup => + throw s!"Compilation error: constructor parameter binding name '{dup}' collides with a compiler-generated parameter local" + | none => + pure () + | none => + pure () for fn in spec.functions do validateFunctionSpec fn validateInteropFunctionSpec fn @@ -368,8 +386,8 @@ def compileValidatedCore (spec : CompilationModel) (selectors : List Nat) : Exce let fallbackSpec ← pickUniqueFunctionByName "fallback" spec.functions let receiveSpec ← pickUniqueFunctionByName "receive" spec.functions let functions ← (externalFns.zip selectors).mapM fun (fnSpec, sel) => - compileFunctionSpec fields spec.events spec.errors sel fnSpec - let internalFuncDefs ← internalFns.mapM (compileInternalFunction fields spec.events spec.errors) + compileFunctionSpec fields spec.events spec.errors spec.adtTypes sel fnSpec + let internalFuncDefs ← internalFns.mapM (compileInternalFunction fields spec.events spec.errors spec.adtTypes) let arrayElementHelpers := if arrayHelpersRequired then [checkedArrayElementCalldataHelper, checkedArrayElementMemoryHelper] @@ -385,11 +403,11 @@ def compileValidatedCore (spec : CompilationModel) (selectors : List Nat) : Exce [dynamicBytesEqCalldataHelper, dynamicBytesEqMemoryHelper] else [] - let fallbackEntrypoint ← fallbackSpec.mapM (compileSpecialEntrypoint fields spec.events spec.errors) - let receiveEntrypoint ← receiveSpec.mapM (compileSpecialEntrypoint fields spec.events spec.errors) + let fallbackEntrypoint ← fallbackSpec.mapM (compileSpecialEntrypoint fields spec.events spec.errors spec.adtTypes) + let receiveEntrypoint ← receiveSpec.mapM (compileSpecialEntrypoint fields spec.events spec.errors spec.adtTypes) return { name := spec.name - deploy := (← compileConstructor fields spec.events spec.errors spec.constructor) + deploy := (← compileConstructor fields spec.events spec.errors spec.adtTypes spec.constructor) constructorPayable := spec.constructor.map (·.isPayable) |>.getD false functions := functions fallbackEntrypoint := fallbackEntrypoint diff --git a/Compiler/CompilationModel/EventAbiHelpers.lean b/Compiler/CompilationModel/EventAbiHelpers.lean index 1fd3f3739..3530409ec 100644 --- a/Compiler/CompilationModel/EventAbiHelpers.lean +++ b/Compiler/CompilationModel/EventAbiHelpers.lean @@ -18,6 +18,7 @@ def normalizeEventWord (ty : ParamType) (expr : YulExpr) : YulExpr := | ParamType.uint8 => YulExpr.call "and" [expr, YulExpr.lit 255] | ParamType.address => YulExpr.call "and" [expr, YulExpr.hex addressMask] | ParamType.bool => yulToBool expr + | ParamType.newtypeOf _ baseType => normalizeEventWord baseType expr | _ => expr partial def staticCompositeLeaves (baseName : String) (ty : ParamType) : @@ -198,5 +199,20 @@ partial def compileIndexedInPlaceEncoding YulStmt.assign outLenName (YulExpr.call "add" [YulExpr.ident outLenName, elemEncodedLen]) ] ++ restStmts) pure (initStmts ++ (← goTuple elemTys 0 0), YulExpr.ident outLenName) + | ParamType.adt _ maxFields => + -- ADTs: ABI-encode as (uint8 tag, uint256 field0, ..., uint256 fieldN) + let tagLoaded := dynamicWordLoad dynamicSource srcBase + let tagStore := YulStmt.expr (YulExpr.call "mstore" [ + dstBase, YulExpr.call "and" [tagLoaded, YulExpr.lit 0xFF] + ]) + let fieldStores := (List.range maxFields).map fun i => + let srcOff := YulExpr.call "add" [srcBase, YulExpr.lit ((i + 1) * 32)] + let dstOff := YulExpr.call "add" [dstBase, YulExpr.lit ((i + 1) * 32)] + YulStmt.expr (YulExpr.call "mstore" [dstOff, dynamicWordLoad dynamicSource srcOff]) + let totalBytes := 32 * (1 + maxFields) + pure (tagStore :: fieldStores, YulExpr.lit totalBytes) + | ParamType.newtypeOf _ baseType => + -- Newtypes erased to base type (#1727 Step 3b) + compileIndexedInPlaceEncoding dynamicSource baseType srcBase dstBase stem end Compiler.CompilationModel diff --git a/Compiler/CompilationModel/EventEmission.lean b/Compiler/CompilationModel/EventEmission.lean index f545d216e..fb0cb7e81 100644 --- a/Compiler/CompilationModel/EventEmission.lean +++ b/Compiler/CompilationModel/EventEmission.lean @@ -53,6 +53,8 @@ def eventParamScalarCompileSupported (ty : ParamType) : Bool := match ty with | .uint256 | .int256 | .uint8 | .address | .bool | .bytes32 => true | .string | .tuple _ | .array _ | .fixedArray _ _ | .bytes => false + | .adt _ _ => false + | .newtypeOf _ baseType => eventParamScalarCompileSupported baseType def eventDefScalarCompileSupported (eventDef : EventDef) : Bool := eventDef.params.all (fun param => eventParamScalarCompileSupported param.ty) && @@ -78,13 +80,29 @@ def scalarEventIndexedTopicParts (indexed : List (EventParam × Expr × YulExpr)) : List (List YulStmt × YulExpr) := indexed.map fun (p, _, argExpr) => - match p.ty with - | ParamType.address => - ([], YulExpr.call "and" [argExpr, YulExpr.hex addressMask]) - | ParamType.bool => - ([], yulToBool argExpr) - | _ => - ([], argExpr) + ([], normalizeEventWord p.ty argExpr) + +def adtEventWordStores (basePtr : YulExpr) (sourceName : String) + (maxFields : Nat) (tagExpr : YulExpr) : List YulStmt := + let tagStore := YulStmt.expr (YulExpr.call "mstore" [ + basePtr, + normalizeEventWord ParamType.uint8 tagExpr + ]) + let fieldStores := (List.range maxFields).map fun fieldIdx => + YulStmt.expr (YulExpr.call "mstore" [ + YulExpr.call "add" [basePtr, YulExpr.lit ((fieldIdx + 1) * 32)], + YulExpr.ident s!"{sourceName}_f{fieldIdx}" + ]) + tagStore :: fieldStores + +def compileAdtEventWordStores (eventName : String) (paramName : String) + (srcExpr : Expr) (argExpr basePtr : YulExpr) (maxFields : Nat) : + Except String (List YulStmt) := + match srcExpr with + | Expr.param sourceName => + pure (adtEventWordStores basePtr sourceName maxFields argExpr) + | _ => + throw s!"Compilation error: ADT event param '{paramName}' in event '{eventName}' currently requires direct ADT parameter reference so payload fields can be encoded ({issue586Ref})." def compileScalarEmitFromCompiledArgs (eventDef : EventDef) (args : List Expr) (compiledArgs : List YulExpr) : @@ -374,6 +392,8 @@ def compileEmit (fields : List Field) (events : List EventDef) pure stores | _ => throw s!"Compilation error: unindexed static composite event param '{p.name}' in event '{eventName}' currently requires direct parameter reference ({issue586Ref})." + | ParamType.adt _ maxFields => + compileAdtEventWordStores eventName p.name srcExpr argExpr curHeadPtr maxFields | _ => pure [YulStmt.expr (YulExpr.call "mstore" [curHeadPtr, normalizeEventWord p.ty argExpr])] let tail ← compileUnindexedStores rest (argIdx + 1) (headOffset + eventHeadWordSize p.ty) @@ -555,12 +575,16 @@ def compileEmit (fields : List Field) (events : List EventDef) ])], YulExpr.ident topicName) | _ => throw s!"Compilation error: indexed static composite event param '{p.name}' in event '{eventName}' currently requires direct parameter reference ({issue586Ref})." - | ParamType.address => - pure ([], YulExpr.call "and" [argExpr, YulExpr.hex addressMask]) - | ParamType.bool => - pure ([], yulToBool argExpr) + | ParamType.adt _ maxFields => + let topicName := s!"__evt_topic{idx + 1}" + let stores ← compileAdtEventWordStores eventName p.name srcExpr argExpr + (YulExpr.ident "__evt_ptr") maxFields + pure (stores ++ [YulStmt.let_ topicName (YulExpr.call "keccak256" [ + YulExpr.ident "__evt_ptr", + YulExpr.lit (eventHeadWordSize p.ty) + ])], YulExpr.ident topicName) | _ => - pure ([], argExpr) + pure ([], normalizeEventWord p.ty argExpr) let indexedTopicStmts := indexedTopicParts.flatMap (·.1) let logFn := eventLogFunction indexed.length let logArgs := eventLogArgs dataSizeExpr indexedTopicParts diff --git a/Compiler/CompilationModel/ExpressionCompile.lean b/Compiler/CompilationModel/ExpressionCompile.lean index 48de5aae2..7dfdfbc46 100644 --- a/Compiler/CompilationModel/ExpressionCompile.lean +++ b/Compiler/CompilationModel/ExpressionCompile.lean @@ -1,4 +1,5 @@ import Compiler.CompilationModel.Types +import Compiler.CompilationModel.AdtStorageLayout import Compiler.CompilationModel.DynamicData import Compiler.CompilationModel.InternalNaming import Compiler.CompilationModel.ValidationHelpers @@ -393,6 +394,21 @@ def compileExpr (fields : List Field) YulExpr.call "mul" [condBool, thenExpr], YulExpr.call "mul" [condNeg, elseExpr] ]) + -- ADT expressions: storage-backed tagged unions (#1727 Steps 5c/5d) + | Expr.adtConstruct adtName variantName _args => + throw s!"Compilation error: ADT construct '{adtName}.{variantName}' cannot be used in expression position. ADT construction expands to multiple sstores and must be compiled at the statement level." + | Expr.adtTag _adtName storageField => + -- Tag byte: sload(baseSlot) & 0xFF + match findFieldSlot fields storageField with + | some baseSlot => + pure (compileAdtTagRead (YulExpr.lit baseSlot)) + | none => throw s!"Compilation error: unknown storage field '{storageField}' for ADT tag read" + | Expr.adtField _adtName _variantName _fieldName fieldIndex storageField => + -- Field read: sload(baseSlot + fieldIndex + 1) + match findFieldSlot fields storageField with + | some baseSlot => + pure (compileAdtFieldRead (YulExpr.lit baseSlot) fieldIndex) + | none => throw s!"Compilation error: unknown storage field '{storageField}' for ADT field read" end -- Compile require condition to a "failure" predicate to avoid double-negation. diff --git a/Compiler/CompilationModel/IssueRefs.lean b/Compiler/CompilationModel/IssueRefs.lean index 6b6684fc1..49c900a48 100644 --- a/Compiler/CompilationModel/IssueRefs.lean +++ b/Compiler/CompilationModel/IssueRefs.lean @@ -36,4 +36,7 @@ def issue1424Ref : String := def issue1571Ref : String := "Issue #1571 (dynamic arrays in storage)" +def issue1728Ref : String := + "Issue #1728 (CEI enforcement — Checks-Effects-Interactions ordering)" + end Compiler.CompilationModel diff --git a/Compiler/CompilationModel/LayoutCompatibilityReport.lean b/Compiler/CompilationModel/LayoutCompatibilityReport.lean index b610fb0ea..6feecd9a9 100644 --- a/Compiler/CompilationModel/LayoutCompatibilityReport.lean +++ b/Compiler/CompilationModel/LayoutCompatibilityReport.lean @@ -32,6 +32,7 @@ private def packedBitsJson (packed : PackedBits) : String := private def fieldTypeSummary : FieldType → String | .uint256 => "uint256" | .address => "address" + | .adt name maxFields => s!"adt({name};maxFields={maxFields})" | .dynamicArray elemType => s!"dynamicArray({paramTypeToSolidityString (storageArrayElemTypeToParamType elemType)})" | .mappingTyped (.simple keyType) => diff --git a/Compiler/CompilationModel/LayoutReport.lean b/Compiler/CompilationModel/LayoutReport.lean index 8b9472efb..59af548d0 100644 --- a/Compiler/CompilationModel/LayoutReport.lean +++ b/Compiler/CompilationModel/LayoutReport.lean @@ -39,6 +39,12 @@ private def fieldTypeJson : FieldType → String jsonObject [("kind", jsonString "uint256")] | .address => jsonObject [("kind", jsonString "address")] + | .adt name maxFields => + jsonObject [ + ("kind", jsonString "adt"), + ("name", jsonString name), + ("maxFields", jsonNat maxFields) + ] | .dynamicArray elemType => jsonObject [ ("kind", jsonString "dynamicArray"), @@ -101,8 +107,12 @@ where let fieldsJson := (spec.fields.zip effectiveFields).zipIdx.map fun ((declaredField, effectiveField), idx) => fieldJson declaredField effectiveField idx + let nsField := match spec.storageNamespace with + | some ns => jsonString (toString ns) + | none => "null" jsonObject [ ("contract", jsonString spec.name), + ("storageNamespace", nsField), ("fields", jsonArray fieldsJson), ("reservedSlotRanges", jsonArray (spec.reservedSlotRanges.map reservedSlotRangeJson)), ("slotAliasRanges", jsonArray (spec.slotAliasRanges.map slotAliasRangeJson)) diff --git a/Compiler/CompilationModel/LayoutValidation.lean b/Compiler/CompilationModel/LayoutValidation.lean index 5bf9f0143..01a82f5be 100644 --- a/Compiler/CompilationModel/LayoutValidation.lean +++ b/Compiler/CompilationModel/LayoutValidation.lean @@ -215,8 +215,7 @@ def firstReservedSlotWriteConflict (fields : List Field) | [] => none | f :: rest => let writeSlots : List (Nat × String) := - (f.slot.getD idx, f.name) :: - (f.aliasSlots.zipIdx.map (fun (slot, aliasIdx) => (slot, s!"{f.name}.aliasSlots[{aliasIdx}]"))) + fieldOccupiedSlots f (f.slot.getD idx) match writeSlots.findSome? (fun (slot, ownerName) => match ranges.zipIdx.find? (fun (range, _) => slotInReservedRange slot range) with | some (range, rangeIdx) => some (slot, ownerName, rangeIdx, range) @@ -225,6 +224,27 @@ def firstReservedSlotWriteConflict (fields : List Field) | none => goFields rest (idx + 1) goFields fields 0 +where + fieldOccupiedSlots (f : Field) (slot : Nat) : List (Nat × String) := + let canonical := + match f.ty with + | FieldType.adt _ maxFields => + (List.range (maxFields + 1)).map fun offset => + (slot + offset, if offset == 0 then f.name else s!"{f.name}.payload[{offset - 1}]") + | _ => [(slot, f.name)] + canonical ++ + (f.aliasSlots.zipIdx.flatMap (fun (aliasSlot, aliasIdx) => + match f.ty with + | FieldType.adt _ maxFields => + (List.range (maxFields + 1)).map fun offset => + (aliasSlot + offset, + if offset == 0 then + s!"{f.name}.aliasSlots[{aliasIdx}]" + else + s!"{f.name}.aliasSlots[{aliasIdx}].payload[{offset - 1}]") + | _ => + [(aliasSlot, s!"{f.name}.aliasSlots[{aliasIdx}]")])) + def firstInvalidPackedBits (fields : List Field) : Option (String × PackedBits) := let rec go (remaining : List Field) : Option (String × PackedBits) := @@ -251,6 +271,7 @@ def firstMappingPackedBits (fields : List Field) : | FieldType.mappingTyped _, some _ => some f.name | FieldType.mappingStruct _ _, some _ => some f.name | FieldType.mappingStruct2 _ _ _, some _ => some f.name + | FieldType.adt _ _, some _ => some f.name | _, _ => go rest go fields @@ -313,9 +334,7 @@ def firstFieldWriteSlotConflict (fields : List Field) : Option (Nat × String × | [] => none | f :: rest => let writeSlots : List (Nat × String × Option PackedBits) := - (f.slot.getD idx, f.name, f.packedBits) :: - (f.aliasSlots.zipIdx.map (fun (slot, aliasIdx) => - (slot, s!"{f.name}.aliasSlots[{aliasIdx}]", f.packedBits))) + fieldOccupiedSlots f (f.slot.getD idx) let rec firstInFieldConflict (seenSlots : List (Nat × String × Option PackedBits)) : List (Nat × String × Option PackedBits) → Option (Nat × String × String) | [] => none @@ -328,6 +347,30 @@ def firstFieldWriteSlotConflict (fields : List Field) : Option (Nat × String × | none => go (writeSlots.reverse ++ seen) (idx + 1) rest go [] 0 fields +where + fieldOccupiedSlots (f : Field) (slot : Nat) : List (Nat × String × Option PackedBits) := + let canonical := + match f.ty with + | FieldType.adt _ maxFields => + (List.range (maxFields + 1)).map fun offset => + (slot + offset, + if offset == 0 then f.name else s!"{f.name}.payload[{offset - 1}]", + none) + | _ => [(slot, f.name, f.packedBits)] + canonical ++ + (f.aliasSlots.zipIdx.flatMap (fun (aliasSlot, aliasIdx) => + match f.ty with + | FieldType.adt _ maxFields => + (List.range (maxFields + 1)).map fun offset => + (aliasSlot + offset, + if offset == 0 then + s!"{f.name}.aliasSlots[{aliasIdx}]" + else + s!"{f.name}.aliasSlots[{aliasIdx}].payload[{offset - 1}]", + none) + | _ => + [(aliasSlot, s!"{f.name}.aliasSlots[{aliasIdx}]", f.packedBits)])) + /-- Stepping lemma: firstInFieldConflict on nil. -/ theorem firstFieldWriteSlotConflict_firstInFieldConflict_nil (seen : List (Nat × String × Option PackedBits)) : @@ -356,9 +399,7 @@ theorem firstFieldWriteSlotConflict_go_cons (f : Field) (rest : List Field) : firstFieldWriteSlotConflict.go seen idx (f :: rest) = let writeSlots := - (f.slot.getD idx, f.name, f.packedBits) :: - (f.aliasSlots.zipIdx.map (fun (slot, aliasIdx) => - (slot, s!"{f.name}.aliasSlots[{aliasIdx}]", f.packedBits))) + firstFieldWriteSlotConflict.fieldOccupiedSlots f (f.slot.getD idx) match firstFieldWriteSlotConflict.go.firstInFieldConflict seen writeSlots with | some conflict => some conflict | none => firstFieldWriteSlotConflict.go (writeSlots.reverse ++ seen) (idx + 1) rest := rfl diff --git a/Compiler/CompilationModel/LogicalPurity.lean b/Compiler/CompilationModel/LogicalPurity.lean index 6f7383157..ffa07dfdb 100644 --- a/Compiler/CompilationModel/LogicalPurity.lean +++ b/Compiler/CompilationModel/LogicalPurity.lean @@ -43,10 +43,15 @@ partial def exprContainsCallLike (expr : Expr) : Bool := exprContainsCallLike a | Expr.ite cond thenVal elseVal => exprContainsCallLike cond || exprContainsCallLike thenVal || exprContainsCallLike elseVal + | Expr.adtConstruct _ _ args => + exprListContainsCallLike args + | Expr.adtField _ _ _ _ _ => + false | Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee - | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ => + | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ + | Expr.adtTag _ _ => false partial def exprListContainsCallLike : List Expr → Bool | [] => false @@ -127,10 +132,15 @@ def exprContainsUnsafeLogicalCallLike (expr : Expr) : Bool := exprContainsUnsafeLogicalCallLike cond || exprContainsUnsafeLogicalCallLike thenVal || exprContainsUnsafeLogicalCallLike elseVal + | Expr.adtConstruct _ _ args => + exprListAnyUnsafeLogicalCallLike args + | Expr.adtField _ _ _ _ _ => + false | Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee - | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ => + | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ + | Expr.adtTag _ _ => false termination_by sizeOf expr decreasing_by all_goals simp_wf; all_goals omega @@ -186,19 +196,31 @@ def stmtContainsUnsafeLogicalCallLike : Stmt → Bool stmtListAnyUnsafeLogicalCallLike elseBranch | Stmt.forEach _ count body => exprContainsUnsafeLogicalCallLike count || stmtListAnyUnsafeLogicalCallLike body + | Stmt.unsafeBlock _ body => + stmtListAnyUnsafeLogicalCallLike body + | Stmt.matchAdt _ scrutinee branches => + exprContainsUnsafeLogicalCallLike scrutinee || + matchBranchesAnyUnsafeLogicalCallLike branches | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args => exprListAnyUnsafeLogicalCallLike args | Stmt.rawLog topics dataOffset dataSize => exprListAnyUnsafeLogicalCallLike topics || exprContainsUnsafeLogicalCallLike dataOffset || exprContainsUnsafeLogicalCallLike dataSize - | Stmt.externalCallBind _ _ args => + | Stmt.externalCallBind _ _ args | Stmt.tryExternalCallBind _ _ _ args => exprListAnyUnsafeLogicalCallLike args | Stmt.ecm _ args => exprListAnyUnsafeLogicalCallLike args termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega +def matchBranchesAnyUnsafeLogicalCallLike : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListAnyUnsafeLogicalCallLike body || matchBranchesAnyUnsafeLogicalCallLike rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega + def stmtListAnyUnsafeLogicalCallLike : List Stmt → Bool | [] => false | s :: ss => stmtContainsUnsafeLogicalCallLike s || stmtListAnyUnsafeLogicalCallLike ss diff --git a/Compiler/CompilationModel/ParamLoading.lean b/Compiler/CompilationModel/ParamLoading.lean index bd9b26968..6a00ef216 100644 --- a/Compiler/CompilationModel/ParamLoading.lean +++ b/Compiler/CompilationModel/ParamLoading.lean @@ -113,6 +113,53 @@ def genStaticTypeLoads | _ => [] termination_by sizeOf ty +-- Generate loading stmts for a single param by type. Recurses on ParamType for newtypeOf unwrapping. +def genSingleParamLoad + (loadWord : YulExpr → YulExpr) (sizeExpr : YulExpr) + (headSize : Nat) (baseOffset : Nat) (name : String) (ty : ParamType) (headOffset : Nat) : + List YulStmt := + match ty with + | ParamType.uint256 | ParamType.int256 | ParamType.uint8 | ParamType.address | ParamType.bool | ParamType.bytes32 => + genScalarLoad loadWord name ty headOffset + | ParamType.tuple elemTypes => + if isDynamicParamType ty then + genDynamicParamLoads loadWord sizeExpr headSize baseOffset name ty headOffset + else + genStaticTypeLoads loadWord name (ParamType.tuple elemTypes) headOffset + | ParamType.array _ => + genDynamicParamLoads loadWord sizeExpr headSize baseOffset name ty headOffset + | ParamType.fixedArray _ n => + -- Static fixed arrays are decoded inline recursively (including nested tuple members). + -- For scalar element arrays we preserve `` as an alias to `_0`. + if isDynamicParamType ty then + genDynamicParamLoads loadWord sizeExpr headSize baseOffset name ty headOffset + else + let staticLoads := genStaticTypeLoads loadWord name ty headOffset + if n == 0 then staticLoads else + let firstAlias := match ty with + | ParamType.fixedArray elemTy _ => + if isScalarParamType elemTy then + [YulStmt.let_ name (YulExpr.ident s!"{name}_0")] + else + [] + | _ => [] + staticLoads ++ firstAlias + | ParamType.bytes | ParamType.string => + genDynamicParamLoads loadWord sizeExpr headSize baseOffset name ty headOffset + | ParamType.adt _ maxFields => + -- ADTs: decode (uint8 tag, uint256 field0, ..., uint256 fieldN) from calldata + -- Tag word: load first word and mask to uint8 + let tagLoad := [YulStmt.let_ name (YulExpr.call "and" [ + loadWord (YulExpr.lit headOffset), YulExpr.lit 255 + ])] + -- Field words: load consecutive 32-byte words + let fieldLoads := (List.range maxFields).map fun i => + YulStmt.let_ s!"{name}_f{i}" (loadWord (YulExpr.lit (headOffset + (i + 1) * 32))) + tagLoad ++ fieldLoads + | ParamType.newtypeOf _ baseType => + -- Newtypes are erased to their base type at Yul level (#1727 Step 3b) + genSingleParamLoad loadWord sizeExpr headSize baseOffset name baseType headOffset + def genParamLoadBodyFrom (loadWord : YulExpr → YulExpr) (sizeExpr : YulExpr) (headSize : Nat) (baseOffset : Nat) (params : List Param) (headOffset : Nat) : @@ -120,34 +167,7 @@ def genParamLoadBodyFrom match params with | [] => [] | param :: rest => - let stmts := match param.ty with - | ParamType.uint256 | ParamType.int256 | ParamType.uint8 | ParamType.address | ParamType.bool | ParamType.bytes32 => - genScalarLoad loadWord param.name param.ty headOffset - | ParamType.tuple elemTypes => - if isDynamicParamType param.ty then - genDynamicParamLoads loadWord sizeExpr headSize baseOffset param.name param.ty headOffset - else - genStaticTypeLoads loadWord param.name (ParamType.tuple elemTypes) headOffset - | ParamType.array _ => - genDynamicParamLoads loadWord sizeExpr headSize baseOffset param.name param.ty headOffset - | ParamType.fixedArray _ n => - -- Static fixed arrays are decoded inline recursively (including nested tuple members). - -- For scalar element arrays we preserve `` as an alias to `_0`. - if isDynamicParamType param.ty then - genDynamicParamLoads loadWord sizeExpr headSize baseOffset param.name param.ty headOffset - else - let staticLoads := genStaticTypeLoads loadWord param.name param.ty headOffset - if n == 0 then staticLoads else - let firstAlias := match param.ty with - | ParamType.fixedArray elemTy _ => - if isScalarParamType elemTy then - [YulStmt.let_ param.name (YulExpr.ident s!"{param.name}_0")] - else - [] - | _ => [] - staticLoads ++ firstAlias - | ParamType.bytes | ParamType.string => - genDynamicParamLoads loadWord sizeExpr headSize baseOffset param.name param.ty headOffset + let stmts := genSingleParamLoad loadWord sizeExpr headSize baseOffset param.name param.ty headOffset stmts ++ genParamLoadBodyFrom loadWord sizeExpr headSize baseOffset rest (headOffset + paramHeadSize param.ty) diff --git a/Compiler/CompilationModel/ScopeValidation.lean b/Compiler/CompilationModel/ScopeValidation.lean index 9fca28705..b39497178 100644 --- a/Compiler/CompilationModel/ScopeValidation.lean +++ b/Compiler/CompilationModel/ScopeValidation.lean @@ -22,6 +22,10 @@ partial def staticParamBindingNames (name : String) (ty : ParamType) : List Stri | elemTy :: rest => staticParamBindingNames s!"{name}_{idx}" elemTy ++ go rest (idx + 1) go elemTys 0 + | ParamType.adt _ maxFields => + name :: (List.range maxFields).map (fun i => s!"{name}_f{i}") + | ParamType.newtypeOf _ baseType => + staticParamBindingNames name baseType | _ => [] def dynamicParamBindingNames (name : String) : List String := @@ -40,6 +44,8 @@ mutual | ParamType.bytes => true | ParamType.fixedArray elemTy _ => isDynamicParamTypeForScope elemTy | ParamType.tuple elemTys => paramTypeListAnyDynamicForScope elemTys + | ParamType.adt _ _ => false + | ParamType.newtypeOf _ baseType => isDynamicParamTypeForScope baseType termination_by ty => sizeOf ty decreasing_by all_goals simp_wf; all_goals omega @@ -219,6 +225,12 @@ def validateScopedExprIdentifiers validateScopedExprIdentifiers context params paramScope dynamicParams localScope constructorArgCount cond validateScopedExprIdentifiers context params paramScope dynamicParams localScope constructorArgCount thenVal validateScopedExprIdentifiers context params paramScope dynamicParams localScope constructorArgCount elseVal + | Expr.adtConstruct _ _ args => + validateScopedExprIdentifiersList context params paramScope dynamicParams localScope constructorArgCount args + | Expr.adtTag _ _ => + pure () + | Expr.adtField _ _ _ _ _ => + pure () | Expr.literal _ | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee | Expr.calldatasize | Expr.returndataSize => @@ -312,6 +324,13 @@ def validateScopedStmtIdentifiers validateScopedExprIdentifiers context params paramScope dynamicParams localScope constructorArgCount count let _ ← validateScopedStmtListIdentifiers context params paramScope dynamicParams (varName :: localScope) constructorArgCount body pure localScope + | Stmt.unsafeBlock _ body => do + let _ ← validateScopedStmtListIdentifiers context params paramScope dynamicParams localScope constructorArgCount body + pure localScope + | Stmt.matchAdt _ scrutinee branches => do + validateScopedExprIdentifiers context params paramScope dynamicParams localScope constructorArgCount scrutinee + validateScopedMatchBranches context params paramScope dynamicParams localScope constructorArgCount branches + pure localScope | Stmt.internalCall _ args => do validateScopedExprIdentifiersList context params paramScope dynamicParams localScope constructorArgCount args pure localScope @@ -326,6 +345,9 @@ def validateScopedStmtIdentifiers | Stmt.externalCallBind resultVars _ args => do validateScopedExprIdentifiersList context params paramScope dynamicParams localScope constructorArgCount args pure (resultVars.reverse ++ localScope) + | Stmt.tryExternalCallBind successVar resultVars _ args => do + validateScopedExprIdentifiersList context params paramScope dynamicParams localScope constructorArgCount args + pure ((successVar :: resultVars).reverse ++ localScope) | Stmt.ecm mod args => do if args.length != mod.numArgs then throw s!"Compilation error: {context} uses ECM '{mod.name}' with {args.length} arguments but it expects {mod.numArgs}" @@ -353,6 +375,18 @@ def validateScopedStmtListIdentifiers validateScopedStmtListIdentifiers context params paramScope dynamicParams nextScope constructorArgCount rest termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateScopedMatchBranches + (context : String) (params : List Param) (paramScope : List String) (dynamicParams : List String) + (localScope : List String) (constructorArgCount : Option Nat) : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, varNames, body) :: rest => do + let branchScope := varNames.reverse ++ localScope + let _ ← validateScopedStmtListIdentifiers context params paramScope dynamicParams branchScope constructorArgCount body + validateScopedMatchBranches context params paramScope dynamicParams localScope constructorArgCount rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateFunctionIdentifierReferences (spec : FunctionSpec) : Except String Unit := do diff --git a/Compiler/CompilationModel/StorageWrites.lean b/Compiler/CompilationModel/StorageWrites.lean index 11e481949..152811aa2 100644 --- a/Compiler/CompilationModel/StorageWrites.lean +++ b/Compiler/CompilationModel/StorageWrites.lean @@ -1,4 +1,5 @@ import Compiler.CompilationModel.ExpressionCompile +import Compiler.CompilationModel.AdtStorageLayout import Compiler.CompilationModel.AbiTypeLayout import Compiler.CompilationModel.IssueRefs import Compiler.CompilationModel.MappingWrites @@ -90,28 +91,32 @@ def compileSetStorage (fields : List Field) (dynamicSource : DynamicDataSource) YulExpr.call "and" [valueExpr, YulExpr.hex Compiler.Constants.addressMask] else valueExpr - match slots with - | [] => - throw s!"Compilation error: internal invariant failure: no write slots for field '{field}' in setStorage" - | [singleSlot] => - match f.packedBits with - | none => - pure [YulStmt.expr (YulExpr.call "sstore" [YulExpr.lit singleSlot, storedValueExpr])] - | some packed => - pure (compilePackedStorageWrite (YulExpr.lit singleSlot) storedValueExpr packed) + match f.ty with + | .adt _ _ => + throw s!"Compilation error: field '{field}' is ADT-typed; assign it with an ADT constructor so payload slots are preserved" | _ => - let writeSlots := slots.map YulExpr.lit - match f.packedBits with - | none => - pure [ - YulStmt.block ( - [YulStmt.let_ "__compat_value" storedValueExpr] ++ - writeSlots.map (fun writeSlot => - YulStmt.expr (YulExpr.call "sstore" [writeSlot, YulExpr.ident "__compat_value"])) - ) - ] - | some packed => - pure (compileCompatPackedStorageWrites writeSlots storedValueExpr packed) + match slots with + | [] => + throw s!"Compilation error: internal invariant failure: no write slots for field '{field}' in setStorage" + | [singleSlot] => + match f.packedBits with + | none => + pure [YulStmt.expr (YulExpr.call "sstore" [YulExpr.lit singleSlot, storedValueExpr])] + | some packed => + pure (compilePackedStorageWrite (YulExpr.lit singleSlot) storedValueExpr packed) + | _ => + let writeSlots := slots.map YulExpr.lit + match f.packedBits with + | none => + pure [ + YulStmt.block ( + [YulStmt.let_ "__compat_value" storedValueExpr] ++ + writeSlots.map (fun writeSlot => + YulStmt.expr (YulExpr.call "sstore" [writeSlot, YulExpr.ident "__compat_value"])) + ) + ] + | some packed => + pure (compileCompatPackedStorageWrites writeSlots storedValueExpr packed) | none => throw s!"Compilation error: unknown storage field '{field}' in setStorage" def compileStorageArrayPush (fields : List Field) (dynamicSource : DynamicDataSource) diff --git a/Compiler/CompilationModel/TrustSurface.lean b/Compiler/CompilationModel/TrustSurface.lean index faae06e25..9d80836e2 100644 --- a/Compiler/CompilationModel/TrustSurface.lean +++ b/Compiler/CompilationModel/TrustSurface.lean @@ -186,9 +186,14 @@ private partial def collectLowLevelStmtMechanics : Stmt → List String collectLowLevelExprMechanics cond ++ thenBr.flatMap collectLowLevelStmtMechanics ++ elseBr.flatMap collectLowLevelStmtMechanics | .forEach _ count body => collectLowLevelExprMechanics count ++ body.flatMap collectLowLevelStmtMechanics + | .unsafeBlock _ body => + body.flatMap collectLowLevelStmtMechanics + | .matchAdt _ scrutinee branches => + collectLowLevelExprMechanics scrutinee ++ + branches.flatMap fun (_, _, body) => body.flatMap collectLowLevelStmtMechanics | .emit _ args | .internalCall _ args - | .externalCallBind _ _ args + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args | .returnValues args | .ecm _ args | .internalCallAssign _ _ args => @@ -244,9 +249,14 @@ private partial def collectAxiomatizedStmtPrimitives : Stmt → List String elseBr.flatMap collectAxiomatizedStmtPrimitives | .forEach _ count body => collectAxiomatizedExprPrimitives count ++ body.flatMap collectAxiomatizedStmtPrimitives + | .unsafeBlock _ body => + body.flatMap collectAxiomatizedStmtPrimitives + | .matchAdt _ scrutinee branches => + collectAxiomatizedExprPrimitives scrutinee ++ + branches.flatMap fun (_, _, body) => body.flatMap collectAxiomatizedStmtPrimitives | .emit _ args | .internalCall _ args - | .externalCallBind _ _ args + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args | .returnValues args | .ecm _ args | .internalCallAssign _ _ args => @@ -279,6 +289,105 @@ private def isUnsafeBoundaryMechanic (mechanic : String) : Bool := def collectUnsafeBoundaryMechanicsFromStmts (stmts : List Stmt) : List String := dedupPreserve ((collectLowLevelMechanicsFromStmts stmts).filter isUnsafeBoundaryMechanic) +/-- Like `collectLowLevelStmtMechanics` but skips `unsafeBlock` bodies — + returns only mechanics that appear *outside* any `unsafe` wrapper. -/ +private partial def collectUnguardedLowLevelStmtMechanics : Stmt → List String + | .letVar _ value + | .assignVar _ value + | .setStorage _ value + | .setStorageAddr _ value + | .storageArrayPush _ value + | .return value + | .require value _ => + collectLowLevelExprMechanics value + | .setStorageArrayElement _ index value => + collectLowLevelExprMechanics index ++ collectLowLevelExprMechanics value + | .storageArrayPop _ => + [] + | .requireError cond _ args => + collectLowLevelExprMechanics cond ++ args.flatMap collectLowLevelExprMechanics + | .revertError _ args => + args.flatMap collectLowLevelExprMechanics + | .mstore offset value => + ["mstore"] ++ collectLowLevelExprMechanics offset ++ collectLowLevelExprMechanics value + | .tstore offset value => + ["tstore"] ++ collectLowLevelExprMechanics offset ++ collectLowLevelExprMechanics value + | .calldatacopy destOffset sourceOffset size => + ["calldatacopy"] ++ collectLowLevelExprMechanics destOffset ++ + collectLowLevelExprMechanics sourceOffset ++ collectLowLevelExprMechanics size + | .returndataCopy destOffset sourceOffset size => + ["returndataCopy"] ++ collectLowLevelExprMechanics destOffset ++ collectLowLevelExprMechanics sourceOffset ++ collectLowLevelExprMechanics size + | .revertReturndata => + ["revertReturndata"] + | .setMapping _ key value + | .setMappingWord _ key _ value + | .setMappingPackedWord _ key _ _ value + | .setMappingUint _ key value + | .setStructMember _ key _ value => + collectLowLevelExprMechanics key ++ collectLowLevelExprMechanics value + | .setMappingChain _ keys value => + keys.flatMap collectLowLevelExprMechanics ++ collectLowLevelExprMechanics value + | .setMapping2 _ key1 key2 value + | .setMapping2Word _ key1 key2 _ value + | .setStructMember2 _ key1 key2 _ value => + collectLowLevelExprMechanics key1 ++ collectLowLevelExprMechanics key2 ++ collectLowLevelExprMechanics value + | .ite cond thenBr elseBr => + collectLowLevelExprMechanics cond ++ thenBr.flatMap collectUnguardedLowLevelStmtMechanics ++ elseBr.flatMap collectUnguardedLowLevelStmtMechanics + | .forEach _ count body => + collectLowLevelExprMechanics count ++ body.flatMap collectUnguardedLowLevelStmtMechanics + | .unsafeBlock _ _ => + [] + | .matchAdt _ scrutinee branches => + collectLowLevelExprMechanics scrutinee ++ + branches.flatMap fun (_, _, body) => body.flatMap collectUnguardedLowLevelStmtMechanics + | .emit _ args + | .internalCall _ args + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args + | .returnValues args + | .ecm _ args + | .internalCallAssign _ _ args => + args.flatMap collectLowLevelExprMechanics + | .rawLog topics dataOffset dataSize => + topics.flatMap collectLowLevelExprMechanics ++ collectLowLevelExprMechanics dataOffset ++ collectLowLevelExprMechanics dataSize + | .returnArray _ + | .returnBytes _ + | .returnStorageWords _ + | .stop => + [] + +private def collectUnguardedLowLevelMechanicsFromStmts (stmts : List Stmt) : List String := + dedupPreserve (stmts.flatMap collectUnguardedLowLevelStmtMechanics) + +/-- Collect unsafe boundary mechanics that appear *outside* any `unsafe "reason" do` block. + Operations inside `unsafe` blocks are considered documented and do not require + `local_obligations`. -/ +def collectUnguardedUnsafeBoundaryMechanicsFromStmts (stmts : List Stmt) : List String := + dedupPreserve ((collectUnguardedLowLevelMechanicsFromStmts stmts).filter isUnsafeBoundaryMechanic) + +/-- Collect `unsafe "reason" do` block reason strings from a statement, recursing into branches. -/ +private partial def collectUnsafeBlockReasonsInStmt : Stmt → List String + | .unsafeBlock reason body => + [reason] ++ body.flatMap collectUnsafeBlockReasonsInStmt + | .ite _ thenBr elseBr => + thenBr.flatMap collectUnsafeBlockReasonsInStmt ++ elseBr.flatMap collectUnsafeBlockReasonsInStmt + | .forEach _ _ body => + body.flatMap collectUnsafeBlockReasonsInStmt + | .matchAdt _ _ branches => + branches.flatMap fun (_, _, body) => body.flatMap collectUnsafeBlockReasonsInStmt + | _ => [] + +private def collectUnsafeBlockReasonsFromStmts (stmts : List Stmt) : List String := + stmts.flatMap collectUnsafeBlockReasonsInStmt + +/-- Collect all `unsafe "reason" do` block reasons used by a spec. -/ +def collectUnsafeBlockReasons (spec : CompilationModel) : List String := + let stmtsFromFn (fn : FunctionSpec) := fn.body + let stmtsFromCtor : List Stmt := match spec.constructor with + | some ctor => ctor.body + | none => [] + let allStmts := stmtsFromCtor ++ spec.functions.flatMap stmtsFromFn + collectUnsafeBlockReasonsFromStmts allStmts + private def isLinearMemoryMechanic (mechanic : String) : Bool := match mechanic with | "mload" | "mstore" | "calldatacopy" | "returndataCopy" | "returndataOptionalBoolAt" => true @@ -399,9 +508,14 @@ private partial def collectEventEmissionStmtMechanics : Stmt → List String elseBr.flatMap collectEventEmissionStmtMechanics | .forEach _ count body => collectEventEmissionExprMechanics count ++ body.flatMap collectEventEmissionStmtMechanics + | .unsafeBlock _ body => + body.flatMap collectEventEmissionStmtMechanics + | .matchAdt _ scrutinee branches => + collectEventEmissionExprMechanics scrutinee ++ + branches.flatMap fun (_, _, body) => body.flatMap collectEventEmissionStmtMechanics | .emit _ args | .internalCall _ args - | .externalCallBind _ _ args + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args | .returnValues args | .ecm _ args | .internalCallAssign _ _ args => @@ -554,13 +668,18 @@ private partial def collectRuntimeIntrospectionStmtMechanics : Stmt → List Str elseBr.flatMap collectRuntimeIntrospectionStmtMechanics | .forEach _ count body => collectRuntimeIntrospectionExprMechanics count ++ body.flatMap collectRuntimeIntrospectionStmtMechanics + | .unsafeBlock _ body => + body.flatMap collectRuntimeIntrospectionStmtMechanics + | .matchAdt _ scrutinee branches => + collectRuntimeIntrospectionExprMechanics scrutinee ++ + branches.flatMap fun (_, _, body) => body.flatMap collectRuntimeIntrospectionStmtMechanics | .emit _ args | .internalCall _ args | .returnValues args | .ecm _ args | .internalCallAssign _ _ args => args.flatMap collectRuntimeIntrospectionExprMechanics - | .externalCallBind _ _ args => + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args => args.flatMap collectRuntimeIntrospectionExprMechanics | .rawLog topics dataOffset dataSize => topics.flatMap collectRuntimeIntrospectionExprMechanics ++ @@ -692,6 +811,11 @@ private partial def collectExternalStmtNames : Stmt → List String collectExternalExprNames cond ++ thenBr.flatMap collectExternalStmtNames ++ elseBr.flatMap collectExternalStmtNames | .forEach _ count body => collectExternalExprNames count ++ body.flatMap collectExternalStmtNames + | .unsafeBlock _ body => + body.flatMap collectExternalStmtNames + | .matchAdt _ scrutinee branches => + collectExternalExprNames scrutinee ++ + branches.flatMap fun (_, _, body) => body.flatMap collectExternalStmtNames | .emit _ args | .internalCall _ args | .returnValues args @@ -700,6 +824,8 @@ private partial def collectExternalStmtNames : Stmt → List String args.flatMap collectExternalExprNames | .externalCallBind _ externalName args => externalName :: args.flatMap collectExternalExprNames + | .tryExternalCallBind _ _ externalName args => + externalName :: args.flatMap collectExternalExprNames | .rawLog topics dataOffset dataSize => topics.flatMap collectExternalExprNames ++ collectExternalExprNames dataOffset ++ collectExternalExprNames dataSize | .returnArray _ @@ -748,6 +874,10 @@ private partial def collectUsedEcmModulesInStmt : Stmt → List ECM.ExternalCall thenBr.flatMap collectUsedEcmModulesInStmt ++ elseBr.flatMap collectUsedEcmModulesInStmt | .forEach _ _ body => body.flatMap collectUsedEcmModulesInStmt + | .unsafeBlock _ body => + body.flatMap collectUsedEcmModulesInStmt + | .matchAdt _ _ branches => + branches.flatMap fun (_, _, body) => body.flatMap collectUsedEcmModulesInStmt | _ => [] @@ -942,6 +1072,7 @@ private structure UsageSiteSummary where externals : List ExternalFunction modules : List ECM.ExternalCallModule localObligations : List LocalObligation + unsafeBlocks : List String private def ecmAxiomsFromModules (modules : List ECM.ExternalCallModule) : List (String × String) := modules.flatMap (fun mod => mod.axioms.map (fun assumption => (mod.name, assumption))) @@ -957,7 +1088,8 @@ private def siteHasTrustSurface !(collectAxiomatizedPrimitivesFromStmts stmts).isEmpty || !(collectUsedExternalAssumptionsFromStmts externals stmts).isEmpty || !(collectUsedEcmModulesFromStmts stmts).isEmpty || - !(collectLocalObligationsFromStmts localObligations stmts).isEmpty + !(collectLocalObligationsFromStmts localObligations stmts).isEmpty || + !(collectUnsafeBlockReasonsFromStmts stmts).isEmpty private def usageSiteSummary (spec : CompilationModel) @@ -973,6 +1105,7 @@ private def usageSiteSummary let siteExternals := collectUsedExternalAssumptionsFromStmts spec.externals stmts let siteModules := collectUsedEcmModulesFromStmts stmts let siteLocalObligations := collectLocalObligationsFromStmts localObligations stmts + let siteUnsafeBlocks := collectUnsafeBlockReasonsFromStmts stmts { kind := kind name := name mechanics := mechanics @@ -982,7 +1115,8 @@ private def usageSiteSummary primitives := primitives externals := siteExternals modules := siteModules - localObligations := siteLocalObligations } + localObligations := siteLocalObligations + unsafeBlocks := siteUnsafeBlocks } private def collectUsageSiteSummaries (spec : CompilationModel) : List UsageSiteSummary := let constructorSites := @@ -1017,6 +1151,7 @@ private def usageSitesJson (spec : CompilationModel) : String := ("axiomatizedPrimitives", jsonArray (site.primitives.map jsonString)), ("proofStatus", proofStatusJsonForSite site.primitives site.externals site.modules site.localObligations), ("localObligations", jsonArray (site.localObligations.map localObligationJson)), + ("unsafeBlocks", jsonArray (site.unsafeBlocks.map jsonString)), ("hasUncheckedDependencies", if hasUncheckedDependenciesForSite site.externals site.modules then "true" else "false"), ("externalAssumptions", jsonObject [ @@ -1186,6 +1321,10 @@ def emitVerboseUsageSiteLines (specs : List CompilationModel) : List String := else modAcc ++ assumptionLines) [] + let unsafeBlockLines := + if site.unsafeBlocks.isEmpty then [] else + [s!" unsafe blocks: {site.unsafeBlocks.length}"] ++ + site.unsafeBlocks.map (fun reason => s!" [unsafe] \"{reason}\"") let localObligationLines := site.localObligations.map (fun obligation => @@ -1206,6 +1345,7 @@ def emitVerboseUsageSiteLines (specs : List CompilationModel) : List String := uncheckedLines ++ externalAssumptionLines ++ ecmAxiomLines ++ + unsafeBlockLines ++ localObligationLines) [] acc ++ siteLines) @@ -1378,6 +1518,23 @@ def hasAssumedDependencies (spec : CompilationModel) : Bool := (collectUsedExternalAssumptions spec) (collectUsedEcmModules spec) +/-- Render localized unsafe-block lines for `--deny-unsafe` diagnostics. -/ +def emitUnsafeBlockUsageSiteLines (specs : List CompilationModel) : List String := + specs.foldl + (fun acc spec => + let siteLines := + (collectUsageSiteSummaries spec).foldl + (fun siteAcc site => + if site.unsafeBlocks.isEmpty then + siteAcc + else + siteAcc ++ + site.unsafeBlocks.map (fun reason => + s!"- {spec.name} [{site.kind}:{site.name}]: unsafe \"{reason}\"")) + [] + acc ++ siteLines) + [] + /-- Render the machine-readable trust report consumed by CLI/tests. -/ def emitTrustReportJson (specs : List CompilationModel) : String := jsonObject [ @@ -1394,6 +1551,7 @@ where ("partiallyModeledRuntimeIntrospection", jsonArray ((collectRuntimeIntrospectionMechanics spec).map jsonString)), ("axiomatizedPrimitives", jsonArray ((collectAxiomatizedPrimitives spec).map jsonString)), ("localObligations", jsonArray ((collectLocalObligations spec).map localObligationJson)), + ("unsafeBlocks", jsonArray ((collectUnsafeBlockReasons spec).map jsonString)), ("proofStatus", proofStatusJson spec), ("hasUncheckedDependencies", if hasUncheckedDependencies spec then "true" else "false"), ("proofBoundary", jsonObject [ diff --git a/Compiler/CompilationModel/Types.lean b/Compiler/CompilationModel/Types.lean index d853b77c7..49a3cf3ad 100644 --- a/Compiler/CompilationModel/Types.lean +++ b/Compiler/CompilationModel/Types.lean @@ -100,6 +100,9 @@ def storageArrayElemUsesOneStorageWord : StorageArrayElemType → Bool inductive FieldType | uint256 | address + /-- Storage-backed tagged union: tag at the canonical slot followed by + `maxFields` payload slots. -/ + | adt (name : String) (maxFields : Nat) | dynamicArray (elemType : StorageArrayElemType) | mappingTyped (mt : MappingType) -- Flexible mapping types (#154) /-- A mapping whose value is a multi-word struct with named members. @@ -162,12 +165,14 @@ inductive ParamType | array (elemType : ParamType) -- Dynamic array: uint256[], address[] | fixedArray (elemType : ParamType) (size : Nat) -- Fixed array: uint256[3] | bytes -- Dynamic bytes + | adt (name : String) (maxFields : Nat) -- User-defined ADT; maxFields = max variant field count (#1727 Steps 5b/5e) + | newtypeOf (name : String) (baseType : ParamType) -- Semantic newtype; erased to baseType at Yul level (zero overhead) (#1727 Steps 3b/3c) deriving Repr, BEq structure Param where name : String ty : ParamType - deriving Repr + deriving Repr, BEq -- Convert to IR types def ParamType.toIRType : ParamType → IRType @@ -182,6 +187,8 @@ def ParamType.toIRType : ParamType → IRType | array _ => IRType.uint256 -- Arrays are represented as calldata offsets | fixedArray _ _ => IRType.uint256 | bytes => IRType.uint256 + | adt _ _ => IRType.uint256 -- ADTs are represented as storage offsets + | newtypeOf _ baseType => baseType.toIRType -- Erased to base type def Param.toIRParam (p : Param) : IRParam := { name := p.name, ty := p.ty.toIRType } @@ -244,6 +251,28 @@ structure LocalObligation where proofStatus : Compiler.ProofStatus := .assumed deriving Repr +/-! +### ADT Type Definitions (#1727, Phase 5 Step 5b) + +IR-level representation of user-defined algebraic data types (tagged unions). +Each variant carries a tag byte and typed fields. +-/ + +/-- A single variant of an ADT at the IR level. + `tag` is the 0-based index used for storage encoding. -/ +structure AdtVariant where + name : String + tag : Nat + fields : List Param + deriving Repr, BEq + +/-- A user-defined algebraic data type at the IR level. + Storage layout: tag byte (1 slot) + max-width fields in consecutive slots. -/ +structure AdtTypeDef where + name : String + variants : List AdtVariant + deriving Repr, BEq + /-! ## Function Body DSL @@ -366,6 +395,17 @@ inductive Expr Both branches are eagerly evaluated; `cond` is evaluated twice. For complex conditions with side effects, bind to a local variable first. -/ | ite (cond thenVal elseVal : Expr) + /-- Construct an ADT value: `adtConstruct adtName variantName args`. + Produces the tagged-union encoding for the given variant. (#1727 Step 5b) -/ + | adtConstruct (adtName variantName : String) (args : List Expr) + /-- Read the tag byte of an ADT value: `adtTag adtName field`. + Returns the 0-based tag index. (#1727 Step 5b) -/ + | adtTag (adtName field : String) + /-- Read a field from an ADT value stored in contract storage. + `storageField` names the contract storage field holding the ADT. + `fieldIndex` is the 0-based index within the variant's field list, + pre-resolved at IR construction time. (#1727 Steps 5b/5c) -/ + | adtField (adtName variantName fieldName : String) (fieldIndex : Nat) (storageField : String) deriving Repr inductive Stmt @@ -424,10 +464,28 @@ inductive Stmt (resultVars : List String) -- local vars to bind return values to (externalName : String) -- name of the external function declaration (args : List Expr) -- call arguments + /-- Perform an external call without auto-reverting on failure. + Binds a success flag (0/1) to `successVar` and return values to `resultVars`. + The caller is responsible for checking the success flag and handling failures. + (#1727, Axis 1 Step 5f) -/ + | tryExternalCallBind + (successVar : String) -- local var bound to success flag (0 = failure, 1 = success) + (resultVars : List String) -- local vars to bind return values to (only valid if success == 1) + (externalName : String) -- name of the external function declaration + (args : List Expr) -- call arguments /-- Invoke an External Call Module with the given arguments. This generic variant delegates validation, compilation, and state analysis to the module's metadata and compile function. See Compiler.ECM (#964). -/ | ecm (mod : ECM.ExternalCallModule) (args : List Expr) + /-- Unsafe block: `unsafe "reason" do body`. + Marks a region where restricted operations (Step 6b) are permitted. + The reason string is preserved for trust reporting (Step 6c). -/ + | unsafeBlock (reason : String) (body : List Stmt) + /-- Pattern match on an ADT value: `matchAdt adtName scrutinee branches`. + Each branch is `(variantName, boundVarNames, body)`. + Compiles to `YulStmt.switch` on the tag byte. (#1727 Step 5b) -/ + | matchAdt (adtName : String) (scrutinee : Expr) + (branches : List (String × List String × List Stmt)) deriving Repr structure FunctionSpec where @@ -442,6 +500,30 @@ structure FunctionSpec where /-- Whether this entrypoint is ABI-marked as `pure` (no state/environment reads intent). -/ isPure : Bool := false body : List Stmt + /-- Storage field names declared in `modifies(...)`. When non-empty the + compiler validates that the body only writes to these fields and emits + a frame theorem for all other fields. (#1729, Axis 3 Step 1b) -/ + modifies : List String := [] + /-- Whether this function is annotated `no_external_calls`. When true the + compiler validates that the body contains no external call statements + and emits a `_no_calls` theorem. (#1729, Axis 3 Step 1c) -/ + noExternalCalls : Bool := false + /-- Whether this function is annotated `allow_post_interaction_writes`. + When true, CEI enforcement is bypassed for this function. + (#1728, Axis 2 Step 2a) -/ + allowPostInteractionWrites : Bool := false + /-- Storage field name used as reentrancy lock when annotated `nonreentrant(field)`. + When non-empty, CEI enforcement is bypassed because the lock prevents + reentrant state corruption. (#1728, Axis 2 Step 2b) -/ + nonReentrantLock : Option String := none + /-- Whether this function is annotated `cei_safe` — the user asserts CEI + safety via a machine-checked proof obligation. CEI enforcement is bypassed + and a proof obligation is generated. (#1728, Axis 2 Step 2b) -/ + ceiSafe : Bool := false + /-- Storage field name used as access-control role when annotated `requires(field)`. + A `require(caller == roleHolder)` check is auto-injected at the start of the + function body. (#1728, Axis 2 Step 2c) -/ + requiresRole : Option String := none /-- Whether this is an internal-only function (not exposed via selector dispatch) -/ isInternal : Bool := false /-- Local proof obligations that isolate unsafe/assembly-shaped trust @@ -474,6 +556,13 @@ structure CompilationModel where events : List EventDef := [] -- Event definitions (#153) errors : List ErrorDef := [] -- Custom errors (#586) externals : List ExternalFunction := [] -- External function declarations (#184) + /-- User-defined algebraic data types (tagged unions). (#1727 Step 5b) -/ + adtTypes : List AdtTypeDef := [] + /-- EIP-7201 storage namespace offset. When `some n`, every user-declared + `slot k` was already shifted by `n` during macro elaboration. The value + is `keccak256("{ContractName}.storage.v0")` as a 256-bit Nat. + (#1730, Axis 4 Step 4d) -/ + storageNamespace : Option Nat := none deriving Repr /-! diff --git a/Compiler/CompilationModel/UsageAnalysis.lean b/Compiler/CompilationModel/UsageAnalysis.lean index 6f8e69119..4e93919ee 100644 --- a/Compiler/CompilationModel/UsageAnalysis.lean +++ b/Compiler/CompilationModel/UsageAnalysis.lean @@ -9,8 +9,13 @@ def collectStmtBindNames : Stmt → List String collectStmtListBindNames thenBranch ++ collectStmtListBindNames elseBranch | Stmt.forEach varName _ body => varName :: collectStmtListBindNames body + | Stmt.unsafeBlock _ body => + collectStmtListBindNames body + | Stmt.matchAdt _ _ branches => + collectMatchBranchBindNames branches | Stmt.internalCallAssign names _ _ => names | Stmt.externalCallBind resultVars _ _ => resultVars + | Stmt.tryExternalCallBind successVar resultVars _ _ => successVar :: resultVars | Stmt.ecm mod _ => mod.resultVars -- Statements that never bind new names. | Stmt.assignVar _ _ | Stmt.setStorage _ _ | Stmt.setStorageAddr _ _ @@ -34,6 +39,13 @@ def collectStmtListBindNames : List Stmt → List String collectStmtBindNames stmt ++ collectStmtListBindNames rest termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def collectMatchBranchBindNames : List (String × List String × List Stmt) → List String + | [] => [] + | (_, varNames, body) :: rest => + varNames ++ collectStmtListBindNames body ++ collectMatchBranchBindNames rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end mutual @@ -43,6 +55,10 @@ def collectStmtAssignedNames : Stmt → List String collectStmtListAssignedNames thenBranch ++ collectStmtListAssignedNames elseBranch | Stmt.forEach _ _ body => collectStmtListAssignedNames body + | Stmt.unsafeBlock _ body => + collectStmtListAssignedNames body + | Stmt.matchAdt _ _ branches => + collectMatchBranchAssignedNames branches | Stmt.letVar _ _ | Stmt.setStorage _ _ | Stmt.setStorageAddr _ _ | Stmt.storageArrayPush _ _ | Stmt.storageArrayPop _ | Stmt.setStorageArrayElement _ _ _ | Stmt.return _ @@ -54,7 +70,7 @@ def collectStmtAssignedNames : Stmt → List String | Stmt.returnValues _ | Stmt.returnArray _ | Stmt.returnBytes _ | Stmt.returnStorageWords _ | Stmt.mstore _ _ | Stmt.tstore _ _ | Stmt.calldatacopy _ _ _ | Stmt.returndataCopy _ _ _ | Stmt.revertReturndata | Stmt.stop | Stmt.emit _ _ | Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ - | Stmt.rawLog _ _ _ | Stmt.externalCallBind _ _ _ | Stmt.ecm _ _ => + | Stmt.rawLog _ _ _ | Stmt.externalCallBind _ _ _ | Stmt.tryExternalCallBind _ _ _ _ | Stmt.ecm _ _ => [] termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -65,6 +81,13 @@ def collectStmtListAssignedNames : List Stmt → List String collectStmtAssignedNames stmt ++ collectStmtListAssignedNames rest termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def collectMatchBranchAssignedNames : List (String × List String × List Stmt) → List String + | [] => [] + | (_, _, body) :: rest => + collectStmtListAssignedNames body ++ collectMatchBranchAssignedNames rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end mutual @@ -121,11 +144,14 @@ def exprUsesArrayElement : Expr → Bool exprUsesArrayElement a | Expr.ite cond thenVal elseVal => exprUsesArrayElement cond || exprUsesArrayElement thenVal || exprUsesArrayElement elseVal + | Expr.adtConstruct _ _ args => exprListUsesArrayElement args + | Expr.adtField _ _ _ _ _ => false -- Leaf expressions: no sub-expressions that could contain arrayElement. | Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee - | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ => + | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ + | Expr.adtTag _ _ => false termination_by e => sizeOf e decreasing_by all_goals simp_wf; all_goals omega @@ -169,11 +195,16 @@ def stmtUsesArrayElement : Stmt → Bool exprUsesArrayElement cond || stmtListUsesArrayElement thenBranch || stmtListUsesArrayElement elseBranch | Stmt.forEach _ count body => exprUsesArrayElement count || stmtListUsesArrayElement body + | Stmt.unsafeBlock _ body => + stmtListUsesArrayElement body + | Stmt.matchAdt _ scrutinee branches => + exprUsesArrayElement scrutinee || + matchBranchesUseArrayElement branches | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args => exprListUsesArrayElement args | Stmt.rawLog topics dataOffset dataSize => exprListUsesArrayElement topics || exprUsesArrayElement dataOffset || exprUsesArrayElement dataSize - | Stmt.externalCallBind _ _ args => + | Stmt.externalCallBind _ _ args | Stmt.tryExternalCallBind _ _ _ args => exprListUsesArrayElement args | Stmt.ecm _ args => exprListUsesArrayElement args @@ -189,6 +220,13 @@ def stmtListUsesArrayElement : List Stmt → Bool | s :: ss => stmtUsesArrayElement s || stmtListUsesArrayElement ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesUseArrayElement : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListUsesArrayElement body || matchBranchesUseArrayElement rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def functionUsesArrayElement (fn : FunctionSpec) : Bool := @@ -249,11 +287,13 @@ def exprUsesStorageArrayElement : Expr → Bool exprUsesStorageArrayElement a | Expr.ite cond thenVal elseVal => exprUsesStorageArrayElement cond || exprUsesStorageArrayElement thenVal || exprUsesStorageArrayElement elseVal + | Expr.adtConstruct _ _ args => exprListUsesStorageArrayElement args + | Expr.adtField _ _ _ _ _ => false | Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ - | Expr.arrayElement _ _ => + | Expr.arrayElement _ _ | Expr.adtTag _ _ => false termination_by e => sizeOf e decreasing_by all_goals simp_wf; all_goals omega @@ -297,7 +337,13 @@ def stmtUsesStorageArrayElement : Stmt → Bool exprUsesStorageArrayElement cond || stmtListUsesStorageArrayElement thenBranch || stmtListUsesStorageArrayElement elseBranch | Stmt.forEach _ count body => exprUsesStorageArrayElement count || stmtListUsesStorageArrayElement body - | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args | Stmt.externalCallBind _ _ args => + | Stmt.unsafeBlock _ body => + stmtListUsesStorageArrayElement body + | Stmt.matchAdt _ scrutinee branches => + exprUsesStorageArrayElement scrutinee || + matchBranchesUseStorageArrayElement branches + | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args + | Stmt.externalCallBind _ _ args | Stmt.tryExternalCallBind _ _ _ args => exprListUsesStorageArrayElement args | Stmt.rawLog topics dataOffset dataSize => exprListUsesStorageArrayElement topics || exprUsesStorageArrayElement dataOffset || exprUsesStorageArrayElement dataSize @@ -314,6 +360,13 @@ def stmtListUsesStorageArrayElement : List Stmt → Bool | s :: ss => stmtUsesStorageArrayElement s || stmtListUsesStorageArrayElement ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesUseStorageArrayElement : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListUsesStorageArrayElement body || matchBranchesUseStorageArrayElement rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def functionUsesStorageArrayElement (fn : FunctionSpec) : Bool := @@ -373,10 +426,13 @@ def exprUsesDynamicBytesEq : Expr → Bool exprUsesDynamicBytesEq a | Expr.ite cond thenVal elseVal => exprUsesDynamicBytesEq cond || exprUsesDynamicBytesEq thenVal || exprUsesDynamicBytesEq elseVal + | Expr.adtConstruct _ _ args => exprListUsesDynamicBytesEq args + | Expr.adtField _ _ _ _ _ => false | Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee - | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ => + | Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _ | Expr.storageArrayLength _ + | Expr.adtTag _ _ => false termination_by e => sizeOf e decreasing_by all_goals simp_wf; all_goals omega @@ -418,8 +474,13 @@ def stmtUsesDynamicBytesEq : Stmt → Bool exprUsesDynamicBytesEq cond || stmtListUsesDynamicBytesEq thenBranch || stmtListUsesDynamicBytesEq elseBranch | Stmt.forEach _ count body => exprUsesDynamicBytesEq count || stmtListUsesDynamicBytesEq body + | Stmt.unsafeBlock _ body => + stmtListUsesDynamicBytesEq body + | Stmt.matchAdt _ scrutinee branches => + exprUsesDynamicBytesEq scrutinee || + matchBranchesUseDynamicBytesEq branches | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args - | Stmt.externalCallBind _ _ args + | Stmt.externalCallBind _ _ args | Stmt.tryExternalCallBind _ _ _ args | Stmt.ecm _ args => exprListUsesDynamicBytesEq args | Stmt.rawLog topics dataOffset dataSize => @@ -435,6 +496,13 @@ def stmtListUsesDynamicBytesEq : List Stmt → Bool | s :: ss => stmtUsesDynamicBytesEq s || stmtListUsesDynamicBytesEq ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesUseDynamicBytesEq : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListUsesDynamicBytesEq body || matchBranchesUseDynamicBytesEq rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def contractUsesDynamicBytesEq (spec : CompilationModel) : Bool := diff --git a/Compiler/CompilationModel/Validation.lean b/Compiler/CompilationModel/Validation.lean index 4561cc48d..e74a34a7e 100644 --- a/Compiler/CompilationModel/Validation.lean +++ b/Compiler/CompilationModel/Validation.lean @@ -27,6 +27,31 @@ namespace Compiler.CompilationModel open Compiler open Compiler.Yul +private def firstDuplicateString : List String → Option String + | [] => none + | name :: rest => + if rest.contains name then some name else firstDuplicateString rest + +private def adtPayloadParamNames (params : List Param) : List String := + params.flatMap fun param => + match param.ty with + | ParamType.adt _ maxFields => + (List.range maxFields).map fun idx => s!"{param.name}_f{idx}" + | _ => [] + +private def validateAdtPayloadParamNameCollisions + (context : String) (params : List Param) (body : List Stmt) : Except String Unit := do + let generated := adtPayloadParamNames params + match firstDuplicateString generated with + | some name => + throw s!"Compilation error: {context} has ADT parameters whose generated payload local '{name}' collides. Rename the ADT parameters so generated '_f' locals are unique." + | none => pure () + let userNames := params.map (·.name) ++ collectStmtListBindNames body + match generated.find? (fun name => userNames.contains name) with + | some name => + throw s!"Compilation error: {context} reserves generated ADT payload local '{name}'. Rename the parameter or local binding that conflicts with generated '_f' locals." + | none => pure () + def isStorageWordArrayParam : ParamType → Bool | ty => isWordArrayParam ty @@ -63,6 +88,10 @@ def validateStmtParamReferences (fnName : String) (params : List Param) : validateStmtParamReferencesInList fnName params elseBranch | Stmt.forEach _ _ body => do validateStmtParamReferencesInList fnName params body + | Stmt.unsafeBlock _ body => do + validateStmtParamReferencesInList fnName params body + | Stmt.matchAdt _ _ branches => + validateStmtParamReferencesInBranches fnName params branches | _ => pure () termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -75,6 +104,15 @@ def validateStmtParamReferencesInList (fnName : String) (params : List Param) : validateStmtParamReferencesInList fnName params ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateStmtParamReferencesInBranches (fnName : String) (params : List Param) : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateStmtParamReferencesInList fnName params body + validateStmtParamReferencesInBranches fnName params rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end mutual @@ -139,6 +177,10 @@ def validateReturnShapesInStmt (fnName : String) (params : List Param) validateReturnShapesInStmtList fnName params expectedReturns isInternal elseBranch | Stmt.forEach _ _ body => validateReturnShapesInStmtList fnName params expectedReturns isInternal body + | Stmt.unsafeBlock _ body => + validateReturnShapesInStmtList fnName params expectedReturns isInternal body + | Stmt.matchAdt _ _ branches => + validateReturnShapesInBranches fnName params expectedReturns isInternal branches | _ => pure () termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -151,6 +193,16 @@ def validateReturnShapesInStmtList (fnName : String) validateReturnShapesInStmtList fnName params expectedReturns isInternal ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateReturnShapesInBranches (fnName : String) + (params : List Param) (expectedReturns : List ParamType) (isInternal : Bool) : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateReturnShapesInStmtList fnName params expectedReturns isInternal body + validateReturnShapesInBranches fnName params expectedReturns isInternal rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end mutual @@ -171,10 +223,21 @@ mutual true | Stmt.ite _ thenBranch elseBranch => stmtListAlwaysReturnsOrReverts thenBranch && stmtListAlwaysReturnsOrReverts elseBranch + | Stmt.unsafeBlock _ body => + stmtListAlwaysReturnsOrReverts body + | Stmt.matchAdt _ _ branches => + matchBranchesAllReturnOrRevert branches | _ => false termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega + + private def matchBranchesAllReturnOrRevert : List (String × List String × List Stmt) → Bool + | [] => true + | (_, _, body) :: rest => + stmtListAlwaysReturnsOrReverts body && matchBranchesAllReturnOrRevert rest + termination_by bs => sizeOf bs + decreasing_by all_goals simp_wf; all_goals omega end def exprReadsStateOrEnv : Expr → Bool @@ -226,6 +289,12 @@ def exprReadsStateOrEnv : Expr → Bool exprReadsStateOrEnv a | Expr.ite cond thenVal elseVal => exprReadsStateOrEnv cond || exprReadsStateOrEnv thenVal || exprReadsStateOrEnv elseVal + | Expr.adtConstruct _ _ args => exprListReadsStateOrEnv args + | Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ => true +where + exprListReadsStateOrEnv : List Expr → Bool + | [] => false + | e :: es => exprReadsStateOrEnv e || exprListReadsStateOrEnv es mutual def exprWritesState : Expr → Bool @@ -269,6 +338,7 @@ def exprWritesState : Expr → Bool | Expr.dynamicBytesEq _ _ => false | Expr.externalCall _ _ | Expr.internalCall _ _ => true + | Expr.adtConstruct _ _ args => exprListWritesState args | Expr.extcodesize addr => exprWritesState addr | Expr.storageArrayLength _ => @@ -329,11 +399,16 @@ def stmtWritesState : Stmt → Bool exprWritesState cond || stmtListWritesState thenBranch || stmtListWritesState elseBranch | Stmt.forEach _ count body => exprWritesState count || stmtListWritesState body + | Stmt.unsafeBlock _ body => + stmtListWritesState body | Stmt.emit _ _ | Stmt.rawLog _ _ _ | Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ - | Stmt.externalCallBind _ _ _ => true + | Stmt.externalCallBind _ _ _ | Stmt.tryExternalCallBind _ _ _ _ => true | Stmt.ecm mod args => mod.writesState || exprListWritesState args + | Stmt.matchAdt _ scrutinee branches => + exprWritesState scrutinee || + matchBranchesWriteState branches termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -342,6 +417,421 @@ def stmtListWritesState : List Stmt → Bool | s :: ss => stmtWritesState s || stmtListWritesState ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesWriteState : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListWritesState body || matchBranchesWriteState rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Collect the set of storage field names written by a statement. + Returns a list of field name strings found in `setStorage`, `setStorageAddr`, + `setMapping*`, `storageArray*`, and `setStructMember*` constructors. + Used by `modifies(...)` validation (#1729, Axis 3 Step 1b). -/ +def stmtWrittenFields : Stmt → List String + | Stmt.setStorage field _ | Stmt.setStorageAddr field _ + | Stmt.storageArrayPush field _ | Stmt.storageArrayPop field | Stmt.setStorageArrayElement field _ _ + | Stmt.setMapping field _ _ | Stmt.setMappingWord field _ _ _ | Stmt.setMappingPackedWord field _ _ _ _ + | Stmt.setMappingUint field _ _ + | Stmt.setMappingChain field _ _ + | Stmt.setMapping2 field _ _ _ | Stmt.setMapping2Word field _ _ _ _ + | Stmt.setStructMember field _ _ _ | Stmt.setStructMember2 field _ _ _ _ => [field] + | Stmt.ite _ thenBranch elseBranch => + stmtListWrittenFields thenBranch ++ stmtListWrittenFields elseBranch + | Stmt.forEach _ _ body => + stmtListWrittenFields body + | Stmt.unsafeBlock _ body => + stmtListWrittenFields body + | Stmt.matchAdt _ _ branches => + matchBranchesWrittenFields branches + | _ => [] +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def stmtListWrittenFields : List Stmt → List String + | [] => [] + | s :: ss => stmtWrittenFields s ++ stmtListWrittenFields ss +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesWrittenFields : List (String × List String × List Stmt) → List String + | [] => [] + | (_, _, body) :: rest => + stmtListWrittenFields body ++ matchBranchesWrittenFields rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Detect expression-position internal helper calls whose callee write set is + not visible to single-function `modifies(...)` validation. -/ +def exprHasUntrackableWrites : Expr → Bool + | Expr.internalCall _ _ => true + | Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b + | Expr.mod a b | Expr.smod a b + | Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b + | Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b + | Expr.ge a b | Expr.le a b | Expr.signextend a b + | Expr.logicalAnd a b | Expr.logicalOr a b + | Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b + | Expr.ceilDiv a b => + exprHasUntrackableWrites a || exprHasUntrackableWrites b + | Expr.mulDivDown a b c | Expr.mulDivUp a b c => + exprHasUntrackableWrites a || exprHasUntrackableWrites b || exprHasUntrackableWrites c + | Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a => + exprHasUntrackableWrites a + | Expr.ite cond thenVal elseVal => + exprHasUntrackableWrites cond || exprHasUntrackableWrites thenVal || exprHasUntrackableWrites elseVal + | Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key + | Expr.structMember _ key _ | Expr.arrayElement _ key | Expr.storageArrayElement _ key => + exprHasUntrackableWrites key + | Expr.mappingChain _ keys => + exprListHasUntrackableWrites keys + | Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _ + | Expr.structMember2 _ key1 key2 _ => + exprHasUntrackableWrites key1 || exprHasUntrackableWrites key2 + | Expr.mload offset | Expr.tload offset | Expr.calldataload offset + | Expr.returndataOptionalBoolAt offset => + exprHasUntrackableWrites offset + | Expr.keccak256 offset size => + exprHasUntrackableWrites offset || exprHasUntrackableWrites size + | Expr.adtConstruct _ _ args => + exprListHasUntrackableWrites args + | _ => false +termination_by e => sizeOf e +decreasing_by all_goals simp_wf; all_goals omega + +def exprListHasUntrackableWrites : List Expr → Bool + | [] => false + | e :: es => exprHasUntrackableWrites e || exprListHasUntrackableWrites es +termination_by es => sizeOf es +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Check whether a statement may write to storage fields that `stmtWrittenFields` + cannot track — specifically internal calls whose callee bodies are not visible + at single-function validation scope. External calls (`externalCallBind`, + `tryExternalCallBind`, `ecm`) target other contracts and cannot directly modify + the current contract's storage fields, so they are safe for `modifies()`. + Used by `modifies(...)` validation to conservatively reject annotations when + write-set tracking is incomplete. -/ +def stmtHasUntrackableWrites : Stmt → Bool + | Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ => true + | Stmt.letVar _ value | Stmt.assignVar _ value => + exprHasUntrackableWrites value + | Stmt.setStorage _ value | Stmt.setStorageAddr _ value | Stmt.require value _ => + exprHasUntrackableWrites value + | Stmt.requireError cond _ args => + exprHasUntrackableWrites cond || args.any exprHasUntrackableWrites + | Stmt.revertError _ args | Stmt.returnValues args | Stmt.emit _ args => + args.any exprHasUntrackableWrites + | Stmt.return value | Stmt.storageArrayPush _ value => + exprHasUntrackableWrites value + | Stmt.setStorageArrayElement _ index value => + exprHasUntrackableWrites index || exprHasUntrackableWrites value + | Stmt.setMapping _ key value | Stmt.setMappingUint _ key value => + exprHasUntrackableWrites key || exprHasUntrackableWrites value + | Stmt.setMappingWord _ key _ value | Stmt.setMappingPackedWord _ key _ _ value => + exprHasUntrackableWrites key || exprHasUntrackableWrites value + | Stmt.setMappingChain _ keys value => + keys.any exprHasUntrackableWrites || exprHasUntrackableWrites value + | Stmt.setMapping2 _ key1 key2 value | Stmt.setMapping2Word _ key1 key2 _ value + | Stmt.setStructMember2 _ key1 key2 _ value => + exprHasUntrackableWrites key1 || exprHasUntrackableWrites key2 || exprHasUntrackableWrites value + | Stmt.setStructMember _ key _ value => + exprHasUntrackableWrites key || exprHasUntrackableWrites value + | Stmt.rawLog topics dataOffset dataSize => + topics.any exprHasUntrackableWrites || exprHasUntrackableWrites dataOffset || exprHasUntrackableWrites dataSize + | Stmt.mstore offset value | Stmt.tstore offset value => + exprHasUntrackableWrites offset || exprHasUntrackableWrites value + | Stmt.calldatacopy destOffset sourceOffset size | Stmt.returndataCopy destOffset sourceOffset size => + exprHasUntrackableWrites destOffset || exprHasUntrackableWrites sourceOffset || exprHasUntrackableWrites size + | Stmt.ite cond thenBranch elseBranch => + exprHasUntrackableWrites cond || + stmtListHasUntrackableWrites thenBranch || + stmtListHasUntrackableWrites elseBranch + | Stmt.forEach _ count body => + exprHasUntrackableWrites count || stmtListHasUntrackableWrites body + | Stmt.unsafeBlock _ body => + stmtListHasUntrackableWrites body + | Stmt.matchAdt _ scrutinee branches => + exprHasUntrackableWrites scrutinee || matchBranchesHasUntrackableWrites branches + | _ => false +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def stmtListHasUntrackableWrites : List Stmt → Bool + | [] => false + | s :: ss => stmtHasUntrackableWrites s || stmtListHasUntrackableWrites ss +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesHasUntrackableWrites : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListHasUntrackableWrites body || matchBranchesHasUntrackableWrites rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Check whether an expression contains an external call (call, staticcall, delegatecall, + or externalCall). Used by `no_external_calls` validation (#1729, Axis 3 Step 1c). -/ +def exprContainsExternalCall : Expr → Bool + | Expr.call _ _ _ _ _ _ _ | Expr.staticcall _ _ _ _ _ _ + | Expr.delegatecall _ _ _ _ _ _ | Expr.externalCall _ _ => true + | Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b + | Expr.mod a b | Expr.smod a b + | Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b + | Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b + | Expr.ge a b | Expr.le a b | Expr.signextend a b + | Expr.logicalAnd a b | Expr.logicalOr a b + | Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b + | Expr.ceilDiv a b => + exprContainsExternalCall a || exprContainsExternalCall b + | Expr.mulDivDown a b c | Expr.mulDivUp a b c => + exprContainsExternalCall a || exprContainsExternalCall b || exprContainsExternalCall c + | Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a => + exprContainsExternalCall a + | Expr.ite cond thenVal elseVal => + exprContainsExternalCall cond || exprContainsExternalCall thenVal || exprContainsExternalCall elseVal + | Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key + | Expr.structMember _ key _ | Expr.arrayElement _ key | Expr.storageArrayElement _ key => + exprContainsExternalCall key + | Expr.mappingChain _ keys => + exprListContainsExternalCall keys + | Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _ + | Expr.structMember2 _ key1 key2 _ => + exprContainsExternalCall key1 || exprContainsExternalCall key2 + | Expr.mload offset | Expr.tload offset | Expr.calldataload offset + | Expr.returndataOptionalBoolAt offset => + exprContainsExternalCall offset + | Expr.keccak256 offset size => + exprContainsExternalCall offset || exprContainsExternalCall size + | Expr.internalCall _ args => + exprListContainsExternalCall args + | Expr.adtConstruct _ _ args => + exprListContainsExternalCall args + | Expr.dynamicBytesEq _ _ => false + | _ => false +termination_by e => sizeOf e +decreasing_by all_goals simp_wf; all_goals omega + +def exprListContainsExternalCall : List Expr → Bool + | [] => false + | e :: es => exprContainsExternalCall e || exprListContainsExternalCall es +termination_by es => sizeOf es +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Conservative expression call detector for annotations such as + `no_external_calls`, where an internal helper expression may itself perform + an external interaction. CEI uses `exprContainsExternalCall` instead so that + local helper reads do not become false interaction barriers. -/ +def exprMayContainExternalCall : Expr → Bool + | Expr.internalCall _ _ => true + | Expr.call _ _ _ _ _ _ _ | Expr.staticcall _ _ _ _ _ _ + | Expr.delegatecall _ _ _ _ _ _ | Expr.externalCall _ _ => true + | Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b + | Expr.mod a b | Expr.smod a b + | Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b + | Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b + | Expr.ge a b | Expr.le a b | Expr.signextend a b + | Expr.logicalAnd a b | Expr.logicalOr a b + | Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b + | Expr.ceilDiv a b => + exprMayContainExternalCall a || exprMayContainExternalCall b + | Expr.mulDivDown a b c | Expr.mulDivUp a b c => + exprMayContainExternalCall a || exprMayContainExternalCall b || exprMayContainExternalCall c + | Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a => + exprMayContainExternalCall a + | Expr.ite cond thenVal elseVal => + exprMayContainExternalCall cond || exprMayContainExternalCall thenVal || exprMayContainExternalCall elseVal + | Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key + | Expr.structMember _ key _ | Expr.arrayElement _ key | Expr.storageArrayElement _ key => + exprMayContainExternalCall key + | Expr.mappingChain _ keys => + exprListMayContainExternalCall keys + | Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _ + | Expr.structMember2 _ key1 key2 _ => + exprMayContainExternalCall key1 || exprMayContainExternalCall key2 + | Expr.mload offset | Expr.tload offset | Expr.calldataload offset + | Expr.returndataOptionalBoolAt offset => + exprMayContainExternalCall offset + | Expr.keccak256 offset size => + exprMayContainExternalCall offset || exprMayContainExternalCall size + | Expr.adtConstruct _ _ args => + exprListMayContainExternalCall args + | Expr.dynamicBytesEq _ _ => false + | _ => false +termination_by e => sizeOf e +decreasing_by all_goals simp_wf; all_goals omega + +def exprListMayContainExternalCall : List Expr → Bool + | [] => false + | e :: es => exprMayContainExternalCall e || exprListMayContainExternalCall es +termination_by es => sizeOf es +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Check whether a statement contains an external call (externalCallBind, ecm, or + an expression with call/staticcall/delegatecall/externalCall). + Used by `no_external_calls` validation (#1729, Axis 3 Step 1c). -/ +def stmtContainsExternalCall : Stmt → Bool + | Stmt.externalCallBind _ _ _ | Stmt.tryExternalCallBind _ _ _ _ => true + | Stmt.ecm _ _ => true + | Stmt.letVar _ value | Stmt.assignVar _ value => + exprContainsExternalCall value + | Stmt.setStorage _ value | Stmt.setStorageAddr _ value | Stmt.require value _ => + exprContainsExternalCall value + | Stmt.requireError cond _ args => + exprContainsExternalCall cond || args.any exprContainsExternalCall + | Stmt.revertError _ args => + args.any exprContainsExternalCall + | Stmt.return value => + exprContainsExternalCall value + | Stmt.returnValues values => + values.any exprContainsExternalCall + | Stmt.storageArrayPush _ value => + exprContainsExternalCall value + | Stmt.setStorageArrayElement _ index value => + exprContainsExternalCall index || exprContainsExternalCall value + | Stmt.setMapping _ key value | Stmt.setMappingUint _ key value => + exprContainsExternalCall key || exprContainsExternalCall value + | Stmt.setMappingWord _ key _ value => + exprContainsExternalCall key || exprContainsExternalCall value + | Stmt.setMappingPackedWord _ key _ _ value => + exprContainsExternalCall key || exprContainsExternalCall value + | Stmt.setMappingChain _ keys value => + keys.any exprContainsExternalCall || exprContainsExternalCall value + | Stmt.setMapping2 _ key1 key2 value => + exprContainsExternalCall key1 || exprContainsExternalCall key2 || exprContainsExternalCall value + | Stmt.setMapping2Word _ key1 key2 _ value => + exprContainsExternalCall key1 || exprContainsExternalCall key2 || exprContainsExternalCall value + | Stmt.setStructMember _ key _ value => + exprContainsExternalCall key || exprContainsExternalCall value + | Stmt.setStructMember2 _ key1 key2 _ value => + exprContainsExternalCall key1 || exprContainsExternalCall key2 || exprContainsExternalCall value + | Stmt.emit _ args => + args.any exprContainsExternalCall + | Stmt.rawLog topics dataOffset dataSize => + topics.any exprContainsExternalCall || exprContainsExternalCall dataOffset || exprContainsExternalCall dataSize + | Stmt.tstore offset value | Stmt.mstore offset value => + exprContainsExternalCall offset || exprContainsExternalCall value + | Stmt.calldatacopy destOffset sourceOffset size => + exprContainsExternalCall destOffset || exprContainsExternalCall sourceOffset || exprContainsExternalCall size + | Stmt.returndataCopy destOffset sourceOffset size => + exprContainsExternalCall destOffset || exprContainsExternalCall sourceOffset || exprContainsExternalCall size + | Stmt.ite cond thenBranch elseBranch => + exprContainsExternalCall cond || stmtListContainsExternalCall thenBranch || stmtListContainsExternalCall elseBranch + | Stmt.forEach _ count body => + exprContainsExternalCall count || stmtListContainsExternalCall body + | Stmt.unsafeBlock _ body => + stmtListContainsExternalCall body + | Stmt.matchAdt _ scrutinee branches => + exprContainsExternalCall scrutinee || + matchBranchesContainExternalCall branches + | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args => + args.any exprContainsExternalCall + | _ => false +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def stmtListContainsExternalCall : List Stmt → Bool + | [] => false + | s :: ss => stmtContainsExternalCall s || stmtListContainsExternalCall ss +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesContainExternalCall : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListContainsExternalCall body || matchBranchesContainExternalCall rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Conservative variant of `stmtContainsExternalCall` for `no_external_calls` + validation. Returns `true` for internal calls anywhere in the statement tree, + because callee bodies may contain external calls that are not visible at + single-function validation scope. -/ +def stmtMayContainExternalCall : Stmt → Bool + | Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ => true + | Stmt.ite cond thenBranch elseBranch => + exprMayContainExternalCall cond || + stmtListMayContainExternalCall thenBranch || + stmtListMayContainExternalCall elseBranch + | Stmt.forEach _ count body => + exprMayContainExternalCall count || stmtListMayContainExternalCall body + | Stmt.unsafeBlock _ body => + stmtListMayContainExternalCall body + | Stmt.matchAdt _ scrutinee branches => + exprMayContainExternalCall scrutinee || matchBranchesMayContainExternalCall branches + | Stmt.letVar _ value | Stmt.assignVar _ value => + exprMayContainExternalCall value + | Stmt.setStorage _ value | Stmt.setStorageAddr _ value | Stmt.require value _ => + exprMayContainExternalCall value + | Stmt.requireError cond _ args => + exprMayContainExternalCall cond || args.any exprMayContainExternalCall + | Stmt.revertError _ args => + args.any exprMayContainExternalCall + | Stmt.return value => + exprMayContainExternalCall value + | Stmt.returnValues values => + values.any exprMayContainExternalCall + | Stmt.storageArrayPush _ value => + exprMayContainExternalCall value + | Stmt.setStorageArrayElement _ index value => + exprMayContainExternalCall index || exprMayContainExternalCall value + | Stmt.setMapping _ key value | Stmt.setMappingUint _ key value => + exprMayContainExternalCall key || exprMayContainExternalCall value + | Stmt.setMappingWord _ key _ value => + exprMayContainExternalCall key || exprMayContainExternalCall value + | Stmt.setMappingPackedWord _ key _ _ value => + exprMayContainExternalCall key || exprMayContainExternalCall value + | Stmt.setMappingChain _ keys value => + keys.any exprMayContainExternalCall || exprMayContainExternalCall value + | Stmt.setMapping2 _ key1 key2 value => + exprMayContainExternalCall key1 || exprMayContainExternalCall key2 || exprMayContainExternalCall value + | Stmt.setMapping2Word _ key1 key2 _ value => + exprMayContainExternalCall key1 || exprMayContainExternalCall key2 || exprMayContainExternalCall value + | Stmt.setStructMember _ key _ value => + exprMayContainExternalCall key || exprMayContainExternalCall value + | Stmt.setStructMember2 _ key1 key2 _ value => + exprMayContainExternalCall key1 || exprMayContainExternalCall key2 || exprMayContainExternalCall value + | Stmt.emit _ args => + args.any exprMayContainExternalCall + | Stmt.rawLog topics dataOffset dataSize => + topics.any exprMayContainExternalCall || exprMayContainExternalCall dataOffset || exprMayContainExternalCall dataSize + | Stmt.tstore offset value | Stmt.mstore offset value => + exprMayContainExternalCall offset || exprMayContainExternalCall value + | Stmt.calldatacopy destOffset sourceOffset size => + exprMayContainExternalCall destOffset || exprMayContainExternalCall sourceOffset || exprMayContainExternalCall size + | Stmt.returndataCopy destOffset sourceOffset size => + exprMayContainExternalCall destOffset || exprMayContainExternalCall sourceOffset || exprMayContainExternalCall size + | s => stmtContainsExternalCall s +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def stmtListMayContainExternalCall : List Stmt → Bool + | [] => false + | s :: ss => stmtMayContainExternalCall s || stmtListMayContainExternalCall ss +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesMayContainExternalCall : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListMayContainExternalCall body || matchBranchesMayContainExternalCall rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end mutual @@ -380,11 +870,16 @@ def stmtReadsStateOrEnv : Stmt → Bool exprReadsStateOrEnv cond || stmtListReadsStateOrEnv thenBranch || stmtListReadsStateOrEnv elseBranch | Stmt.forEach _ count body => exprReadsStateOrEnv count || stmtListReadsStateOrEnv body + | Stmt.unsafeBlock _ body => + stmtListReadsStateOrEnv body | Stmt.rawLog topics dataOffset dataSize => topics.any exprReadsStateOrEnv || exprReadsStateOrEnv dataOffset || exprReadsStateOrEnv dataSize | Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ - | Stmt.externalCallBind _ _ _ => true + | Stmt.externalCallBind _ _ _ | Stmt.tryExternalCallBind _ _ _ _ => true | Stmt.ecm mod args => mod.readsState || mod.writesState || args.any exprReadsStateOrEnv + | Stmt.matchAdt _ scrutinee branches => + exprReadsStateOrEnv scrutinee || + matchBranchesReadStateOrEnv branches termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -393,12 +888,346 @@ def stmtListReadsStateOrEnv : List Stmt → Bool | s :: ss => stmtReadsStateOrEnv s || stmtListReadsStateOrEnv ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesReadStateOrEnv : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListReadsStateOrEnv body || matchBranchesReadStateOrEnv rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Check whether a single statement contains a persistent-storage write (recursively). + This covers all `setStorage*`, `setMapping*`, `storageArray*`, `setStructMember*`, + and `tstore` constructors, and recurses into `ite`, `forEach`, `unsafeBlock`, and + `matchAdt` to detect nested writes. Events, local variables, and memory writes are + NOT considered persistent state writes for CEI purposes. + (#1728, Axis 2 Step 2a) -/ +def stmtIsPersistentWrite : Stmt → Bool + | Stmt.setStorage _ _ | Stmt.setStorageAddr _ _ + | Stmt.storageArrayPush _ _ | Stmt.storageArrayPop _ | Stmt.setStorageArrayElement _ _ _ + | Stmt.setMapping _ _ _ | Stmt.setMappingWord _ _ _ _ | Stmt.setMappingPackedWord _ _ _ _ _ | Stmt.setMappingUint _ _ _ + | Stmt.setMappingChain _ _ _ + | Stmt.setMapping2 _ _ _ _ | Stmt.setMapping2Word _ _ _ _ _ + | Stmt.setStructMember _ _ _ _ | Stmt.setStructMember2 _ _ _ _ _ + | Stmt.tstore _ _ -- transient storage persists across calls within a transaction + => true + | Stmt.ite _ thenBranch elseBranch => + stmtListContainsPersistentWrite thenBranch || stmtListContainsPersistentWrite elseBranch + | Stmt.forEach _ _ body => + stmtListContainsPersistentWrite body + | Stmt.unsafeBlock _ body => + stmtListContainsPersistentWrite body + | Stmt.matchAdt _ _ branches => + matchBranchesPersistentWrite branches + | _ => false +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def stmtListContainsPersistentWrite : List Stmt → Bool + | [] => false + | s :: rest => stmtIsPersistentWrite s || stmtListContainsPersistentWrite rest +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesPersistentWrite : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListContainsPersistentWrite body || matchBranchesPersistentWrite rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- Conservative variant of `stmtIsPersistentWrite` for CEI validation. + Returns `true` for internal calls and internal call assignments because + their callee bodies may write to storage but we cannot inspect them at + single-function validation scope. Recurses through compound statements so + nested helper calls are not missed by loop/cross-branch CEI checks. -/ +def stmtMayPersistentlyWrite : Stmt → Bool + | Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ => true + | Stmt.ite _ thenBranch elseBranch => + stmtListMayPersistentlyWrite thenBranch || stmtListMayPersistentlyWrite elseBranch + | Stmt.forEach _ _ body => + stmtListMayPersistentlyWrite body + | Stmt.unsafeBlock _ body => + stmtListMayPersistentlyWrite body + | Stmt.matchAdt _ _ branches => + matchBranchesMayPersistentlyWrite branches + | s => stmtIsPersistentWrite s +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def stmtListMayPersistentlyWrite : List Stmt → Bool + | [] => false + | s :: rest => stmtMayPersistentlyWrite s || stmtListMayPersistentlyWrite rest +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesMayPersistentlyWrite : List (String × List String × List Stmt) → Bool + | [] => false + | (_, _, body) :: rest => + stmtListMayPersistentlyWrite body || matchBranchesMayPersistentlyWrite rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +/-- CEI analysis: walk a statement list sequentially and return a descriptive + violation string if a persistent-storage write occurs after any statement + that is or contains an external call. Returns `none` if compliant. + For `ite`, each branch is checked independently AND if either branch contains + an external call, subsequent statements must not write state. + For `forEach`, the body is checked and if it contains an external call the + loop is treated as an interaction for subsequent statements. + (#1728, Axis 2 Step 2a) -/ +def stmtListCEIViolation : List Stmt → Bool → Option String + | [], _ => none + | s :: rest, seenCall => + -- First, check for CEI violation within this statement itself (propagating seenCall) + match stmtInternalCEIViolation s seenCall with + | some msg => some msg + | none => + -- For compound statements (ite, forEach, unsafeBlock, matchAdt), the internal + -- CEI check above already verified ordering within the statement's branches. + let isCompound := match s with + | Stmt.ite _ _ _ | Stmt.forEach _ _ _ | Stmt.unsafeBlock _ _ + | Stmt.matchAdt _ _ _ => true + | _ => false + -- Update seenCall conservatively: statement-form internal calls may + -- perform interactions inside the callee, so callers must treat them + -- as interaction barriers before any later persistent write. + let newSeenCall := seenCall || stmtMayContainExternalCall s + -- Write check: use `stmtMayPersistentlyWrite` which conservatively treats + -- internal calls as potential writes (since callee bodies may write storage + -- but are not visible at this scope). This catches the pattern: + -- externalCallBind(...) -- seenCall becomes true + -- internalCall(helper, [...]) -- may write storage → flagged + if !isCompound && stmtContainsExternalCall s && stmtMayPersistentlyWrite s then + some "state write in same statement as external call" + else if !isCompound && newSeenCall && stmtMayPersistentlyWrite s then + some "state write after external call" + else + stmtListCEIViolation rest newSeenCall +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +/-- Check for CEI violations within a single compound statement (ite, forEach). + Accepts `seenCall` from the enclosing context so that an external call before + an `ite` correctly flags writes inside either branch. + Returns a descriptive string if a violation is found within the statement's + own nested structure. -/ +def stmtInternalCEIViolation : Stmt → Bool → Option String + | Stmt.ite cond thenBranch elseBranch, seenCall => + -- Include external calls from the condition expression itself, so + -- `if externalCall(...) then setStorage ...` is correctly flagged + let condSeenCall := seenCall || exprMayContainExternalCall cond + match stmtListCEIViolation thenBranch condSeenCall with + | some msg => some s!"in if-then branch: {msg}" + | none => + match stmtListCEIViolation elseBranch condSeenCall with + | some msg => some s!"in if-else branch: {msg}" + | none => none + | Stmt.forEach _ count body, seenCall => + -- In a loop, if the body has both an external call and a state write, + -- a second iteration would violate CEI even if the first doesn't + let bodyHasCall := body.any stmtMayContainExternalCall + let bodyHasWrite := body.any stmtMayPersistentlyWrite + if bodyHasCall && bodyHasWrite then + some "loop body contains both external call and state write (subsequent iterations would violate CEI)" + else + -- Include external calls from the loop count expression, so + -- `forEach i (externalCall ...) do setStorage ...` is correctly flagged + let countSeenCall := seenCall || exprMayContainExternalCall count + match stmtListCEIViolation body countSeenCall with + | some msg => some s!"in loop body: {msg}" + | none => none + | Stmt.unsafeBlock _ body, seenCall => + match stmtListCEIViolation body seenCall with + | some msg => some s!"in unsafe block: {msg}" + | none => none + | Stmt.matchAdt _ scrutinee branches, seenCall => + -- Include external calls from the scrutinee expression, so + -- `match adtTag (externalCall ...) { ... setStorage ... }` is correctly flagged + let scrutineeSeenCall := seenCall || exprMayContainExternalCall scrutinee + matchBranchesCEIViolation branches scrutineeSeenCall + | _, _ => none +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def matchBranchesCEIViolation : List (String × List String × List Stmt) → Bool → Option String + | [], _ => none + | (variantName, _, body) :: rest, seenCall => + match stmtListCEIViolation body seenCall with + | some msg => some s!"in match branch '{variantName}': {msg}" + | none => matchBranchesCEIViolation rest seenCall +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +def exprContainsAdtConstruct : Expr → Bool + | Expr.adtConstruct _ _ _ => true + | Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b + | Expr.mod a b | Expr.smod a b + | Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b + | Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b + | Expr.ge a b | Expr.le a b | Expr.signextend a b + | Expr.logicalAnd a b | Expr.logicalOr a b + | Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b + | Expr.ceilDiv a b => + exprContainsAdtConstruct a || exprContainsAdtConstruct b + | Expr.mulDivDown a b c | Expr.mulDivUp a b c => + exprContainsAdtConstruct a || exprContainsAdtConstruct b || exprContainsAdtConstruct c + | Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a + | Expr.mload a | Expr.tload a | Expr.calldataload a + | Expr.returndataOptionalBoolAt a + | Expr.storageArrayElement _ a | Expr.arrayElement _ a => + exprContainsAdtConstruct a + | Expr.ite cond thenVal elseVal => + exprContainsAdtConstruct cond || exprContainsAdtConstruct thenVal || + exprContainsAdtConstruct elseVal + | Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ + | Expr.mappingUint _ key | Expr.structMember _ key _ => + exprContainsAdtConstruct key + | Expr.mappingChain _ keys => + exprListContainsAdtConstruct keys + | Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _ + | Expr.structMember2 _ key1 key2 _ => + exprContainsAdtConstruct key1 || exprContainsAdtConstruct key2 + | Expr.keccak256 offset size => + exprContainsAdtConstruct offset || exprContainsAdtConstruct size + | Expr.call gas target value inOffset inSize outOffset outSize => + exprContainsAdtConstruct gas || exprContainsAdtConstruct target || + exprContainsAdtConstruct value || exprContainsAdtConstruct inOffset || + exprContainsAdtConstruct inSize || exprContainsAdtConstruct outOffset || + exprContainsAdtConstruct outSize + | Expr.staticcall gas target inOffset inSize outOffset outSize => + exprContainsAdtConstruct gas || exprContainsAdtConstruct target || + exprContainsAdtConstruct inOffset || exprContainsAdtConstruct inSize || + exprContainsAdtConstruct outOffset || exprContainsAdtConstruct outSize + | Expr.delegatecall gas target inOffset inSize outOffset outSize => + exprContainsAdtConstruct gas || exprContainsAdtConstruct target || + exprContainsAdtConstruct inOffset || exprContainsAdtConstruct inSize || + exprContainsAdtConstruct outOffset || exprContainsAdtConstruct outSize + | Expr.externalCall _ args | Expr.internalCall _ args => + exprListContainsAdtConstruct args + | Expr.dynamicBytesEq _ _ | Expr.literal _ | Expr.param _ | Expr.constructorArg _ + | Expr.storage _ | Expr.storageAddr _ | Expr.caller | Expr.contractAddress + | Expr.chainid | Expr.msgValue | Expr.blockTimestamp | Expr.blockNumber + | Expr.blobbasefee | Expr.calldatasize | Expr.returndataSize + | Expr.localVar _ + | Expr.arrayLength _ | Expr.storageArrayLength _ + | Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ => + false +termination_by e => sizeOf e +decreasing_by all_goals simp_wf; all_goals omega + +def exprListContainsAdtConstruct : List Expr → Bool + | [] => false + | expr :: rest => exprContainsAdtConstruct expr || exprListContainsAdtConstruct rest +termination_by es => sizeOf es +decreasing_by all_goals simp_wf; all_goals omega +end + +mutual +def validateNoUnsupportedAdtConstructInStmt : Stmt → Except String Unit + | Stmt.setStorage _ (Expr.adtConstruct _ _ args) => + if exprListContainsAdtConstruct args then + throw "Compilation error: ADT construction arguments cannot themselves contain ADT construction; construct nested ADTs in storage explicitly." + else + pure () + | Stmt.letVar _ value | Stmt.assignVar _ value | Stmt.setStorage _ value + | Stmt.setStorageAddr _ value | Stmt.storageArrayPush _ value + | Stmt.setStorageArrayElement _ _ value | Stmt.setMapping _ _ value + | Stmt.setMappingUint _ _ value | Stmt.setMappingWord _ _ _ value + | Stmt.setMapping2 _ _ _ value | Stmt.setMapping2Word _ _ _ _ value + | Stmt.setMappingPackedWord _ _ _ _ value + | Stmt.setMappingChain _ _ value | Stmt.setStructMember _ _ _ value + | Stmt.setStructMember2 _ _ _ _ value | Stmt.require value _ + | Stmt.return value => + if exprContainsAdtConstruct value then + throw "Compilation error: ADT construction is only supported as the direct value of setStorage for ADT storage fields; expression-position ADT values are not scalar Yul expressions." + else + pure () + | Stmt.requireError cond _ args => + if exprContainsAdtConstruct cond || exprListContainsAdtConstruct args then + throw "Compilation error: ADT construction is only supported as the direct value of setStorage for ADT storage fields; expression-position ADT values are not scalar Yul expressions." + else + pure () + | Stmt.revertError _ args | Stmt.returnValues args | Stmt.emit _ args => + if exprListContainsAdtConstruct args then + throw "Compilation error: ADT construction is only supported as the direct value of setStorage for ADT storage fields; expression-position ADT values are not scalar Yul expressions." + else + pure () + | Stmt.rawLog topics dataOffset dataSize => + if exprListContainsAdtConstruct topics || exprContainsAdtConstruct dataOffset || + exprContainsAdtConstruct dataSize then + throw "Compilation error: ADT construction is only supported as the direct value of setStorage for ADT storage fields; expression-position ADT values are not scalar Yul expressions." + else + pure () + | Stmt.ite cond thenBranch elseBranch => do + if exprContainsAdtConstruct cond then + throw "Compilation error: ADT construction cannot be used as an if condition." + validateNoUnsupportedAdtConstructInStmtList thenBranch + validateNoUnsupportedAdtConstructInStmtList elseBranch + | Stmt.forEach _ count body => do + if exprContainsAdtConstruct count then + throw "Compilation error: ADT construction cannot be used as a loop bound." + validateNoUnsupportedAdtConstructInStmtList body + | Stmt.unsafeBlock _ body => + validateNoUnsupportedAdtConstructInStmtList body + | Stmt.matchAdt _ scrutinee branches => do + if exprContainsAdtConstruct scrutinee then + throw "Compilation error: ADT construction cannot be used as a match scrutinee; match storage-backed ADT tags instead." + validateNoUnsupportedAdtConstructInBranches branches + | Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args + | Stmt.externalCallBind _ _ args | Stmt.tryExternalCallBind _ _ _ args + | Stmt.ecm _ args => + if exprListContainsAdtConstruct args then + throw "Compilation error: ADT construction cannot be passed as a call argument; ABI/function boundary ADT lowering is not implemented." + else + pure () + | Stmt.mstore offset value | Stmt.tstore offset value => do + if exprContainsAdtConstruct offset || exprContainsAdtConstruct value then + throw "Compilation error: ADT construction cannot be used in raw memory/transient-storage operations." + | Stmt.calldatacopy destOffset sourceOffset size + | Stmt.returndataCopy destOffset sourceOffset size => do + if exprContainsAdtConstruct destOffset || exprContainsAdtConstruct sourceOffset || + exprContainsAdtConstruct size then + throw "Compilation error: ADT construction cannot be used in copy offsets or sizes." + | Stmt.storageArrayPop _ | Stmt.returnArray _ | Stmt.returnBytes _ + | Stmt.returnStorageWords _ | Stmt.revertReturndata | Stmt.stop => + pure () +termination_by s => sizeOf s +decreasing_by all_goals simp_wf; all_goals omega + +def validateNoUnsupportedAdtConstructInStmtList : List Stmt → Except String Unit + | [] => pure () + | stmt :: rest => do + validateNoUnsupportedAdtConstructInStmt stmt + validateNoUnsupportedAdtConstructInStmtList rest +termination_by ss => sizeOf ss +decreasing_by all_goals simp_wf; all_goals omega + +def validateNoUnsupportedAdtConstructInBranches : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateNoUnsupportedAdtConstructInStmtList body + validateNoUnsupportedAdtConstructInBranches rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateFunctionSpec (spec : FunctionSpec) : Except String Unit := do - let unsafeBoundaryMechanics := collectUnsafeBoundaryMechanicsFromStmts spec.body - if !unsafeBoundaryMechanics.isEmpty && spec.localObligations.isEmpty then - throw s!"Compilation error: function '{spec.name}' uses low-level/assembly mechanic(s) {String.intercalate ", " unsafeBoundaryMechanics} without any local_obligations entry ({issue1424Ref}). Add local_obligations [...] to make the trust boundary explicit." + -- Check for unsafe boundary mechanics outside `unsafe "reason" do` blocks. + -- Mechanics inside `unsafe` blocks are documented by the reason string and + -- do not independently require `local_obligations` (#1728, Phase 6 Step 6b). + let unguardedMechanics := collectUnguardedUnsafeBoundaryMechanicsFromStmts spec.body + if !unguardedMechanics.isEmpty && spec.localObligations.isEmpty then + throw s!"Compilation error: function '{spec.name}' uses low-level/assembly mechanic(s) {String.intercalate ", " unguardedMechanics} outside an unsafe block without any local_obligations entry ({issue1424Ref}). Wrap the low-level code in `unsafe \"reason\" do` or add local_obligations [...] to make the trust boundary explicit." if spec.isPayable && (spec.isView || spec.isPure) then throw s!"Compilation error: function '{spec.name}' cannot be both payable and view/pure ({issue586Ref})" if spec.isView && spec.isPure then @@ -411,11 +1240,38 @@ def validateFunctionSpec (spec : FunctionSpec) : Except String Unit := do throw s!"Compilation error: function '{spec.name}' is marked pure but reads state/environment ({issue734Ref})" if spec.body.any stmtContainsUnsafeLogicalCallLike then throw s!"Compilation error: function '{spec.name}' uses Expr.logicalAnd/Expr.logicalOr/Expr.ite or arithmetic helpers (mulDivUp/wDivUp/min/max) with call-like operand(s) that would be duplicated in Yul output ({issue748Ref}). Move call-like expressions into Stmt.letVar before combining." + validateAdtPayloadParamNameCollisions s!"function '{spec.name}'" spec.params spec.body + validateNoUnsupportedAdtConstructInStmtList spec.body let returns ← functionReturns spec spec.body.forM (validateReturnShapesInStmt spec.name spec.params returns spec.isInternal) if !returns.isEmpty && !stmtListAlwaysReturnsOrReverts spec.body then throw s!"Compilation error: function '{spec.name}' declares return values but not all control-flow paths end in return/revert ({issue738Ref})" spec.body.forM (validateStmtParamReferences spec.name spec.params) + -- Validate modifies annotation: if declared, every written field must be in the set + if !spec.modifies.isEmpty then + -- Reject modifies() when the body contains calls whose write sets cannot be + -- statically tracked (internal calls, external calls, ECM invocations). + if stmtListHasUntrackableWrites spec.body then + throw s!"Compilation error: function '{spec.name}' is annotated modifies({String.intercalate ", " spec.modifies}) but contains internal call statements whose write sets cannot be verified statically. Remove the modifies annotation or inline the called logic." + let writtenFields := (stmtListWrittenFields spec.body).eraseDups + for field in writtenFields do + if !spec.modifies.contains field then + throw s!"Compilation error: function '{spec.name}' is annotated modifies({String.intercalate ", " spec.modifies}) but writes to undeclared field '{field}'" + -- Validate no_external_calls annotation: reject external call statements. + -- Uses the conservative `stmtMayContainExternalCall` which also flags internal calls + -- (since callee bodies may contain external calls not visible at this scope). + if spec.noExternalCalls && spec.body.any stmtMayContainExternalCall then + throw s!"Compilation error: function '{spec.name}' is annotated no_external_calls but contains statements that may perform external calls (including internal function calls whose bodies cannot be verified here)" + -- CEI enforcement: reject state writes after external calls unless the function + -- explicitly records a trust-surface opt-out. `nonreentrant(field)` and + -- `cei_safe` are metadata/proof hooks in the current pipeline; until they + -- synthesize a real guard or check a proof term, they must not suppress CEI. + let ceiExempt := spec.allowPostInteractionWrites + if !ceiExempt then + match stmtListCEIViolation spec.body false with + | some violation => + throw s!"Compilation error: function '{spec.name}' violates CEI (Checks-Effects-Interactions) ordering: {violation}. Reorder state writes before external calls, or annotate with allow_post_interaction_writes to opt out ({issue1728Ref})" + | none => pure () validateFunctionIdentifierReferences spec mutual @@ -428,6 +1284,10 @@ def validateNoRuntimeReturnsInConstructorStmt : Stmt → Except String Unit validateNoRuntimeReturnsInConstructorStmtList elseBranch | Stmt.forEach _ _ body => validateNoRuntimeReturnsInConstructorStmtList body + | Stmt.unsafeBlock _ body => + validateNoRuntimeReturnsInConstructorStmtList body + | Stmt.matchAdt _ _ branches => + validateNoRuntimeReturnsInConstructorBranches branches | _ => pure () termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -439,17 +1299,28 @@ def validateNoRuntimeReturnsInConstructorStmtList : List Stmt → Except String validateNoRuntimeReturnsInConstructorStmtList ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateNoRuntimeReturnsInConstructorBranches : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateNoRuntimeReturnsInConstructorStmtList body + validateNoRuntimeReturnsInConstructorBranches rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateConstructorSpec (ctor : Option ConstructorSpec) : Except String Unit := do match ctor with | none => pure () | some spec => - let unsafeBoundaryMechanics := collectUnsafeBoundaryMechanicsFromStmts spec.body - if !unsafeBoundaryMechanics.isEmpty && spec.localObligations.isEmpty then - throw s!"Compilation error: constructor uses low-level/assembly mechanic(s) {String.intercalate ", " unsafeBoundaryMechanics} without any local_obligations entry ({issue1424Ref}). Add local_obligations [...] to make the trust boundary explicit." + let unguardedMechanics := collectUnguardedUnsafeBoundaryMechanicsFromStmts spec.body + if !unguardedMechanics.isEmpty && spec.localObligations.isEmpty then + throw s!"Compilation error: constructor uses low-level/assembly mechanic(s) {String.intercalate ", " unguardedMechanics} outside an unsafe block without any local_obligations entry ({issue1424Ref}). Wrap the low-level code in `unsafe \"reason\" do` or add local_obligations [...] to make the trust boundary explicit." if spec.body.any stmtContainsUnsafeLogicalCallLike then throw s!"Compilation error: constructor uses Expr.logicalAnd/Expr.logicalOr/Expr.ite or arithmetic helpers (mulDivUp/wDivUp/min/max) with call-like operand(s) that would be duplicated in Yul output ({issue748Ref}). Move call-like expressions into Stmt.letVar before combining." + validateAdtPayloadParamNameCollisions "constructor" spec.params spec.body + validateNoUnsupportedAdtConstructInStmtList spec.body spec.body.forM validateNoRuntimeReturnsInConstructorStmt spec.body.forM (validateStmtParamReferences "constructor" spec.params) validateConstructorIdentifierReferences ctor diff --git a/Compiler/CompilationModel/ValidationCalls.lean b/Compiler/CompilationModel/ValidationCalls.lean index 88026d27e..bba70e368 100644 --- a/Compiler/CompilationModel/ValidationCalls.lean +++ b/Compiler/CompilationModel/ValidationCalls.lean @@ -209,6 +209,11 @@ def validateInternalCallShapesInStmt | Stmt.forEach _ count body => do validateInternalCallShapesInExpr functions callerName count validateInternalCallShapesInStmtList functions callerName body + | Stmt.unsafeBlock _ body => + validateInternalCallShapesInStmtList functions callerName body + | Stmt.matchAdt _ scrutinee branches => do + validateInternalCallShapesInExpr functions callerName scrutinee + validateInternalCallShapesInMatchBranches functions callerName branches | Stmt.emit _ args => validateInternalCallShapesInExprList functions callerName args | Stmt.returnValues values => @@ -249,6 +254,8 @@ def validateInternalCallShapesInStmt validateInternalCallShapesInExpr functions callerName dataSize | Stmt.externalCallBind _resultVars _ args => validateInternalCallShapesInExprList functions callerName args + | Stmt.tryExternalCallBind _ _resultVars _ args => + validateInternalCallShapesInExprList functions callerName args | Stmt.ecm _ args => validateInternalCallShapesInExprList functions callerName args | _ => @@ -264,6 +271,16 @@ def validateInternalCallShapesInStmtList validateInternalCallShapesInStmtList functions callerName ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateInternalCallShapesInMatchBranches + (functions : List FunctionSpec) (callerName : String) : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateInternalCallShapesInStmtList functions callerName body + validateInternalCallShapesInMatchBranches functions callerName rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateInternalCallShapesInFunction (functions : List FunctionSpec) @@ -422,6 +439,11 @@ def validateExternalCallTargetsInStmt | Stmt.forEach _ count body => do validateExternalCallTargetsInExpr externals context count validateExternalCallTargetsInStmtList externals context body + | Stmt.unsafeBlock _ body => + validateExternalCallTargetsInStmtList externals context body + | Stmt.matchAdt _ scrutinee branches => do + validateExternalCallTargetsInExpr externals context scrutinee + validateExternalCallTargetsInMatchBranches externals context branches | Stmt.emit _ args => validateExternalCallTargetsInExprList externals context args | Stmt.internalCall _ args => @@ -447,6 +469,37 @@ def validateExternalCallTargetsInStmt else checkDuplicateVars (name :: seen) rest checkDuplicateVars [] resultVars + | Stmt.tryExternalCallBind successVar resultVars externalName args => do + validateExternalCallTargetsInExprList externals context args + match externals.find? (fun ext => ext.name == externalName) with + | none => + throw s!"Compilation error: {context} uses Stmt.tryExternalCallBind with unknown external function '{externalName}'." + | some ext => do + if args.length != ext.params.length then + throw s!"Compilation error: {context} calls external function '{externalName}' with {args.length} args, expected {ext.params.length}." + let returns ← externalFunctionReturns ext + if returns.length != resultVars.length then + throw s!"Compilation error: {context} binds {resultVars.length} values from external function '{externalName}', but it returns {returns.length}." + let tryName := s!"{externalName}_try" + match externals.find? (fun candidate => candidate.name == tryName) with + | none => + throw s!"Compilation error: {context} uses Stmt.tryExternalCallBind for external function '{externalName}', but required linked wrapper '{tryName}' is not declared." + | some tryExt => do + if tryExt.params != ext.params then + throw s!"Compilation error: try wrapper '{tryName}' must take the same parameters as external function '{externalName}'." + let tryReturns ← externalFunctionReturns tryExt + let expectedTryReturns := ParamType.bool :: returns + if tryReturns != expectedTryReturns then + throw s!"Compilation error: try wrapper '{tryName}' must return Bool followed by the return values of external function '{externalName}'." + let allVars := successVar :: resultVars + let rec checkDuplicateTryVars (seen : List String) : List String → Except String Unit + | [] => pure () + | name :: rest => + if seen.contains name then + throw s!"Compilation error: {context} uses Stmt.tryExternalCallBind with duplicate result variable '{name}'." + else + checkDuplicateTryVars (name :: seen) rest + checkDuplicateTryVars [] allVars | Stmt.returnValues values => validateExternalCallTargetsInExprList externals context values | Stmt.rawLog topics dataOffset dataSize => do @@ -468,6 +521,16 @@ def validateExternalCallTargetsInStmtList validateExternalCallTargetsInStmtList externals context ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateExternalCallTargetsInMatchBranches + (externals : List ExternalFunction) (context : String) : + List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateExternalCallTargetsInStmtList externals context body + validateExternalCallTargetsInMatchBranches externals context rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateExternalCallTargetsInFunction @@ -487,6 +550,8 @@ def supportedCustomErrorParamType : ParamType → Bool | ParamType.array elemTy => supportedCustomErrorParamType elemTy | ParamType.fixedArray elemTy _ => supportedCustomErrorParamType elemTy | ParamType.tuple elemTys => supportedCustomErrorParamTypes elemTys + | ParamType.adt _ _ => true + | ParamType.newtypeOf _ baseType => supportedCustomErrorParamType baseType termination_by ty => sizeOf ty decreasing_by all_goals simp_wf diff --git a/Compiler/CompilationModel/ValidationEvents.lean b/Compiler/CompilationModel/ValidationEvents.lean index 40b2aeabe..a09aa8a14 100644 --- a/Compiler/CompilationModel/ValidationEvents.lean +++ b/Compiler/CompilationModel/ValidationEvents.lean @@ -45,6 +45,10 @@ def validateCustomErrorArgShapesInStmt (fnName : String) (params : List Param) validateCustomErrorArgShapesInStmtList fnName params errors elseBranch | Stmt.forEach _ _ body => validateCustomErrorArgShapesInStmtList fnName params errors body + | Stmt.unsafeBlock _ body => + validateCustomErrorArgShapesInStmtList fnName params errors body + | Stmt.matchAdt _ _ branches => + validateCustomErrorArgShapesInMatchBranches fnName params errors branches | _ => pure () termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega @@ -57,6 +61,15 @@ def validateCustomErrorArgShapesInStmtList (fnName : String) (params : List Para validateCustomErrorArgShapesInStmtList fnName params errors ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateCustomErrorArgShapesInMatchBranches (fnName : String) (params : List Param) + (errors : List ErrorDef) : List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateCustomErrorArgShapesInStmtList fnName params errors body + validateCustomErrorArgShapesInMatchBranches fnName params errors rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateCustomErrorArgShapesInFunction (spec : FunctionSpec) (errors : List ErrorDef) : @@ -197,6 +210,11 @@ partial def validateEventArgShapesInStmt (fnName : String) (params : List Param) elseBranch.forM (validateEventArgShapesInStmt fnName params events) | Stmt.forEach _ _ body => body.forM (validateEventArgShapesInStmt fnName params events) + | Stmt.unsafeBlock _ body => + body.forM (validateEventArgShapesInStmt fnName params events) + | Stmt.matchAdt _ _ branches => do + for (_, _, body) in branches do + body.forM (validateEventArgShapesInStmt fnName params events) | _ => pure () def validateEventArgShapesInFunction (spec : FunctionSpec) (events : List EventDef) : diff --git a/Compiler/CompilationModel/ValidationHelpers.lean b/Compiler/CompilationModel/ValidationHelpers.lean index 13b2dff34..b2c936a18 100644 --- a/Compiler/CompilationModel/ValidationHelpers.lean +++ b/Compiler/CompilationModel/ValidationHelpers.lean @@ -114,6 +114,9 @@ def collectExprNames : Expr → List String | Expr.min a b => collectExprNames a ++ collectExprNames b | Expr.max a b => collectExprNames a ++ collectExprNames b | Expr.ite cond thenVal elseVal => collectExprNames cond ++ collectExprNames thenVal ++ collectExprNames elseVal + | Expr.adtConstruct _ _ args => collectExprListNames args + | Expr.adtTag _ field => [field] + | Expr.adtField _ _ _ _ _ => [] termination_by expr => sizeOf expr decreasing_by all_goals simp_wf @@ -170,6 +173,10 @@ def collectStmtNames : Stmt → List String collectExprNames cond ++ collectStmtListNames thenBranch ++ collectStmtListNames elseBranch | Stmt.forEach varName count body => varName :: collectExprNames count ++ collectStmtListNames body + | Stmt.unsafeBlock _ body => + collectStmtListNames body + | Stmt.matchAdt _ scrutinee branches => + collectExprNames scrutinee ++ collectMatchBranchNames branches | Stmt.emit eventName args => eventName :: collectExprListNames args | Stmt.internalCall functionName args => functionName :: collectExprListNames args | Stmt.internalCallAssign names functionName args => @@ -178,6 +185,8 @@ def collectStmtNames : Stmt → List String collectExprListNames topics ++ collectExprNames dataOffset ++ collectExprNames dataSize | Stmt.externalCallBind resultVars externalName args => resultVars ++ externalName :: collectExprListNames args + | Stmt.tryExternalCallBind successVar resultVars externalName args => + successVar :: resultVars ++ externalName :: collectExprListNames args | Stmt.ecm mod args => mod.resultVars ++ collectExprListNames args termination_by stmt => sizeOf stmt @@ -185,6 +194,15 @@ decreasing_by all_goals simp_wf all_goals omega +def collectMatchBranchNames : List (String × List String × List Stmt) → List String + | [] => [] + | (_, varNames, body) :: rest => + varNames ++ collectStmtListNames body ++ collectMatchBranchNames rest +termination_by bs => sizeOf bs +decreasing_by + all_goals simp_wf + all_goals omega + def collectStmtListNames : List Stmt → List String | [] => [] | stmt :: rest => collectStmtNames stmt ++ collectStmtListNames rest diff --git a/Compiler/CompilationModel/ValidationInterop.lean b/Compiler/CompilationModel/ValidationInterop.lean index 083c0f739..6a8c1cd9e 100644 --- a/Compiler/CompilationModel/ValidationInterop.lean +++ b/Compiler/CompilationModel/ValidationInterop.lean @@ -167,6 +167,11 @@ def validateInteropStmt (context : String) : Stmt → Except String Unit | Stmt.forEach _ count body => do validateInteropExpr context count validateInteropStmtList context body + | Stmt.unsafeBlock _ body => + validateInteropStmtList context body + | Stmt.matchAdt _ scrutinee branches => do + validateInteropExpr context scrutinee + validateInteropMatchBranches context branches | Stmt.emit _ args => validateInteropExprList context args | Stmt.internalCall _ args => @@ -175,6 +180,8 @@ def validateInteropStmt (context : String) : Stmt → Except String Unit validateInteropExprList context args | Stmt.externalCallBind _ _ args => validateInteropExprList context args + | Stmt.tryExternalCallBind _ _ _ args => + validateInteropExprList context args | Stmt.returnValues values => validateInteropExprList context values | Stmt.rawLog topics dataOffset dataSize => do @@ -195,6 +202,14 @@ def validateInteropStmtList (context : String) : List Stmt → Except String Uni validateInteropStmtList context ss termination_by ss => sizeOf ss decreasing_by all_goals simp_wf; all_goals omega + +def validateInteropMatchBranches (context : String) : List (String × List String × List Stmt) → Except String Unit + | [] => pure () + | (_, _, body) :: rest => do + validateInteropStmtList context body + validateInteropMatchBranches context rest +termination_by bs => sizeOf bs +decreasing_by all_goals simp_wf; all_goals omega end def validateInteropFunctionSpec (spec : FunctionSpec) : Except String Unit := do diff --git a/Compiler/CompilationModelFeatureTest.lean b/Compiler/CompilationModelFeatureTest.lean index f2680da60..e83157aa9 100644 --- a/Compiler/CompilationModelFeatureTest.lean +++ b/Compiler/CompilationModelFeatureTest.lean @@ -233,7 +233,7 @@ verity_contract MacroExternal where linked_externals external echo(Uint256) -> (Uint256) - function storeEcho (next : Uint256) : Unit := do + function allow_post_interaction_writes storeEcho (next : Uint256) : Unit := do let echoed := externalCall "echo" [next] setStorage echoedValue echoed @@ -2050,6 +2050,100 @@ private def stringEventMismatchSpec : CompilationModel := { ] } +private def eventEncodingRegressionSpec : CompilationModel := { + name := "EventEncodingRegression" + fields := [] + «constructor» := none + functions := [ + { name := "log" + params := [ + { name := "choice", ty := ParamType.adt "Choice" 2 }, + { name := "who", ty := ParamType.newtypeOf "SafeAddress" ParamType.address } + ] + returnType := none + body := [ + Stmt.emit "ChoiceStored" [Expr.param "choice"], + Stmt.emit "ChoiceIndexed" [Expr.param "choice"], + Stmt.emit "WhoIndexed" [Expr.param "who"], + Stmt.stop + ] + } + ] + events := [ + { name := "ChoiceStored" + params := [{ name := "choice", ty := ParamType.adt "Choice" 2, kind := EventParamKind.unindexed }] + }, + { name := "ChoiceIndexed" + params := [{ name := "choice", ty := ParamType.adt "Choice" 2, kind := EventParamKind.indexed }] + }, + { name := "WhoIndexed" + params := [{ name := "who", ty := ParamType.newtypeOf "SafeAddress" ParamType.address, kind := EventParamKind.indexed }] + } + ] + adtTypes := [ + { name := "Choice" + variants := [ + { name := "None", tag := 0, fields := [] }, + { name := "Some", tag := 1, fields := [ + { name := "amount", ty := ParamType.uint256 }, + { name := "recipient", ty := ParamType.address } + ] } + ] + } + ] +} + +private def adtParamPayloadNameCollisionSpec : CompilationModel := { + name := "AdtParamPayloadNameCollision" + fields := [] + «constructor» := none + functions := [ + { name := "store" + params := [ + { name := "choice", ty := ParamType.adt "Choice" 1 }, + { name := "choice_f0", ty := ParamType.uint256 } + ] + returnType := none + body := [Stmt.stop] + } + ] + adtTypes := [ + { name := "Choice" + variants := [ + { name := "None", tag := 0, fields := [] }, + { name := "Some", tag := 1, fields := [{ name := "amount", ty := ParamType.uint256 }] } + ] + } + ] +} + +private def adtAliasPayloadReservedSlotSpec : CompilationModel := { + name := "AdtAliasPayloadReservedSlot" + fields := [ + { name := "choice", ty := FieldType.adt "Choice" 2, «slot» := some 10, aliasSlots := [100] } + ] + reservedSlotRanges := [{ start := 101, end_ := 101 }] + «constructor» := none + functions := [ + { name := "noop" + params := [] + returnType := none + body := [Stmt.stop] + } + ] + adtTypes := [ + { name := "Choice" + variants := [ + { name := "None", tag := 0, fields := [] }, + { name := "Some", tag := 1, fields := [ + { name := "amount", ty := ParamType.uint256 }, + { name := "recipient", ty := ParamType.address } + ] } + ] + } + ] +} + private def addressArrayReturnSpec : CompilationModel := { name := "AddressArrayReturn" fields := [] @@ -2801,6 +2895,24 @@ set_option maxRecDepth 4096 in (contains stringArrayErrorAbi "\"inputs\": [{\"name\": \"\", \"type\": \"uint256\"}, {\"name\": \"\", \"type\": \"string[]\"}]") && (contains stringArrayErrorAbi "\"name\": \"SecondMessages\"") && (contains stringArrayErrorAbi "\"inputs\": [{\"name\": \"\", \"type\": \"string[]\"}, {\"name\": \"\", \"type\": \"string[]\"}]")) + let eventEncodingRegressionYul ← expectCompileToYul + "ADT and newtype event encoding regression spec compiles" + eventEncodingRegressionSpec + expectTrue "ADT event encoding stores payload fields before logging" + ((contains eventEncodingRegressionYul "choice_f0") && + (contains eventEncodingRegressionYul "choice_f1") && + (contains eventEncodingRegressionYul "keccak256(__evt_ptr, 96)")) + expectTrue "newtype event topics normalize through the erased base type" + (contains eventEncodingRegressionYul + "and(who, 0xffffffffffffffffffffffffffffffffffffffff)") + expectCompileErrorContains + "ADT payload parameter locals are reserved against parameter collisions" + adtParamPayloadNameCollisionSpec + "function parameter binding name 'choice_f0' collides" + expectCompileErrorContains + "ADT alias payload slots are checked against reserved slot ranges" + adtAliasPayloadReservedSlotSpec + "choice.aliasSlots[0].payload[0]" let addressArrayReturnCompiled := match Compiler.CompilationModel.compile addressArrayReturnSpec (selectorsFor addressArrayReturnSpec) with | .ok _ => true diff --git a/Compiler/CompileDriver.lean b/Compiler/CompileDriver.lean index 3715e20c4..3b0eabb9f 100644 --- a/Compiler/CompileDriver.lean +++ b/Compiler/CompileDriver.lean @@ -38,12 +38,13 @@ def compileSpecsWithOptions (denyProxyUpgradeability : Bool := false) (layoutReportPath : Option String := none) (layoutCompatibilityReportPath : Option String := none) - (denyLayoutIncompatibility : Bool := false) : IO Unit := + (denyLayoutIncompatibility : Bool := false) + (denyUnsafe : Bool := false) : IO Unit := Compiler.CompileDriverCommon.compileSpecsWithOptions backend specs outDir verbose libraryPaths options patchReportPath trustReportPath assumptionReportPath abiOutDir denyUncheckedDependencies denyAssumedDependencies denyAxiomatizedPrimitives denyLocalObligations denyLinearMemoryMechanics denyEventEmission denyLowLevelMechanics denyRuntimeIntrospection denyProxyUpgradeability layoutReportPath - layoutCompatibilityReportPath denyLayoutIncompatibility + layoutCompatibilityReportPath denyLayoutIncompatibility denyUnsafe unsafe def compileModulesWithOptions (outDir : String) @@ -66,11 +67,12 @@ unsafe def compileModulesWithOptions (denyProxyUpgradeability : Bool := false) (layoutReportPath : Option String := none) (layoutCompatibilityReportPath : Option String := none) - (denyLayoutIncompatibility : Bool := false) : IO Unit := do + (denyLayoutIncompatibility : Bool := false) + (denyUnsafe : Bool := false) : IO Unit := do Compiler.CompileDriverCommon.compileModulesWithOptions backend outDir modules verbose libraryPaths options patchReportPath trustReportPath assumptionReportPath abiOutDir denyUncheckedDependencies denyAssumedDependencies denyAxiomatizedPrimitives denyLocalObligations denyLinearMemoryMechanics denyEventEmission denyLowLevelMechanics denyRuntimeIntrospection - denyProxyUpgradeability layoutReportPath layoutCompatibilityReportPath denyLayoutIncompatibility + denyProxyUpgradeability layoutReportPath layoutCompatibilityReportPath denyLayoutIncompatibility denyUnsafe end Compiler diff --git a/Compiler/CompileDriverBase.lean b/Compiler/CompileDriverBase.lean index 584a9aa7c..54c837d5e 100644 --- a/Compiler/CompileDriverBase.lean +++ b/Compiler/CompileDriverBase.lean @@ -38,12 +38,13 @@ def compileSpecsWithOptions (denyProxyUpgradeability : Bool := false) (layoutReportPath : Option String := none) (layoutCompatibilityReportPath : Option String := none) - (denyLayoutIncompatibility : Bool := false) : IO Unit := + (denyLayoutIncompatibility : Bool := false) + (denyUnsafe : Bool := false) : IO Unit := Compiler.CompileDriverCommon.compileSpecsWithOptions backend specs outDir verbose libraryPaths options patchReportPath trustReportPath assumptionReportPath abiOutDir denyUncheckedDependencies denyAssumedDependencies denyAxiomatizedPrimitives denyLocalObligations denyLinearMemoryMechanics denyEventEmission denyLowLevelMechanics denyRuntimeIntrospection denyProxyUpgradeability layoutReportPath - layoutCompatibilityReportPath denyLayoutIncompatibility + layoutCompatibilityReportPath denyLayoutIncompatibility denyUnsafe unsafe def compileModulesWithOptions (outDir : String) @@ -66,11 +67,12 @@ unsafe def compileModulesWithOptions (denyProxyUpgradeability : Bool := false) (layoutReportPath : Option String := none) (layoutCompatibilityReportPath : Option String := none) - (denyLayoutIncompatibility : Bool := false) : IO Unit := do + (denyLayoutIncompatibility : Bool := false) + (denyUnsafe : Bool := false) : IO Unit := do Compiler.CompileDriverCommon.compileModulesWithOptions backend outDir modules verbose libraryPaths options patchReportPath trustReportPath assumptionReportPath abiOutDir denyUncheckedDependencies denyAssumedDependencies denyAxiomatizedPrimitives denyLocalObligations denyLinearMemoryMechanics denyEventEmission denyLowLevelMechanics denyRuntimeIntrospection - denyProxyUpgradeability layoutReportPath layoutCompatibilityReportPath denyLayoutIncompatibility + denyProxyUpgradeability layoutReportPath layoutCompatibilityReportPath denyLayoutIncompatibility denyUnsafe end Compiler.Base diff --git a/Compiler/CompileDriverCommon.lean b/Compiler/CompileDriverCommon.lean index 11624cfeb..3738dffb3 100644 --- a/Compiler/CompileDriverCommon.lean +++ b/Compiler/CompileDriverCommon.lean @@ -142,6 +142,12 @@ private def ensureNoLowLevelMechanics (specs : List CompilationModel) : IO Unit throw (IO.userError s!"Low-level mechanics remain:\n{String.intercalate "\n" lowLevelSites}") +private def ensureNoUnsafeBlocks (specs : List CompilationModel) : IO Unit := do + let unsafeSites := emitUnsafeBlockUsageSiteLines specs + if !unsafeSites.isEmpty then + throw (IO.userError + s!"Unsafe blocks remain:\n{String.intercalate "\n" unsafeSites}") + private def ensureLayoutCompatible (specs : List CompilationModel) : IO Unit := do let (baseline, candidate) ← requireLayoutCompatibilityPair specs let incompatibilities := emitIncompatibleLayoutChangeLines baseline candidate @@ -205,7 +211,8 @@ def compileSpecsWithOptions (denyProxyUpgradeability : Bool := false) (layoutReportPath : Option String := none) (layoutCompatibilityReportPath : Option String := none) - (denyLayoutIncompatibility : Bool := false) : IO Unit := do + (denyLayoutIncompatibility : Bool := false) + (denyUnsafe : Bool := false) : IO Unit := do IO.FS.createDirAll outDir match abiOutDir with | some dir => IO.FS.createDirAll dir @@ -225,8 +232,10 @@ def compileSpecsWithOptions match abiOutDir with | some dir => Compiler.ABI.writeContractABIFile dir spec + Compiler.ABI.writeContractStorageLayoutFile dir spec if verbose then IO.println s!"✓ Wrote ABI {dir}/{spec.name}.abi.json" + IO.println s!"✓ Wrote storage layout {dir}/{spec.name}.storage.json" | none => pure () patchRows := (contract.name, patchReport) :: patchRows if verbose then @@ -283,6 +292,8 @@ def compileSpecsWithOptions ensureNoAssumedDependencies specs if denyUncheckedDependencies then ensureNoUncheckedDependencies specs + if denyUnsafe then + ensureNoUnsafeBlocks specs if verbose then IO.println "" IO.println "Linear memory mechanics report:" @@ -364,6 +375,19 @@ def compileSpecsWithOptions IO.println " (no local unsafe/refinement obligations declared)" IO.println " Proof boundary: local obligations isolate unsafe/assembly-shaped trust boundaries to one usage site and can later be discharged from `assumed`/`unchecked` to `proved`." IO.println "" + IO.println "Unsafe block report:" + let mut anyUnsafeBlocks := false + for spec in specs do + let unsafeReasons := collectUnsafeBlockReasons spec + if !unsafeReasons.isEmpty then + anyUnsafeBlocks := true + IO.println s!" {spec.name}:" + for reason in unsafeReasons do + IO.println s!" [unsafe] \"{reason}\"" + if !anyUnsafeBlocks then + IO.println " (no unsafe blocks used)" + IO.println " Trust boundary: each `unsafe \"reason\" do` block suppresses restricted-operation gating for its body. Use `--deny-unsafe` to reject all unsafe blocks." + IO.println "" IO.println "Proof-status summary:" let mut anyForeignStatus := false let mut anyUncheckedStatus := false @@ -502,7 +526,8 @@ unsafe def compileModulesWithOptions (denyProxyUpgradeability : Bool := false) (layoutReportPath : Option String := none) (layoutCompatibilityReportPath : Option String := none) - (denyLayoutIncompatibility : Bool := false) : IO Unit := do + (denyLayoutIncompatibility : Bool := false) + (denyUnsafe : Bool := false) : IO Unit := do let specs ← match ← Compiler.ModuleInput.loadSpecsFromRawModules modules with | .ok specs => pure specs @@ -511,6 +536,6 @@ unsafe def compileModulesWithOptions backend specs outDir verbose libraryPaths options patchReportPath trustReportPath assumptionReportPath abiOutDir denyUncheckedDependencies denyAssumedDependencies denyAxiomatizedPrimitives denyLocalObligations denyLinearMemoryMechanics denyEventEmission denyLowLevelMechanics denyRuntimeIntrospection denyProxyUpgradeability layoutReportPath - layoutCompatibilityReportPath denyLayoutIncompatibility + layoutCompatibilityReportPath denyLayoutIncompatibility denyUnsafe end Compiler.CompileDriverCommon diff --git a/Compiler/CompileDriverTest.lean b/Compiler/CompileDriverTest.lean index 60ead1541..80a1da1cd 100644 --- a/Compiler/CompileDriverTest.lean +++ b/Compiler/CompileDriverTest.lean @@ -255,6 +255,7 @@ private def linkedLibrarySpec : CompilationModel := { , { name := "b", ty := ParamType.uint256 } ] returnType := none + allowPostInteractionWrites := true body := [ Stmt.letVar "h" (Expr.externalCall "PoseidonT3_hash" [Expr.param "a", Expr.param "b"]), Stmt.setStorage "lastHash" (Expr.localVar "h"), @@ -537,6 +538,24 @@ private def localObligationTrustSurfaceSpec : CompilationModel := { ] } +private def unsafeBlockTrustSurfaceSpec : CompilationModel := { + name := "UnsafeBlockTrustSurface" + fields := [] + «constructor» := none + functions := [ + { name := "exerciseUnsafe" + params := [] + returnType := none + body := [ + Stmt.unsafeBlock "manual memory write for packed encoding" [ + Stmt.mstore (Expr.literal 0) (Expr.literal 1) + ], + Stmt.stop + ] + } + ] +} + private def ecrecoverTrustSurfaceSpec : CompilationModel := { name := "EcrecoverTrustSurface" fields := [] @@ -1678,6 +1697,42 @@ unsafe def runTests : IO Unit := do throw (IO.userError "✗ denied proxy-upgradeability compile still writes trust report file") IO.println "✓ denied proxy-upgradeability compile still writes trust report file" + -- deny-unsafe: reject contracts with unsafe blocks + let deniedUnsafeTrustReportPath := s!"{trustReportDir}/trust-report-denied-unsafe.json" + expectFailureContains + "compileSpecsWithOptions rejects unsafe blocks when deny flag enabled" + (compileSpecsWithOptions + [unsafeBlockTrustSurfaceSpec] outDir false [] {} none (some deniedUnsafeTrustReportPath) none none false false false false false false false false false none none false true) + "Unsafe blocks remain:\n- UnsafeBlockTrustSurface [function:exerciseUnsafe]: unsafe \"manual memory write for packed encoding\"" + let deniedUnsafeTrustReportWritten ← fileExists deniedUnsafeTrustReportPath + if !deniedUnsafeTrustReportWritten then + throw (IO.userError "✗ denied unsafe-block compile still writes trust report file") + IO.println "✓ denied unsafe-block compile still writes trust report file" + + -- deny-unsafe passes for contracts without unsafe blocks + let denyUnsafeOkOutDir := s!"/tmp/compile-driver-deny-unsafe-ok-{nonce}" + compileSpecsWithOptions + [abiSmokeSpec] denyUnsafeOkOutDir false [] {} none none none none false false false false false false false false false none none false true + let denyUnsafeOkArtifactWritten ← fileExists s!"{denyUnsafeOkOutDir}/AbiSmoke.yul" + if !denyUnsafeOkArtifactWritten then + throw (IO.userError "✗ compileSpecsWithOptions allows contracts without unsafe blocks under deny-unsafe gate") + IO.println "✓ compileSpecsWithOptions allows contracts without unsafe blocks under deny-unsafe gate" + + -- trust report JSON includes unsafeBlocks field + let unsafeTrustReportDir := s!"/tmp/compile-driver-unsafe-trust-report-{nonce}" + let unsafeTrustReportPath := s!"{unsafeTrustReportDir}/trust-report-unsafe.json" + IO.FS.createDirAll unsafeTrustReportDir + let unsafeOutDir := s!"/tmp/compile-driver-unsafe-out-{nonce}" + compileSpecsWithOptions + [unsafeBlockTrustSurfaceSpec] unsafeOutDir false [] {} none (some unsafeTrustReportPath) none none + let unsafeTrustReportWritten ← fileExists unsafeTrustReportPath + if !unsafeTrustReportWritten then + throw (IO.userError "✗ trust report file should exist for unsafe block spec") + expectFileContains + "trust report JSON includes unsafeBlocks for contracts with unsafe blocks" + unsafeTrustReportPath + ["\"unsafeBlocks\":[\"manual memory write for packed encoding\"]"] + compileSpecsWithOptions [abiSmokeSpec] outDir false [] { patchConfig := { enabled := true } } (some patchReportPath) none none none let writtenPatchReport ← fileExists patchReportPath if !writtenPatchReport then diff --git a/Compiler/MainDriver.lean b/Compiler/MainDriver.lean index b1d63b8c9..d07151d70 100644 --- a/Compiler/MainDriver.lean +++ b/Compiler/MainDriver.lean @@ -30,6 +30,7 @@ private structure CLIArgs where denyRuntimeIntrospection : Bool := false denyProxyUpgradeability : Bool := false denyLayoutIncompatibility : Bool := false + denyUnsafe : Bool := false trustReportPath : Option String := none assumptionReportPath : Option String := none layoutReportPath : Option String := none @@ -81,6 +82,7 @@ private def parseArgs (args : List String) : IO CLIArgs := do IO.println " --deny-runtime-introspection Fail if any contract uses partially modeled runtime-introspection primitives" IO.println " --deny-proxy-upgradeability Fail if any contract uses `delegatecall`-style proxy / upgradeability mechanics" IO.println " --deny-layout-incompatibility Fail if the candidate layout moves or mutates baseline storage fields" + IO.println " --deny-unsafe Fail if any contract contains `unsafe \"reason\" do` blocks" IO.println " --mapping-slot-scratch-base Scratch memory base for mappingSlot helper (default: 0)" IO.println " --verbose Enable verbose output" IO.println " -v Short form of --verbose" @@ -172,6 +174,8 @@ private def parseArgs (args : List String) : IO CLIArgs := do go rest { cfg with denyProxyUpgradeability := true } | "--deny-layout-incompatibility" :: rest => go rest { cfg with denyLayoutIncompatibility := true } + | "--deny-unsafe" :: rest => + go rest { cfg with denyUnsafe := true } | "--mapping-slot-scratch-base" :: raw :: rest => match raw.toNat? with | some n => go rest { cfg with mappingSlotScratchBase := n } @@ -230,7 +234,7 @@ unsafe def run (args : List String) : IO Unit := do cfg.assumptionReportPath cfg.abiOutDir cfg.denyUncheckedDependencies cfg.denyAssumedDependencies cfg.denyAxiomatizedPrimitives cfg.denyLocalObligations cfg.denyLinearMemoryMechanics cfg.denyEventEmission cfg.denyLowLevelMechanics cfg.denyRuntimeIntrospection cfg.denyProxyUpgradeability cfg.layoutReportPath - cfg.layoutCompatibilityReportPath cfg.denyLayoutIncompatibility + cfg.layoutCompatibilityReportPath cfg.denyLayoutIncompatibility cfg.denyUnsafe catch e => if e.toString == "help" then return () diff --git a/Compiler/Proofs/EndToEnd.lean b/Compiler/Proofs/EndToEnd.lean index 3ec792264..7bdcfa684 100644 --- a/Compiler/Proofs/EndToEnd.lean +++ b/Compiler/Proofs/EndToEnd.lean @@ -175,7 +175,7 @@ theorem compileFunctionSpec_bridged_of_safe_static_params Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts fields errors .calldata [] false spec.body) (hcompile : - CompilationModel.compileFunctionSpec fields events errors selector spec = + CompilationModel.compileFunctionSpec fields events errors [] selector spec = Except.ok irFn) : Compiler.Proofs.YulGeneration.Backends.BridgedStmts irFn.body := by rcases Compiler.Proofs.IRGeneration.Function.compileFunctionSpec_ok_components @@ -205,7 +205,7 @@ theorem compiledExternalFunctions_bridged_of_safe_static List.Forall₂ (fun entry irFn => CompilationModel.compileFunctionSpec fields events errors - entry.2 entry.1 = Except.ok irFn) + [] entry.2 entry.1 = Except.ok irFn) entries irFns → (∀ entry, entry ∈ entries → Compiler.Proofs.YulGeneration.Backends.AllStaticScalarParams diff --git a/Compiler/Proofs/IRGeneration/Contract.lean b/Compiler/Proofs/IRGeneration/Contract.lean index dba80437f..cd25955c0 100644 --- a/Compiler/Proofs/IRGeneration/Contract.lean +++ b/Compiler/Proofs/IRGeneration/Contract.lean @@ -33,10 +33,10 @@ private theorem compiled_functions_forall₂_of_mapM_ok (errors : List ErrorDef) : ∀ (entries : List (FunctionSpec × Nat)) irFns, (entries.mapM fun (entry : FunctionSpec × Nat) => - compileFunctionSpec fields events errors entry.2 entry.1) = Except.ok irFns → + compileFunctionSpec fields events errors [] entry.2 entry.1) = Except.ok irFns → List.Forall₂ (fun (entry : FunctionSpec × Nat) irFn => - compileFunctionSpec fields events errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec fields events errors [] entry.2 entry.1 = Except.ok irFn) entries irFns := by intro entries induction entries with @@ -46,12 +46,12 @@ private theorem compiled_functions_forall₂_of_mapM_ok simp | cons entry entries ih => intro irFns hmap - rcases hstep : compileFunctionSpec fields events errors entry.2 entry.1 with _ | irFn + rcases hstep : compileFunctionSpec fields events errors [] entry.2 entry.1 with _ | irFn · simp only [List.mapM_cons, hstep, bind, Except.bind] at hmap cases hmap · rcases htail : List.mapM (fun (entry : FunctionSpec × Nat) => - compileFunctionSpec fields events errors entry.2 entry.1) entries with _ | irFnsTail + compileFunctionSpec fields events errors [] entry.2 entry.1) entries with _ | irFnsTail · simp only [List.mapM_cons, hstep, htail, bind, Except.bind] at hmap cases hmap · simp only [List.mapM_cons, hstep, htail, bind, Except.bind] at hmap @@ -63,11 +63,11 @@ private theorem compiled_internal_functions_forall₂_of_mapM_ok (events : List EventDef) (errors : List ErrorDef) : ∀ (entries : List FunctionSpec) internalDefs, - (entries.mapM (compileInternalFunction fields events errors)) = + (entries.mapM (compileInternalFunction fields events errors [])) = Except.ok internalDefs → List.Forall₂ (fun fn internalDef => - compileInternalFunction fields events errors fn = Except.ok internalDef) + compileInternalFunction fields events errors [] fn = Except.ok internalDef) entries internalDefs := by intro entries induction entries with @@ -77,11 +77,11 @@ private theorem compiled_internal_functions_forall₂_of_mapM_ok simp | cons entry entries ih => intro internalDefs hmap - rcases hstep : compileInternalFunction fields events errors entry with _ | internalDef + rcases hstep : compileInternalFunction fields events errors [] entry with _ | internalDef · simp only [List.mapM_cons, hstep, bind, Except.bind] at hmap cases hmap · rcases htail : - List.mapM (compileInternalFunction fields events errors) entries with _ | internalDefsTail + List.mapM (compileInternalFunction fields events errors []) entries with _ | internalDefsTail · simp only [List.mapM_cons, hstep, htail, bind, Except.bind] at hmap cases hmap · simp only [List.mapM_cons, hstep, htail, bind, Except.bind] at hmap @@ -381,7 +381,7 @@ private theorem compileValidatedCore_ok_yields_compiled_functions (hcore : compileValidatedCore model selectors = Except.ok ir) : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := by have hfallback : @@ -393,21 +393,22 @@ private theorem compileValidatedCore_ok_yields_compiled_functions pickUniqueFunctionByName_eq_ok_none_of_absent "receive" model.functions hSupported.noReceive unfold compileValidatedCore at hcore - rw [hSupported.normalizedFields, hSupported.noEvents, hSupported.noErrors, + rw [hSupported.normalizedFields, + hSupported.noAdtTypes, hSupported.noEvents, hSupported.noErrors, hfallback, hreceive] at hcore simp only [bind, Except.bind, pure, Except.pure] at hcore rcases hmap : ((model.functions.filter (fun fn => !fn.isInternal && !isInteropEntrypointName fn.name)).zip selectors).mapM - (fun x => compileFunctionSpec model.fields [] [] x.2 x.1) with _ | irFns + (fun x => compileFunctionSpec model.fields [] [] [] x.2 x.1) with _ | irFns · simp [hmap] at hcore · simp [hmap] at hcore rcases hinternal : (model.functions.filter (·.isInternal)).mapM - (compileInternalFunction model.fields [] []) with _ | internalFuncDefs + (compileInternalFunction model.fields [] [] []) with _ | internalFuncDefs · simp [hinternal] at hcore · rcases hctor : - compileConstructor model.fields [] [] model.constructor with _ | deployStmts + compileConstructor model.fields [] [] [] model.constructor with _ | deployStmts · simp [hinternal, hctor] at hcore cases hcore · simp [hinternal, hctor] at hcore @@ -418,13 +419,15 @@ private theorem compileValidatedCore_ok_yields_compiled_functions have hcompiled : List.Forall₂ (fun (entry : FunctionSpec × Nat) irFn => - compileFunctionSpec model.fields [] [] entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) ((model.functions.filter (fun fn => !fn.isInternal && !isInteropEntrypointName fn.name)).zip selectors) irFns := - compiled_functions_forall₂_of_mapM_ok model.fields [] [] _ _ hmap + by + simpa [hSupported.noEvents, hSupported.noErrors] using + (compiled_functions_forall₂_of_mapM_ok model.fields [] [] _ _ hmap) simpa [SourceSemantics.selectorFunctionPairs, selectorDispatchedFunctions, - hSupported.noEvents, hSupported.noErrors, hfunctions] using hcompiled + hfunctions] using hcompiled private theorem compileValidatedCore_ok_yields_compiled_functions_except_mapping_writes (model : CompilationModel) @@ -434,7 +437,7 @@ private theorem compileValidatedCore_ok_yields_compiled_functions_except_mapping (hcore : compileValidatedCore model selectors = Except.ok ir) : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := by have hfallback : @@ -446,21 +449,22 @@ private theorem compileValidatedCore_ok_yields_compiled_functions_except_mapping pickUniqueFunctionByName_eq_ok_none_of_absent "receive" model.functions hSupported.noReceive unfold compileValidatedCore at hcore - rw [hSupported.normalizedFields, hSupported.noEvents, hSupported.noErrors, + rw [hSupported.normalizedFields, + hSupported.noAdtTypes, hSupported.noEvents, hSupported.noErrors, hfallback, hreceive] at hcore simp only [bind, Except.bind, pure, Except.pure] at hcore rcases hmap : ((model.functions.filter (fun fn => !fn.isInternal && !isInteropEntrypointName fn.name)).zip selectors).mapM - (fun x => compileFunctionSpec model.fields [] [] x.2 x.1) with _ | irFns + (fun x => compileFunctionSpec model.fields [] [] [] x.2 x.1) with _ | irFns · simp [hmap] at hcore · simp [hmap] at hcore rcases hinternal : (model.functions.filter (·.isInternal)).mapM - (compileInternalFunction model.fields [] []) with _ | internalFuncDefs + (compileInternalFunction model.fields [] [] []) with _ | internalFuncDefs · simp [hinternal] at hcore · rcases hctor : - compileConstructor model.fields [] [] model.constructor with _ | deployStmts + compileConstructor model.fields [] [] [] model.constructor with _ | deployStmts · simp [hinternal, hctor] at hcore cases hcore · simp [hinternal, hctor] at hcore @@ -471,13 +475,15 @@ private theorem compileValidatedCore_ok_yields_compiled_functions_except_mapping have hcompiled : List.Forall₂ (fun (entry : FunctionSpec × Nat) irFn => - compileFunctionSpec model.fields [] [] entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) ((model.functions.filter (fun fn => !fn.isInternal && !isInteropEntrypointName fn.name)).zip selectors) irFns := - compiled_functions_forall₂_of_mapM_ok model.fields [] [] _ _ hmap + by + simpa [hSupported.noEvents, hSupported.noErrors] using + (compiled_functions_forall₂_of_mapM_ok model.fields [] [] _ _ hmap) simpa [SourceSemantics.selectorFunctionPairs, selectorDispatchedFunctions, - hSupported.noEvents, hSupported.noErrors, hfunctions] using hcompiled + hfunctions] using hcompiled private theorem filterInternalFunctions_eq_nil_of_all_nonInternal : ∀ (fns : List FunctionSpec), @@ -533,20 +539,19 @@ private theorem compileValidatedCore_ok_yields_internalFunctions_nil hSupported.contractUsesDynamicBytesEq_eq_false unfold compileValidatedCore at hcore rw [hSupported.normalizedFields, hfallback, hreceive, harray, hstorageArray, - hdynamicBytesEq, hnoInternalFns] at hcore + hdynamicBytesEq, hnoInternalFns, hSupported.noAdtTypes] at hcore simp only [bind, Except.bind, pure, Except.pure, List.mapM_nil] at hcore rcases hmap : ((model.functions.filter (fun fn => !fn.isInternal && !isInteropEntrypointName fn.name)).zip selectors).mapM - (fun x => compileFunctionSpec model.fields model.events model.errors x.2 x.1) with _ | irFns + (fun x => compileFunctionSpec model.fields model.events model.errors [] x.2 x.1) with _ | irFns · simp [hmap] at hcore · rcases hctor : - compileConstructor model.fields model.events model.errors model.constructor with _ | deployStmts + compileConstructor model.fields model.events model.errors [] model.constructor with _ | deployStmts · simp [hmap, hctor] at hcore cases hcore · simp [hmap, hctor] at hcore - injection hcore with hir - cases hir + cases hcore rfl theorem supported_params_of_supportedSpec @@ -595,7 +600,7 @@ theorem interpretContract_correct_of_ir_functions (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -604,7 +609,7 @@ theorem interpretContract_correct_of_ir_functions (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) @@ -634,7 +639,7 @@ theorem compile_preserves_semantics_of_compiled_functions (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions) (hparamsSupported : @@ -643,7 +648,7 @@ theorem compile_preserves_semantics_of_compiled_functions (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) @@ -674,7 +679,7 @@ theorem compile_ok_yields_compiled_functions (hcompile : CompilationModel.compile model selectors = Except.ok ir) : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := by unfold CompilationModel.compile at hcompile @@ -697,7 +702,7 @@ theorem compile_ok_yields_compiled_functions_except_mapping_writes (hcompile : CompilationModel.compile model selectors = Except.ok ir) : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := by unfold CompilationModel.compile at hcompile @@ -762,15 +767,15 @@ theorem compile_ok_yields_internalFunctions_nil_except_mapping_writes · simp [hvalidate] at hcompile unfold compileValidatedCore at hcompile rw [hSupported.normalizedFields, hfallback, hreceive, harray, hstorageArray, - hdynamicBytesEq, hnoInternalFns] at hcompile + hdynamicBytesEq, hnoInternalFns, hSupported.noAdtTypes] at hcompile simp only [bind, Except.bind, pure, Except.pure, List.mapM_nil] at hcompile rcases hmap : ((model.functions.filter (fun fn => !fn.isInternal && !isInteropEntrypointName fn.name)).zip selectors).mapM - (fun x => compileFunctionSpec model.fields model.events model.errors x.2 x.1) with _ | irFns + (fun x => compileFunctionSpec model.fields model.events model.errors [] x.2 x.1) with _ | irFns · simp [hmap] at hcompile · rcases hctor : - compileConstructor model.fields model.events model.errors model.constructor with _ | deployStmts + compileConstructor model.fields model.events model.errors [] model.constructor with _ | deployStmts · simp [hmap, hctor] at hcompile cases hcompile · simp [hmap, hctor] at hcompile @@ -792,7 +797,7 @@ theorem compileFunctionSpec_ok_yields_legacyCompatibleExternalStmtList (irFn : IRFunction) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) : + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) : LegacyCompatibleExternalStmtList irFn.body := by rcases Function.compileFunctionSpec_ok_components model.fields model.events model.errors sel fn irFn hcompileFn with @@ -812,8 +817,7 @@ theorem compileFunctionSpec_ok_yields_legacyCompatibleExternalStmtList hbody.state.surfaceClosed (SupportedBodyCallInterface.surfaceClosed hbody) hbody.effects.surfaceClosed) - (hcompile := by - simpa [hSupported.noEvents, hSupported.noErrors] using hbodyCompile) + (hcompile := hbodyCompile) exact legacyCompatibleExternalStmtList_append _ _ hparams hbody theorem compileFunctionSpec_ok_yields_legacyCompatibleExternalStmtList_except_mapping_writes @@ -825,7 +829,7 @@ theorem compileFunctionSpec_ok_yields_legacyCompatibleExternalStmtList_except_ma (irFn : IRFunction) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) : + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) : LegacyCompatibleExternalStmtList irFn.body := by rcases Function.compileFunctionSpec_ok_components model.fields model.events model.errors sel fn irFn hcompileFn with @@ -846,8 +850,7 @@ theorem compileFunctionSpec_ok_yields_legacyCompatibleExternalStmtList_except_ma hbody.state.surfaceClosed (SupportedBodyCallInterface.surfaceClosed_exceptMappingWrites (hBody := hbody)) hbody.effects.surfaceClosed) - (hcompile := by - simpa [hSupported.noEvents, hSupported.noErrors] using hbodyCompile) + (hcompile := hbodyCompile) exact legacyCompatibleExternalStmtList_append _ _ hparams hbody private theorem compiled_functions_legacyCompatibleExternalBodies @@ -857,7 +860,7 @@ private theorem compiled_functions_legacyCompatibleExternalBodies ∀ {entries irFns}, List.Forall₂ (fun (entry : FunctionSpec × Nat) irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) entries irFns → (∀ entry ∈ entries, entry.1 ∈ selectorDispatchedFunctions model) → @@ -895,7 +898,7 @@ private theorem compiled_functions_legacyCompatibleExternalBodies_except_mapping ∀ {entries irFns}, List.Forall₂ (fun (entry : FunctionSpec × Nat) irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) entries irFns → (∀ entry ∈ entries, entry.1 ∈ selectorDispatchedFunctions model) → @@ -936,7 +939,7 @@ theorem compile_ok_yields_legacyCompatibleExternalBodies have hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := compile_ok_yields_compiled_functions @@ -968,7 +971,7 @@ theorem compile_ok_yields_legacyCompatibleExternalBodies_except_mapping_writes have hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := compile_ok_yields_compiled_functions_except_mapping_writes @@ -1052,7 +1055,7 @@ theorem compileFunctionSpec_correct_generic (hcalldataSizeFits : Function.TxCalldataSizeFitsEvm tx) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) : FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) @@ -1107,7 +1110,7 @@ theorem compileFunctionSpec_correct_generic_except_mapping_writes (hsafety : SupportedStmtListMappingWriteSlotSafety model.fields) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) : FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -1149,7 +1152,7 @@ theorem compileFunctionSpec_correct_generic_except_mapping_writes_stmtSafety (hsafety : ∀ stmt ∈ fn.body, StmtMappingWriteSlotSafe model.fields stmt) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) : FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -1196,7 +1199,7 @@ theorem compileFunctionSpec_correct_generic_with_helper_proofs (hcalldataSizeFits : Function.TxCalldataSizeFitsEvm tx) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) : FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) @@ -1250,7 +1253,7 @@ theorem compileFunctionSpec_correct_generic_with_helper_proofs_and_helper_ir (hcalldataSizeFits : Function.TxCalldataSizeFitsEvm tx) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (hhelperIR : execIRFunctionWithInternals runtimeContract 0 irFn tx.args @@ -1301,7 +1304,7 @@ theorem compileFunctionSpec_correct_generic_with_helper_proofs_and_helper_ir_of_ (hcalldataSizeFits : Function.TxCalldataSizeFitsEvm tx) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (hbodyDisjoint : YulStmtListCallsDisjointFromInternalTable runtimeContract irFn.body) : @@ -1367,7 +1370,7 @@ theorem compile_preserves_semantics have hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := compile_ok_yields_compiled_functions @@ -1383,7 +1386,7 @@ theorem compile_preserves_semantics have hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) @@ -1443,7 +1446,7 @@ theorem compile_preserves_semantics_except_mapping_writes have hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := compile_ok_yields_compiled_functions_except_mapping_writes @@ -1459,7 +1462,7 @@ theorem compile_preserves_semantics_except_mapping_writes have hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -1513,7 +1516,7 @@ theorem compile_preserves_semantics_except_mapping_writes_stmtSafety have hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := compile_ok_yields_compiled_functions_except_mapping_writes @@ -1529,7 +1532,7 @@ theorem compile_preserves_semantics_except_mapping_writes_stmtSafety have hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -1681,7 +1684,7 @@ theorem compile_preserves_semantics_with_helper_proofs have hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) ir.functions := compile_ok_yields_compiled_functions @@ -1697,7 +1700,7 @@ theorem compile_preserves_semantics_with_helper_proofs have hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) diff --git a/Compiler/Proofs/IRGeneration/ContractFeatureTest.lean b/Compiler/Proofs/IRGeneration/ContractFeatureTest.lean index 1270226a8..4509c9c15 100644 --- a/Compiler/Proofs/IRGeneration/ContractFeatureTest.lean +++ b/Compiler/Proofs/IRGeneration/ContractFeatureTest.lean @@ -113,6 +113,7 @@ private def literalMappingWrite_supported_spec : { noEvents := rfl noErrors := rfl noExternals := rfl + noAdtTypes := rfl noFallback := literalMappingWrite_noFallback noReceive := literalMappingWrite_noReceive } constructor := by @@ -501,6 +502,7 @@ private theorem constructorOnly_compileConstructor : constructorOnlySpec.fields constructorOnlySpec.events constructorOnlySpec.errors + [] constructorOnlySpec.constructor = Except.ok (genConstructorArgLoads constructorOnlyCtor.params ++ bodyStmts) ∧ compileStmtList @@ -511,6 +513,7 @@ private theorem constructorOnly_compileConstructor : [] false (constructorOnlyCtor.params.map (·.name)) + [] constructorOnlyCtor.body = Except.ok bodyStmts := by rcases Function.compileConstructor_ok_components @@ -527,6 +530,7 @@ private theorem constructorOnly_compileConstructor : [] false (constructorOnlyCtor.params.map (·.name)) + [] constructorOnlyCtor.body with | .ok body => body | .error _ => []) @@ -546,6 +550,7 @@ example : constructorOnlySpec.fields constructorOnlySpec.events constructorOnlySpec.errors + [] constructorOnlySpec.constructor = Except.ok (genConstructorArgLoads constructorOnlyCtor.params ++ bodyStmts) := by rcases constructorOnly_compileConstructor with ⟨bodyStmts, hdeploy, _⟩ @@ -561,8 +566,9 @@ example : collectStmtListBindNames identityInternalHelper.body) → compileStmtList [] [] [] .calldata retNames true (identityInternalHelper.params.map (·.name) ++ retNames) + [] identityInternalHelper.body = Except.ok bodyStmts → - compileInternalFunction [] [] [] identityInternalHelper = + compileInternalFunction [] [] [] [] identityInternalHelper = Except.ok (YulStmt.funcDef (internalFunctionYulName identityInternalHelper.name) @@ -840,6 +846,7 @@ example : (sizeOf (match compileStmtList constructorOnlySpec.fields [] [] .memory [] false + (constructorOnlyCtor.params.map (·.name)) [] [Stmt.setStorageAddr "owner" (.param "initialOwner"), .stop] with | .ok body => body @@ -850,15 +857,16 @@ example : (match compileStmtList constructorOnlySpec.fields [] [] .memory [] false (constructorOnlyCtor.params.map (·.name)) + [] [Stmt.setStorageAddr "owner" (.param "initialOwner"), .stop] with | .ok body => body | .error _ => []))) := by have hbodyCompile : compileStmtList constructorOnlySpec.fields constructorOnlySpec.events constructorOnlySpec.errors - .memory [] false (constructorOnlyCtor.params.map (·.name)) constructorOnlyCtor.body = + .memory [] false (constructorOnlyCtor.params.map (·.name)) [] constructorOnlyCtor.body = Except.ok (match compileStmtList constructorOnlySpec.fields [] [] .memory [] false - (constructorOnlyCtor.params.map (·.name)) constructorOnlyCtor.body with + (constructorOnlyCtor.params.map (·.name)) [] constructorOnlyCtor.body with | .ok body => body | .error _ => []) := by rfl @@ -889,7 +897,7 @@ example : (initialWorld := Verity.defaultState) (bindings := [("initialOwner", Compiler.Constants.addressMask &&& 11)]) (bodyStmts := match compileStmtList constructorOnlySpec.fields [] [] .memory [] false - [] constructorOnlyCtor.body with + (constructorOnlyCtor.params.map (·.name)) [] constructorOnlyCtor.body with | .ok body => body | .error _ => []) (hbodyCompile := hbodyCompile) @@ -917,7 +925,7 @@ example : bodyStmts)) := by let bodyStmts := match compileStmtList constructorOnlySpec.fields [] [] .memory [] false - (constructorOnlyCtor.params.map (·.name)) constructorOnlyCtor.body with + (constructorOnlyCtor.params.map (·.name)) [] constructorOnlyCtor.body with | .ok body => body | .error _ => [] let bindings := [("initialOwner", Compiler.Constants.addressMask &&& 11)] @@ -925,7 +933,7 @@ example : · native_decide · have hbodyCompile : compileStmtList constructorOnlySpec.fields constructorOnlySpec.events constructorOnlySpec.errors - .memory [] false (constructorOnlyCtor.params.map (·.name)) constructorOnlyCtor.body = + .memory [] false (constructorOnlyCtor.params.map (·.name)) [] constructorOnlyCtor.body = Except.ok bodyStmts := by rfl have hbind : diff --git a/Compiler/Proofs/IRGeneration/Dispatch.lean b/Compiler/Proofs/IRGeneration/Dispatch.lean index 0ae6f6e76..325ce3bc8 100644 --- a/Compiler/Proofs/IRGeneration/Dispatch.lean +++ b/Compiler/Proofs/IRGeneration/Dispatch.lean @@ -76,14 +76,14 @@ private theorem find_compiledFunction_some_of_forall₂ (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec fields events errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec fields events errors [] entry.2 entry.1 = Except.ok irFn) pairs irFns) {fn : FunctionSpec} {sel : Nat} (hfind : pairs.find? (fun entry => entry.2 == selector) = some (fn, sel)) : ∃ irFn, irFns.find? (fun irFn => irFn.selector == selector) = some irFn ∧ - compileFunctionSpec fields events errors sel fn = Except.ok irFn := by + compileFunctionSpec fields events errors [] sel fn = Except.ok irFn := by induction hcompiled generalizing fn sel with | nil => simp at hfind @@ -92,7 +92,7 @@ private theorem find_compiledFunction_some_of_forall₂ by_cases hselEq : headSel = selector · simp [hselEq] at hfind rcases hfind with ⟨rfl, rfl⟩ - have hhead' : compileFunctionSpec fields events errors selector headFn = Except.ok irFn := by + have hhead' : compileFunctionSpec fields events errors [] selector headFn = Except.ok irFn := by simpa [hselEq] using hhead refine ⟨irFn, ?_, hhead'⟩ have hselector : irFn.selector = selector := by @@ -119,7 +119,7 @@ private theorem find_compiledFunction_none_of_forall₂ (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec fields events errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec fields events errors [] entry.2 entry.1 = Except.ok irFn) pairs irFns) (hfind : pairs.find? (fun entry => entry.2 == selector) = none) : @@ -146,7 +146,7 @@ theorem interpretContract_correct_of_compiled_functions (initialWorld : Verity.ContractState) (hcompiled : List.Forall₂ - (fun entry irFn => compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + (fun entry irFn => compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -155,7 +155,7 @@ theorem interpretContract_correct_of_compiled_functions (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) @@ -253,7 +253,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -262,7 +262,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) @@ -274,7 +274,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs have hlegacyFunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) @@ -305,14 +305,14 @@ private theorem legacy_function_correct_of_supportedSourceFunctionSemanticsExcep (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) (execIRFunction irFn tx.args (FunctionBody.initialIRStateForTx model tx initialWorld))) : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (SourceSemantics.interpretFunction model fn tx initialWorld) @@ -335,7 +335,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -344,7 +344,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -384,7 +384,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes_an (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -393,7 +393,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes_an (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -432,7 +432,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes_an (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -441,7 +441,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes_an (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -482,7 +482,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes_an (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -491,7 +491,7 @@ theorem interpretContract_correct_of_compiled_functions_except_mapping_writes_an (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemanticsExceptMappingWrites model selectors hSupported fn tx initialWorld) @@ -534,7 +534,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -543,7 +543,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) @@ -581,7 +581,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -590,7 +590,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) @@ -636,7 +636,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -645,7 +645,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) @@ -684,7 +684,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hcompiled : List.Forall₂ (fun entry irFn => - compileFunctionSpec model.fields model.events model.errors entry.2 entry.1 = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] entry.2 entry.1 = Except.ok irFn) (SourceSemantics.selectorFunctionPairs model selectors) irFns) (hparamsSupported : @@ -693,7 +693,7 @@ theorem interpretContract_correct_of_compiled_functions_with_helper_proofs_and_h (hfunction : ∀ fn sel irFn bindings, fn ∈ selectorDispatchedFunctions model → - compileFunctionSpec model.fields model.events model.errors sel fn = Except.ok irFn → + compileFunctionSpec model.fields model.events model.errors [] sel fn = Except.ok irFn → SourceSemantics.bindSupportedParams fn.params tx.args = some bindings → FunctionBody.sourceResultMatchesIRResult (supportedSourceFunctionSemantics model selectors hSupported fn tx initialWorld) diff --git a/Compiler/Proofs/IRGeneration/ExprCore.lean b/Compiler/Proofs/IRGeneration/ExprCore.lean index 1196927ff..1fb1e1df4 100644 --- a/Compiler/Proofs/IRGeneration/ExprCore.lean +++ b/Compiler/Proofs/IRGeneration/ExprCore.lean @@ -34,7 +34,9 @@ def exprBoundNames : Expr → List String | .delegatecall gas target inOffset inSize outOffset outSize => exprBoundNames gas ++ exprBoundNames target ++ exprBoundNames inOffset ++ exprBoundNames inSize ++ exprBoundNames outOffset ++ exprBoundNames outSize - | .externalCall _ args | .internalCall _ args => exprListBoundNames args + | .externalCall _ args | .internalCall _ args | .adtConstruct _ _ args => exprListBoundNames args + | .adtTag _ field => [field] + | .adtField _ _ _ _ storageField => [storageField] | .arrayElement name index => name :: exprBoundNames index | .arrayLength name => [name] | .storageArrayLength name => [name] @@ -198,6 +200,20 @@ inductive StmtListCompileCore : List String → List Stmt → Prop where | stop {scope : List String} {rest : List Stmt} : StmtListCompileCore scope rest → StmtListCompileCore scope (.stop :: rest) + | mstore {scope : List String} {offset value : Expr} {rest : List Stmt} : + ExprCompileCore offset → + exprBoundNamesInScope offset scope → + ExprCompileCore value → + exprBoundNamesInScope value scope → + StmtListCompileCore scope rest → + StmtListCompileCore scope (.mstore offset value :: rest) + | tstore {scope : List String} {offset value : Expr} {rest : List Stmt} : + ExprCompileCore offset → + exprBoundNamesInScope offset scope → + ExprCompileCore value → + exprBoundNamesInScope value scope → + StmtListCompileCore scope rest → + StmtListCompileCore scope (.tstore offset value :: rest) /-- Core statement lists whose execution is guaranteed to terminate before reaching the end of the list. This is the smallest useful extension needed @@ -227,6 +243,20 @@ inductive StmtListTerminalCore : List String → List Stmt → Prop where | stop {scope : List String} {rest : List Stmt} : StmtListCompileCore scope rest → StmtListTerminalCore scope (.stop :: rest) + | mstore {scope : List String} {offset value : Expr} {rest : List Stmt} : + ExprCompileCore offset → + exprBoundNamesInScope offset scope → + ExprCompileCore value → + exprBoundNamesInScope value scope → + StmtListTerminalCore scope rest → + StmtListTerminalCore scope (.mstore offset value :: rest) + | tstore {scope : List String} {offset value : Expr} {rest : List Stmt} : + ExprCompileCore offset → + exprBoundNamesInScope offset scope → + ExprCompileCore value → + exprBoundNamesInScope value scope → + StmtListTerminalCore scope rest → + StmtListTerminalCore scope (.tstore offset value :: rest) | ite {scope : List String} {cond : Expr} {thenBranch elseBranch rest : List Stmt} : ExprCompileCore cond → diff --git a/Compiler/Proofs/IRGeneration/Function.lean b/Compiler/Proofs/IRGeneration/Function.lean index 92195ff4c..c93ee9e84 100644 --- a/Compiler/Proofs/IRGeneration/Function.lean +++ b/Compiler/Proofs/IRGeneration/Function.lean @@ -179,8 +179,8 @@ theorem compileFunctionSpec_ok_of_components (hreturns : functionReturns spec = Except.ok returns) (hbody : compileStmtList fields events errors .calldata [] false - (spec.params.map (·.name)) spec.body = Except.ok bodyStmts) : - compileFunctionSpec fields events errors selector spec = + (spec.params.map (·.name)) [] spec.body = Except.ok bodyStmts) : + compileFunctionSpec fields events errors [] selector spec = Except.ok (compiledFunctionIR selector spec returns bodyStmts) := by unfold CompilationModel.compileFunctionSpec rw [hvalidate, hreturns, hbody] @@ -189,7 +189,7 @@ theorem compileFunctionSpec_ok_of_components theorem compileFunctionSpec_ok_params (fields : List Field) (events : List EventDef) (errors : List ErrorDef) (selector : Nat) (spec : FunctionSpec) (irFn : IRFunction) - (hcompile : compileFunctionSpec fields events errors selector spec = Except.ok irFn) : + (hcompile : compileFunctionSpec fields events errors [] selector spec = Except.ok irFn) : irFn.params = spec.params.map Param.toIRParam := by unfold CompilationModel.compileFunctionSpec at hcompile cases hvalidate : validateFunctionSpec spec @@ -202,7 +202,7 @@ theorem compileFunctionSpec_ok_params case ok returns => cases hbody : compileStmtList fields events errors .calldata [] false - (spec.params.map (·.name)) spec.body + (spec.params.map (·.name)) [] spec.body · rw [hvalidate, hreturns, hbody] at hcompile cases hcompile case ok bodyStmts => @@ -213,7 +213,7 @@ theorem compileFunctionSpec_ok_params theorem compileFunctionSpec_ok_selector (fields : List Field) (events : List EventDef) (errors : List ErrorDef) (selector : Nat) (spec : FunctionSpec) (irFn : IRFunction) - (hcompile : compileFunctionSpec fields events errors selector spec = Except.ok irFn) : + (hcompile : compileFunctionSpec fields events errors [] selector spec = Except.ok irFn) : irFn.selector = selector := by unfold CompilationModel.compileFunctionSpec at hcompile cases hvalidate : validateFunctionSpec spec @@ -226,7 +226,7 @@ theorem compileFunctionSpec_ok_selector case ok returns => cases hbody : compileStmtList fields events errors .calldata [] false - (spec.params.map (·.name)) spec.body + (spec.params.map (·.name)) [] spec.body · rw [hvalidate, hreturns, hbody] at hcompile cases hcompile case ok bodyStmts => @@ -237,12 +237,12 @@ theorem compileFunctionSpec_ok_selector theorem compileFunctionSpec_ok_components (fields : List Field) (events : List EventDef) (errors : List ErrorDef) (selector : Nat) (spec : FunctionSpec) (irFn : IRFunction) - (hcompile : compileFunctionSpec fields events errors selector spec = Except.ok irFn) : + (hcompile : compileFunctionSpec fields events errors [] selector spec = Except.ok irFn) : ∃ returns bodyStmts, validateFunctionSpec spec = Except.ok () ∧ functionReturns spec = Except.ok returns ∧ compileStmtList fields events errors .calldata [] false - (spec.params.map (·.name)) spec.body = Except.ok bodyStmts ∧ + (spec.params.map (·.name)) [] spec.body = Except.ok bodyStmts ∧ irFn = compiledFunctionIR selector spec returns bodyStmts := by unfold CompilationModel.compileFunctionSpec at hcompile cases hvalidate : validateFunctionSpec spec @@ -255,7 +255,7 @@ theorem compileFunctionSpec_ok_components case ok returns => cases hbody : compileStmtList fields events errors .calldata [] false - (spec.params.map (·.name)) spec.body + (spec.params.map (·.name)) [] spec.body · rw [hvalidate, hreturns, hbody] at hcompile cases hcompile case ok bodyStmts => @@ -268,25 +268,25 @@ theorem compileFunctionSpec_ok_components theorem compileConstructor_some_ok_of_body (fields : List Field) (events : List EventDef) (errors : List ErrorDef) (ctor : ConstructorSpec) (bodyStmts : List YulStmt) - (hbody : - compileStmtList fields events errors .memory [] false - (ctor.params.map (·.name)) ctor.body = Except.ok bodyStmts) : - compileConstructor fields events errors (some ctor) = - Except.ok (genConstructorArgLoads ctor.params ++ bodyStmts) := by + (hbody : + compileStmtList fields events errors .memory [] false + (ctor.params.map (·.name)) [] ctor.body = Except.ok bodyStmts) : + compileConstructor fields events errors [] (some ctor) = + Except.ok (genConstructorArgLoads ctor.params ++ bodyStmts) := by simp [CompilationModel.compileConstructor, hbody] theorem compileConstructor_ok_components (fields : List Field) (events : List EventDef) (errors : List ErrorDef) - (ctor : ConstructorSpec) (deployStmts : List YulStmt) - (hcompile : - compileConstructor fields events errors (some ctor) = Except.ok deployStmts) : - ∃ bodyStmts, - compileStmtList fields events errors .memory [] false - (ctor.params.map (·.name)) ctor.body = Except.ok bodyStmts ∧ - deployStmts = genConstructorArgLoads ctor.params ++ bodyStmts := by - cases hbody : - compileStmtList fields events errors .memory [] false - (ctor.params.map (·.name)) ctor.body with + (ctor : ConstructorSpec) (deployStmts : List YulStmt) + (hcompile : + compileConstructor fields events errors [] (some ctor) = Except.ok deployStmts) : + ∃ bodyStmts, + compileStmtList fields events errors .memory [] false + (ctor.params.map (·.name)) [] ctor.body = Except.ok bodyStmts ∧ + deployStmts = genConstructorArgLoads ctor.params ++ bodyStmts := by + cases hbody : + compileStmtList fields events errors .memory [] false + (ctor.params.map (·.name)) [] ctor.body with | error err => simp [CompilationModel.compileConstructor, hbody] at hcompile | ok bodyStmts => @@ -932,7 +932,7 @@ theorem supported_function_body_correct_from_exact_state_core (hcore : FunctionBody.StmtListCompileCore (fn.params.map (·.name)) fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hstateRuntime : FunctionBody.runtimeStateMatchesIR (SourceSemantics.effectiveFields model) @@ -970,7 +970,7 @@ theorem supported_function_body_correct_from_exact_state_core simpa [FunctionBody.runtimeStateMatchesIR] using hstateRuntime have hbodyCompile' : compileStmtList (SourceSemantics.effectiveFields model) [] [] - .calldata [] false (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by + .calldata [] false (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by simpa [hnormalized, hnoEvents, hnoErrors] using hbodyCompile rcases FunctionBody.exec_compileStmtList_core (fields := SourceSemantics.effectiveFields model) @@ -1006,7 +1006,7 @@ theorem supported_function_body_correct_from_exact_state_core_extraFuel (hcore : FunctionBody.StmtListCompileCore (fn.params.map (·.name)) fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hstateRuntime : FunctionBody.runtimeStateMatchesIR (SourceSemantics.effectiveFields model) @@ -1044,7 +1044,7 @@ theorem supported_function_body_correct_from_exact_state_core_extraFuel simpa [FunctionBody.runtimeStateMatchesIR] using hstateRuntime have hbodyCompile' : compileStmtList (SourceSemantics.effectiveFields model) [] [] - .calldata [] false (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by + .calldata [] false (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by simpa [hnormalized, hnoEvents, hnoErrors] using hbodyCompile rcases FunctionBody.exec_compileStmtList_core_extraFuel (fields := SourceSemantics.effectiveFields model) @@ -1081,7 +1081,7 @@ theorem supported_function_body_correct_from_exact_state_terminal_core_extraFuel (hterminal : FunctionBody.StmtListTerminalCore (fn.params.map (·.name)) fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hstateRuntime : FunctionBody.runtimeStateMatchesIR (SourceSemantics.effectiveFields model) @@ -1123,7 +1123,7 @@ theorem supported_function_body_correct_from_exact_state_terminal_core_extraFuel simpa [FunctionBody.runtimeStateMatchesIR] using hstateRuntime have hbodyCompile' : compileStmtList (SourceSemantics.effectiveFields model) [] [] - .calldata [] false (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by + .calldata [] false (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by simpa [hnormalized, hnoEvents, hnoErrors] using hbodyCompile let sizeSlack := extraFuel - (sizeOf bodyStmts - bodyStmts.length) rcases FunctionBody.exec_compileStmtList_terminal_core_sizeOf_extraFuel @@ -1177,8 +1177,8 @@ theorem compileFunctionSpec_correct_of_body (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) - (hcompile : compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) + (hcompile : compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hparamsSupported : ∀ param ∈ fn.params, SupportedExternalParamType param.ty) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) @@ -1254,11 +1254,11 @@ theorem compileFunctionSpec_correct_of_body_normalized_extraFuel compileStmtList (applySlotAliasRanges model.fields model.slotAliasRanges) model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : compileFunctionSpec (applySlotAliasRanges model.fields model.slotAliasRanges) - model.events model.errors selector fn = Except.ok irFn) + model.events model.errors [] selector fn = Except.ok irFn) (hparamsSupported : ∀ param ∈ fn.params, SupportedExternalParamType param.ty) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) @@ -1286,10 +1286,10 @@ theorem compileFunctionSpec_correct_of_body_normalized_extraFuel let initialState := FunctionBody.initialIRStateForTx model tx initialWorld have hbodyCompile' : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by simpa [hnormalized] using hbodyCompile have hcompile' : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn := by + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn := by simpa [hnormalized] using hcompile have hcompiled := compileFunctionSpec_ok_of_components model.fields model.events model.errors @@ -1342,11 +1342,11 @@ theorem compileFunctionSpec_correct_of_body_supported_extraFuel compileStmtList (applySlotAliasRanges model.fields model.slotAliasRanges) model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : compileFunctionSpec (applySlotAliasRanges model.fields model.slotAliasRanges) - model.events model.errors selector fn = Except.ok irFn) + model.events model.errors [] selector fn = Except.ok irFn) (hparamsSupported : ∀ param ∈ fn.params, SupportedExternalParamType param.ty) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) @@ -1395,9 +1395,9 @@ theorem supported_function_correct (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) : @@ -1651,9 +1651,10 @@ theorem supported_function_correct (by simpa [SourceSemantics.effectiveFields] using hSupported.normalizedFields) hSupported.noEvents hSupported.noErrors + hSupported.noAdtTypes hsupportedFn.body.helperSurfaceClosed hhelperFree - hbodyCompile + (by rw [hSupported.noAdtTypes]; exact hbodyCompile) hscope hbounded hbodyStateRuntime @@ -1751,9 +1752,9 @@ theorem supported_function_correct_with_helper_proofs_body_goal (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (extraFuel : Nat) @@ -1870,9 +1871,9 @@ theorem supported_function_correct_with_helper_proofs_body_goal_and_helper_ir (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (extraFuel : Nat) @@ -1949,9 +1950,9 @@ theorem supported_function_correct_with_helper_proofs_body_goal_and_helper_ir_of (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (extraFuel : Nat) @@ -2160,8 +2161,8 @@ private theorem compileStmt_constructor_mode_eq (hcoreClosed : stmtTouchesUnsupportedCoreSurface stmt = false) (hcallClosed : stmtTouchesUnsupportedCallSurface stmt = false) (hrawClosed : stmtTouchesUnsupportedConstructorRawCalldataSurface stmt = false) : - compileStmt fields events errors .memory [] false scope stmt = - compileStmt fields [] [] .calldata [] false scope stmt := by + compileStmt fields events errors .memory [] false scope [] stmt = + compileStmt fields [] [] .calldata [] false scope [] stmt := by cases stmt <;> try simp [stmtTouchesUnsupportedEffectSurface] at heffectsClosed <;> try simp [stmtTouchesUnsupportedCoreSurface] at hcoreClosed <;> @@ -2183,8 +2184,8 @@ private theorem compileStmtList_constructor_mode_eq' stmtListTouchesUnsupportedCoreSurface body = false → stmtListTouchesUnsupportedCallSurface body = false → stmtListTouchesUnsupportedConstructorRawCalldataSurface body = false → - compileStmtList fields events errors .memory [] false scope body = - compileStmtList fields [] [] .calldata [] false scope body + compileStmtList fields events errors .memory [] false scope [] body = + compileStmtList fields [] [] .calldata [] false scope [] body | [], _, _, _, _ => by simp [compileStmtList] | stmt :: rest, heffectsClosed, hcoreClosed, hcallClosed, hrawClosed => by simp only [stmtListTouchesUnsupportedEffectSurface, @@ -2222,8 +2223,8 @@ private theorem compileStmtList_constructor_mode_eq (hcoreClosed : stmtListTouchesUnsupportedCoreSurface body = false) (hcallClosed : stmtListTouchesUnsupportedCallSurface body = false) (hrawClosed : stmtListTouchesUnsupportedConstructorRawCalldataSurface body = false) : - compileStmtList fields events errors .memory [] false [] body = - compileStmtList fields [] [] .calldata [] false [] body := by + compileStmtList fields events errors .memory [] false [] [] body = + compileStmtList fields [] [] .calldata [] false [] [] body := by exact compileStmtList_constructor_mode_eq' (events := events) (errors := errors) (scope := []) heffectsClosed hcoreClosed hcallClosed hrawClosed @@ -2374,9 +2375,9 @@ theorem supported_constructor_body_correct_with_body_interface (initialWorld : Verity.ContractState) (bindings : List (String × Nat)) (bodyStmts : List YulStmt) - (hbodyCompile : - compileStmtList model.fields model.events model.errors .memory [] false - (ctor.params.map (·.name)) ctor.body = Except.ok bodyStmts) + (hbodyCompile : + compileStmtList model.fields model.events model.errors .memory [] false + (ctor.params.map (·.name)) [] ctor.body = Except.ok bodyStmts) (hbind : SourceSemantics.bindSupportedParams ctor.params (tx.args.take ctor.params.length) = some bindings) @@ -2411,7 +2412,7 @@ theorem supported_constructor_body_correct_with_body_interface simp [SourceSemantics.constructorExecutionBindings, hbind, hguard] have hbodyCompileCalldata : compileStmtList model.fields [] [] .calldata [] false - (ctor.params.map (·.name)) ctor.body = Except.ok bodyStmts := by + (ctor.params.map (·.name)) [] ctor.body = Except.ok bodyStmts := by have hmode := compileStmtList_constructor_mode_eq' (fields := model.fields) (events := model.events) (errors := model.errors) @@ -2425,7 +2426,7 @@ theorem supported_constructor_body_correct_with_body_interface exact hmode.symm have hbodyCompileEffective : compileStmtList (SourceSemantics.effectiveFields model) [] [] .calldata [] false - (ctor.params.map (·.name)) ctor.body = Except.ok bodyStmts := by + (ctor.params.map (·.name)) [] ctor.body = Except.ok bodyStmts := by simpa [SourceSemantics.effectiveFields, hnormalized] using hbodyCompileCalldata have hstateRuntime : FunctionBody.runtimeStateMatchesIR @@ -2553,6 +2554,7 @@ theorem supported_function_correct_with_body_interface_except_mapping_writes applySlotAliasRanges model.fields model.slotAliasRanges = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hparams : SupportedParamProfile fn.params) (hBody : SupportedBodyInterfaceExceptMappingWrites model fn) (hnoConflict : firstFieldWriteSlotConflict model.fields = none) @@ -2568,9 +2570,9 @@ theorem supported_function_correct_with_body_interface_except_mapping_writes (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) : @@ -2721,9 +2723,10 @@ theorem supported_function_correct_with_body_interface_except_mapping_writes (by simpa [SourceSemantics.effectiveFields] using hnormalized) hnoEvents hnoErrors + hnoAdtTypes hBody.helperSurfaceClosed hhelperFree - hbodyCompile + (by rw [hnoAdtTypes]; exact hbodyCompile) hscope hbounded hbodyStateRuntime @@ -2832,6 +2835,7 @@ theorem supported_function_correct_with_body_interface_except_mapping_writes_stm applySlotAliasRanges model.fields model.slotAliasRanges = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hparams : SupportedParamProfile fn.params) (hBody : SupportedBodyInterfaceExceptMappingWrites model fn) (hnoConflict : firstFieldWriteSlotConflict model.fields = none) @@ -2847,9 +2851,9 @@ theorem supported_function_correct_with_body_interface_except_mapping_writes_stm (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) : @@ -2989,9 +2993,10 @@ theorem supported_function_correct_with_body_interface_except_mapping_writes_stm (by simpa [SourceSemantics.effectiveFields] using hnormalized) hnoEvents hnoErrors + hnoAdtTypes hBody.helperSurfaceClosed hhelperFree - hbodyCompile + (by rw [hnoAdtTypes]; exact hbodyCompile) hscope hbounded hbodyStateRuntime @@ -3103,7 +3108,7 @@ theorem supported_function_correct_except_mapping_writes (bindings : List (String × Nat)) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (hnoConflict : firstFieldWriteSlotConflict model.fields = none) (hsafety : SupportedStmtListMappingWriteSlotSafety model.fields) @@ -3124,6 +3129,7 @@ theorem supported_function_correct_except_mapping_writes (hnormalized := hSupported.normalizedFields) (hnoEvents := hSupported.noEvents) (hnoErrors := hSupported.noErrors) + (hnoAdtTypes := hSupported.noAdtTypes) (hparams := (hSupported.supportedFunctionOfSelectorDispatched hfn).params) (hBody := (hSupported.supportedFunctionOfSelectorDispatched hfn).body) (hnoConflict := hnoConflict) @@ -3158,7 +3164,7 @@ theorem supported_function_correct_except_mapping_writes_stmtSafety (bindings : List (String × Nat)) (hfn : fn ∈ selectorDispatchedFunctions model) (hcompileFn : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (hnoConflict : firstFieldWriteSlotConflict model.fields = none) (hsafety : ∀ stmt ∈ fn.body, StmtMappingWriteSlotSafe model.fields stmt) @@ -3179,6 +3185,7 @@ theorem supported_function_correct_except_mapping_writes_stmtSafety (hnormalized := hSupported.normalizedFields) (hnoEvents := hSupported.noEvents) (hnoErrors := hSupported.noErrors) + (hnoAdtTypes := hSupported.noAdtTypes) (hparams := (hSupported.supportedFunctionOfSelectorDispatched hfn).params) (hBody := (hSupported.supportedFunctionOfSelectorDispatched hfn).body) (hnoConflict := hnoConflict) @@ -3225,9 +3232,9 @@ theorem supported_function_correct_with_helper_proofs_goal (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (hbodyHelperGoal : @@ -3374,9 +3381,10 @@ theorem supported_function_correct_with_helper_proofs_goal (by simpa [SourceSemantics.effectiveFields] using hSupported.normalizedFields) hSupported.noEvents hSupported.noErrors + hSupported.noAdtTypes hsupportedFn.body.helperSurfaceClosed hhelperFree - hbodyCompile + (hSupported.noAdtTypes ▸ hbodyCompile) hscope hbounded hbodyStateRuntime @@ -3415,9 +3423,9 @@ theorem supported_function_correct_with_helper_proofs (hreturns : functionReturns fn = Except.ok returns) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts) (hcompile : - compileFunctionSpec model.fields model.events model.errors selector fn = Except.ok irFn) + compileFunctionSpec model.fields model.events model.errors [] selector fn = Except.ok irFn) (hbind : SourceSemantics.bindSupportedParams fn.params tx.args = some bindings) (htxNormalized : TxContextNormalized tx) (hcalldataSizeFits : TxCalldataSizeFitsEvm tx) : diff --git a/Compiler/Proofs/IRGeneration/FunctionBody.lean b/Compiler/Proofs/IRGeneration/FunctionBody.lean index 2b619233f..ab19651c1 100644 --- a/Compiler/Proofs/IRGeneration/FunctionBody.lean +++ b/Compiler/Proofs/IRGeneration/FunctionBody.lean @@ -970,6 +970,10 @@ theorem decodeSupportedParamWord_lt_evmModulus simp [SourceSemantics.decodeSupportedParamWord] at hdecode | fixedArray _ _ => simp [SourceSemantics.decodeSupportedParamWord] at hdecode + | adt _ _ => + simp [SourceSemantics.decodeSupportedParamWord] at hdecode + | newtypeOf _ _ => + simp [SourceSemantics.decodeSupportedParamWord] at hdecode | bytes => simp [SourceSemantics.decodeSupportedParamWord] at hdecode @@ -7133,12 +7137,18 @@ inductive StmtCompileCore : Stmt → Prop where ExprCompileCore value → StmtCompileCore (.return value) | stop : StmtCompileCore .stop + | mstore {offset value : Expr} : + ExprCompileCore offset → ExprCompileCore value → + StmtCompileCore (.mstore offset value) + | tstore {offset value : Expr} : + ExprCompileCore offset → ExprCompileCore value → + StmtCompileCore (.tstore offset value) theorem compileStmt_core_ok {fields : List Field} {stmt : Stmt} (hcore : StmtCompileCore stmt) : - ∃ bodyIR, CompilationModel.compileStmt fields [] [] .calldata [] false [] stmt = Except.ok bodyIR := by + ∃ bodyIR, CompilationModel.compileStmt fields [] [] .calldata [] false [] [] stmt = Except.ok bodyIR := by cases hcore with | letVar hvalue => rename_i name value @@ -7168,6 +7178,20 @@ theorem compileStmt_core_ok exact ⟨[YulStmt.expr (YulExpr.call "stop" [])], by rw [CompilationModel.compileStmt] rfl⟩ + | mstore hoffset hvalue => + rename_i offset value + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + exact ⟨[YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])], by + rw [CompilationModel.compileStmt, hoffsetIR, hvalueIR] + rfl⟩ + | tstore hoffset hvalue => + rename_i offset value + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + exact ⟨[YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])], by + rw [CompilationModel.compileStmt, hoffsetIR, hvalueIR] + rfl⟩ theorem runtimeStateMatchesIR_setBothMemory {fields : List Field} @@ -7409,7 +7433,7 @@ theorem exec_compileStmt_letVar_core (hpresent : exprBoundNamesPresent value runtime.bindings) (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, - CompilationModel.compileStmt fields [] [] .calldata [] false [] (.letVar name value) = Except.ok bodyIR ∧ + CompilationModel.compileStmt fields [] [] .calldata [] false [] [] (.letVar name value) = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmt fields runtime (.letVar name value) let irExec := execIRStmts (bodyIR.length + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -7452,7 +7476,7 @@ theorem exec_compileStmt_assignVar_core (hpresent : exprBoundNamesPresent value runtime.bindings) (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, - CompilationModel.compileStmt fields [] [] .calldata [] false [] (.assignVar name value) = Except.ok bodyIR ∧ + CompilationModel.compileStmt fields [] [] .calldata [] false [] [] (.assignVar name value) = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmt fields runtime (.assignVar name value) let irExec := execIRStmts (bodyIR.length + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -7485,7 +7509,7 @@ theorem exec_compileStmt_return_core (hpresent : exprBoundNamesPresent value runtime.bindings) (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, - CompilationModel.compileStmt fields [] [] .calldata [] false [] (.return value) = Except.ok bodyIR ∧ + CompilationModel.compileStmt fields [] [] .calldata [] false [] [] (.return value) = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmt fields runtime (.return value) let irExec := execIRStmts (bodyIR.length + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -7521,7 +7545,7 @@ theorem exec_compileStmt_return_core_extraFuel (hpresent : exprBoundNamesPresent value runtime.bindings) (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, - CompilationModel.compileStmt fields [] [] .calldata [] false [] (.return value) = Except.ok bodyIR ∧ + CompilationModel.compileStmt fields [] [] .calldata [] false [] [] (.return value) = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmt fields runtime (.return value) let irExec := execIRStmts (bodyIR.length + extraFuel + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -7569,7 +7593,7 @@ theorem exec_compileStmt_stop_core (hbounded : bindingsBounded runtime.bindings) (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, - CompilationModel.compileStmt fields [] [] .calldata [] false [] .stop = Except.ok bodyIR ∧ + CompilationModel.compileStmt fields [] [] .calldata [] false [] [] .stop = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmt fields runtime .stop let irExec := execIRStmts (bodyIR.length + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -7600,7 +7624,7 @@ theorem exec_compileStmt_stop_core_extraFuel (hbounded : bindingsBounded runtime.bindings) (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, - CompilationModel.compileStmt fields [] [] .calldata [] false [] .stop = Except.ok bodyIR ∧ + CompilationModel.compileStmt fields [] [] .calldata [] false [] [] .stop = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmt fields runtime .stop let irExec := execIRStmts (bodyIR.length + extraFuel + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -7798,7 +7822,7 @@ theorem compileStmt_core_ok_any_scope (hcore : StmtCompileCore stmt) : ∃ bodyIR, CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames stmt = Except.ok bodyIR := by + fields [] [] .calldata [] false inScopeNames [] stmt = Except.ok bodyIR := by cases hcore with | letVar hvalue => rename_i name value @@ -7828,6 +7852,20 @@ theorem compileStmt_core_ok_any_scope exact ⟨[YulStmt.expr (YulExpr.call "stop" [])], by rw [CompilationModel.compileStmt] rfl⟩ + | mstore hoffset hvalue => + rename_i offset value + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + exact ⟨[YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])], by + rw [CompilationModel.compileStmt, hoffsetIR, hvalueIR] + rfl⟩ + | tstore hoffset hvalue => + rename_i offset value + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + exact ⟨[YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])], by + rw [CompilationModel.compileStmt, hoffsetIR, hvalueIR] + rfl⟩ /-! ### Scope-independence of compileStmt / compileStmtList success @@ -7842,15 +7880,15 @@ private theorem compileStmt_ok_any_scope_aux (fields : List Field) : (∀ (stmt : Stmt) (scope1 scope2 : List String), sizeOf stmt < n → - (∃ ir, CompilationModel.compileStmt fields [] [] .calldata [] false scope1 stmt = + (∃ ir, CompilationModel.compileStmt fields [] [] .calldata [] false scope1 [] stmt = Except.ok ir) → - ∃ ir', CompilationModel.compileStmt fields [] [] .calldata [] false scope2 stmt = + ∃ ir', CompilationModel.compileStmt fields [] [] .calldata [] false scope2 [] stmt = Except.ok ir') ∧ (∀ (stmts : List Stmt) (scope1 scope2 : List String), sizeOf stmts < n → - (∃ ir, CompilationModel.compileStmtList fields [] [] .calldata [] false scope1 stmts = + (∃ ir, CompilationModel.compileStmtList fields [] [] .calldata [] false scope1 [] stmts = Except.ok ir) → - ∃ ir', CompilationModel.compileStmtList fields [] [] .calldata [] false scope2 stmts = + ∃ ir', CompilationModel.compileStmtList fields [] [] .calldata [] false scope2 [] stmts = Except.ok ir') := by induction n with | zero => exact ⟨fun _ _ _ h => absurd h (Nat.not_lt_zero _), @@ -7868,12 +7906,12 @@ private theorem compileStmt_ok_any_scope_aux | ok condIR => simp only [hcond] at hir ⊢ cases hthen1 : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope1 thenBranch with + fields [] [] .calldata [] false scope1 [] thenBranch with | error e => simp [hthen1] at hir | ok thenIR1 => simp only [hthen1] at hir cases helse1 : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope1 elseBranch with + fields [] [] .calldata [] false scope1 [] elseBranch with | error e => simp [helse1] at hir | ok elseIR1 => rcases ih.2 thenBranch scope1 scope2 @@ -7892,7 +7930,7 @@ private theorem compileStmt_ok_any_scope_aux | ok countIR => simp only [hcount] at hir ⊢ cases hbody1 : CompilationModel.compileStmtList - fields [] [] .calldata [] false (varName :: scope1) body with + fields [] [] .calldata [] false (varName :: scope1) [] body with | error e => simp [hbody1] at hir | ok bodyIR1 => rcases ih.2 body (varName :: scope1) (varName :: scope2) @@ -7900,6 +7938,16 @@ private theorem compileStmt_ok_any_scope_aux with ⟨bodyIR2, hbody2⟩ simp only [hbody2] exact ⟨_, rfl⟩ + | unsafeBlock _ body => + rcases hok with ⟨ir, hir⟩ + simp only [CompilationModel.compileStmt] at hir ⊢ + rcases ih.2 body scope1 scope2 + (by simp [Stmt.unsafeBlock.sizeOf_spec] at hlt; omega) ⟨ir, hir⟩ + with ⟨bodyIR2, hbody2⟩ + exact ⟨bodyIR2, hbody2⟩ + | matchAdt adtName scrutinee branches => + rcases hok with ⟨ir, hir⟩ + simp [CompilationModel.compileStmt, lookupAdtTypeDef, Except.bind, bind] at hir -- All remaining cases: inScopeNames is unused, so the result is identical | letVar | assignVar | setStorage | setStorageAddr | storageArrayPush | storageArrayPop | setStorageArrayElement | setMapping | setMappingWord @@ -7908,7 +7956,7 @@ private theorem compileStmt_ok_any_scope_aux | requireError | revertError | «return» | returnValues | returnArray | returnBytes | returnStorageWords | mstore | tstore | calldatacopy | returndataCopy | revertReturndata | stop | emit | internalCall - | internalCallAssign | externalCallBind | ecm | rawLog => + | internalCallAssign | externalCallBind | tryExternalCallBind | ecm | rawLog => simp only [CompilationModel.compileStmt] at hok ⊢; exact hok · -- compileStmtList part intro stmts scope1 scope2 hlt hok @@ -7918,12 +7966,12 @@ private theorem compileStmt_ok_any_scope_aux rcases hok with ⟨ir, hir⟩ simp only [CompilationModel.compileStmtList, bind, Except.bind] at hir ⊢ cases hs1 : CompilationModel.compileStmt - fields [] [] .calldata [] false scope1 s with + fields [] [] .calldata [] false scope1 [] s with | error e => simp [hs1] at hir | ok headIR1 => simp only [hs1] at hir cases hss1 : CompilationModel.compileStmtList - fields [] [] .calldata [] false (collectStmtNames s ++ scope1) ss with + fields [] [] .calldata [] false (collectStmtNames s ++ scope1) [] ss with | error e => simp [hss1] at hir | ok tailIR1 => rcases ih.1 s scope1 scope2 (by simp [List.cons.sizeOf_spec] at hlt; omega) @@ -7939,9 +7987,9 @@ theorem compileStmt_ok_any_scope {scope1 scope2 : List String} {stmt : Stmt} (hok : ∃ ir, CompilationModel.compileStmt - fields [] [] .calldata [] false scope1 stmt = Except.ok ir) : + fields [] [] .calldata [] false scope1 [] stmt = Except.ok ir) : ∃ ir', CompilationModel.compileStmt - fields [] [] .calldata [] false scope2 stmt = Except.ok ir' := + fields [] [] .calldata [] false scope2 [] stmt = Except.ok ir' := (compileStmt_ok_any_scope_aux (sizeOf stmt + 1) fields).1 stmt scope1 scope2 (Nat.lt_succ_of_le (Nat.le_refl _)) hok @@ -7952,15 +8000,15 @@ private theorem compileStmt_ok_any_scope_with_surface_aux (errors : List ErrorDef) : (∀ (stmt : Stmt) (scope1 scope2 : List String), sizeOf stmt < n → - (∃ ir, CompilationModel.compileStmt fields events errors .calldata [] false scope1 stmt = + (∃ ir, CompilationModel.compileStmt fields events errors .calldata [] false scope1 [] stmt = Except.ok ir) → - ∃ ir', CompilationModel.compileStmt fields events errors .calldata [] false scope2 stmt = + ∃ ir', CompilationModel.compileStmt fields events errors .calldata [] false scope2 [] stmt = Except.ok ir') ∧ (∀ (stmts : List Stmt) (scope1 scope2 : List String), sizeOf stmts < n → - (∃ ir, CompilationModel.compileStmtList fields events errors .calldata [] false scope1 stmts = + (∃ ir, CompilationModel.compileStmtList fields events errors .calldata [] false scope1 [] stmts = Except.ok ir) → - ∃ ir', CompilationModel.compileStmtList fields events errors .calldata [] false scope2 stmts = + ∃ ir', CompilationModel.compileStmtList fields events errors .calldata [] false scope2 [] stmts = Except.ok ir') := by induction n with | zero => exact ⟨fun _ _ _ h => absurd h (Nat.not_lt_zero _), @@ -7977,12 +8025,12 @@ private theorem compileStmt_ok_any_scope_with_surface_aux | ok condIR => simp only [hcond] at hir ⊢ cases hthen1 : CompilationModel.compileStmtList - fields events errors .calldata [] false scope1 thenBranch with + fields events errors .calldata [] false scope1 [] thenBranch with | error e => simp [hthen1] at hir | ok thenIR1 => simp only [hthen1] at hir cases helse1 : CompilationModel.compileStmtList - fields events errors .calldata [] false scope1 elseBranch with + fields events errors .calldata [] false scope1 [] elseBranch with | error e => simp [helse1] at hir | ok elseIR1 => rcases ih.2 thenBranch scope1 scope2 @@ -8001,7 +8049,7 @@ private theorem compileStmt_ok_any_scope_with_surface_aux | ok countIR => simp only [hcount] at hir ⊢ cases hbody1 : CompilationModel.compileStmtList - fields events errors .calldata [] false (varName :: scope1) body with + fields events errors .calldata [] false (varName :: scope1) [] body with | error e => simp [hbody1] at hir | ok bodyIR1 => rcases ih.2 body (varName :: scope1) (varName :: scope2) @@ -8009,6 +8057,15 @@ private theorem compileStmt_ok_any_scope_with_surface_aux with ⟨bodyIR2, hbody2⟩ simp only [hbody2] exact ⟨_, rfl⟩ + | unsafeBlock reason body => + rcases hok with ⟨ir, hir⟩ + simp only [CompilationModel.compileStmt] at hir ⊢ + exact ih.2 body scope1 scope2 + (by simp [Stmt.unsafeBlock.sizeOf_spec] at hlt; omega) + ⟨ir, hir⟩ + | matchAdt adtName scrutinee branches => + rcases hok with ⟨ir, hir⟩ + simp [CompilationModel.compileStmt, lookupAdtTypeDef, Except.bind, bind] at hir | letVar | assignVar | setStorage | setStorageAddr | storageArrayPush | storageArrayPop | setStorageArrayElement | setMapping | setMappingWord | setMappingPackedWord | setMapping2 | setMapping2Word | setMappingUint @@ -8016,7 +8073,8 @@ private theorem compileStmt_ok_any_scope_with_surface_aux | requireError | revertError | «return» | returnValues | returnArray | returnBytes | returnStorageWords | mstore | tstore | calldatacopy | returndataCopy | revertReturndata | stop | emit | internalCall - | internalCallAssign | externalCallBind | ecm | rawLog => + | internalCallAssign | externalCallBind | tryExternalCallBind + | ecm | rawLog => simp only [CompilationModel.compileStmt] at hok ⊢; exact hok · intro stmts scope1 scope2 hlt hok cases stmts with @@ -8025,12 +8083,12 @@ private theorem compileStmt_ok_any_scope_with_surface_aux rcases hok with ⟨ir, hir⟩ simp only [CompilationModel.compileStmtList, bind, Except.bind] at hir ⊢ cases hs1 : CompilationModel.compileStmt - fields events errors .calldata [] false scope1 s with + fields events errors .calldata [] false scope1 [] s with | error e => simp [hs1] at hir | ok headIR1 => simp only [hs1] at hir cases hss1 : CompilationModel.compileStmtList - fields events errors .calldata [] false (collectStmtNames s ++ scope1) ss with + fields events errors .calldata [] false (collectStmtNames s ++ scope1) [] ss with | error e => simp [hss1] at hir | ok tailIR1 => rcases ih.1 s scope1 scope2 (by simp [List.cons.sizeOf_spec] at hlt; omega) @@ -8048,9 +8106,9 @@ theorem compileStmt_ok_any_scope_with_surface {scope1 scope2 : List String} {stmt : Stmt} (hok : ∃ ir, CompilationModel.compileStmt - fields events errors .calldata [] false scope1 stmt = Except.ok ir) : + fields events errors .calldata [] false scope1 [] stmt = Except.ok ir) : ∃ ir', CompilationModel.compileStmt - fields events errors .calldata [] false scope2 stmt = Except.ok ir' := + fields events errors .calldata [] false scope2 [] stmt = Except.ok ir' := (compileStmt_ok_any_scope_with_surface_aux (sizeOf stmt + 1) fields events errors).1 stmt scope1 scope2 (Nat.lt_succ_of_le (Nat.le_refl _)) hok @@ -8061,9 +8119,9 @@ theorem compileStmtList_ok_any_scope_with_surface {scope1 scope2 : List String} {stmts : List Stmt} (hok : ∃ ir, CompilationModel.compileStmtList - fields events errors .calldata [] false scope1 stmts = Except.ok ir) : + fields events errors .calldata [] false scope1 [] stmts = Except.ok ir) : ∃ ir', CompilationModel.compileStmtList - fields events errors .calldata [] false scope2 stmts = Except.ok ir' := + fields events errors .calldata [] false scope2 [] stmts = Except.ok ir' := (compileStmt_ok_any_scope_with_surface_aux (sizeOf stmts + 1) fields events errors).2 stmts scope1 scope2 (Nat.lt_succ_of_le (Nat.le_refl _)) hok @@ -8072,9 +8130,9 @@ theorem compileStmtList_ok_any_scope {scope1 scope2 : List String} {stmts : List Stmt} (hok : ∃ ir, CompilationModel.compileStmtList - fields [] [] .calldata [] false scope1 stmts = Except.ok ir) : + fields [] [] .calldata [] false scope1 [] stmts = Except.ok ir) : ∃ ir', CompilationModel.compileStmtList - fields [] [] .calldata [] false scope2 stmts = Except.ok ir' := + fields [] [] .calldata [] false scope2 [] stmts = Except.ok ir' := (compileStmt_ok_any_scope_aux (sizeOf stmts + 1) fields).2 stmts scope1 scope2 (Nat.lt_succ_of_le (Nat.le_refl _)) hok @@ -8088,13 +8146,13 @@ theorem compileStmtList_cons_ok_of_compileStmt_ok_with_surface {headIR tailIR : List YulStmt} (hhead : CompilationModel.compileStmt - fields events errors .calldata [] false inScopeNames stmt = Except.ok headIR) + fields events errors .calldata [] false inScopeNames [] stmt = Except.ok headIR) (htail : CompilationModel.compileStmtList fields events errors .calldata [] false - (collectStmtNames stmt ++ inScopeNames) rest = Except.ok tailIR) : + (collectStmtNames stmt ++ inScopeNames) [] rest = Except.ok tailIR) : CompilationModel.compileStmtList - fields events errors .calldata [] false inScopeNames (stmt :: rest) = + fields events errors .calldata [] false inScopeNames [] (stmt :: rest) = Except.ok (headIR ++ tailIR) := by rw [CompilationModel.compileStmtList, hhead] dsimp @@ -8109,13 +8167,13 @@ theorem compileStmtList_cons_ok_of_compileStmt_ok {headIR tailIR : List YulStmt} (hhead : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames stmt = Except.ok headIR) + fields [] [] .calldata [] false inScopeNames [] stmt = Except.ok headIR) (htail : CompilationModel.compileStmtList fields [] [] .calldata [] false - (collectStmtNames stmt ++ inScopeNames) rest = Except.ok tailIR) : + (collectStmtNames stmt ++ inScopeNames) [] rest = Except.ok tailIR) : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames (stmt :: rest) = + fields [] [] .calldata [] false inScopeNames [] (stmt :: rest) = Except.ok (headIR ++ tailIR) := by rw [CompilationModel.compileStmtList, hhead] dsimp @@ -8124,31 +8182,34 @@ theorem compileStmtList_cons_ok_of_compileStmt_ok theorem compileStmtList_cons_ok_inv {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} + {adtTypes : List AdtTypeDef} {stmt : Stmt} {rest : List Stmt} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames (stmt :: rest) = + fields events errors .calldata [] false inScopeNames adtTypes (stmt :: rest) = Except.ok bodyIR) : ∃ headIR tailIR, CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames stmt = Except.ok headIR ∧ + fields events errors .calldata [] false inScopeNames adtTypes stmt = Except.ok headIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false - (collectStmtNames stmt ++ inScopeNames) rest = Except.ok tailIR ∧ + fields events errors .calldata [] false + (collectStmtNames stmt ++ inScopeNames) adtTypes rest = Except.ok tailIR ∧ bodyIR = headIR ++ tailIR := by rw [CompilationModel.compileStmtList] at hcompile cases hhead : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames stmt with + fields events errors .calldata [] false inScopeNames adtTypes stmt with | error err => simp [hhead] at hcompile cases hcompile | ok headIR => cases htail : CompilationModel.compileStmtList - fields [] [] .calldata [] false - (collectStmtNames stmt ++ inScopeNames) rest with + fields events errors .calldata [] false + (collectStmtNames stmt ++ inScopeNames) adtTypes rest with | error err => simp [hhead, htail] at hcompile cases hcompile @@ -8169,14 +8230,14 @@ theorem compileStmt_terminal_ite_ok_inv (helseNonempty : elseBranch.isEmpty = false) (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames + fields [] [] .calldata [] false inScopeNames [] (.ite cond thenBranch elseBranch) = Except.ok bodyIR) : ∃ condIR thenIR elseIR tempName, CompilationModel.compileExpr fields .calldata cond = Except.ok condIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames thenBranch = Except.ok thenIR ∧ + fields [] [] .calldata [] false inScopeNames [] thenBranch = Except.ok thenIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames elseBranch = Except.ok elseIR ∧ + fields [] [] .calldata [] false inScopeNames [] elseBranch = Except.ok elseIR ∧ tempName = CompilationModel.pickFreshName "__ite_cond" (inScopeNames ++ collectExprNames cond ++ @@ -8193,13 +8254,13 @@ theorem compileStmt_terminal_ite_ok_inv cases hcompile | ok condIR => cases hthen : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames thenBranch with + fields [] [] .calldata [] false inScopeNames [] thenBranch with | error err => simp [hcond, hthen] at hcompile cases hcompile | ok thenIR => cases helse : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames elseBranch with + fields [] [] .calldata [] false inScopeNames [] elseBranch with | error err => simp [hcond, hthen, helse] at hcompile cases hcompile @@ -8225,17 +8286,17 @@ theorem compileStmtList_terminal_ite_ok_inv (helseNonempty : elseBranch.isEmpty = false) (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames + fields [] [] .calldata [] false inScopeNames [] (.ite cond thenBranch elseBranch :: rest) = Except.ok bodyIR) : ∃ condIR thenIR elseIR tailIR tempName, CompilationModel.compileExpr fields .calldata cond = Except.ok condIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames thenBranch = Except.ok thenIR ∧ + fields [] [] .calldata [] false inScopeNames [] thenBranch = Except.ok thenIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames elseBranch = Except.ok elseIR ∧ + fields [] [] .calldata [] false inScopeNames [] elseBranch = Except.ok elseIR ∧ CompilationModel.compileStmtList fields [] [] .calldata [] false - (collectStmtNames (.ite cond thenBranch elseBranch) ++ inScopeNames) rest = + (collectStmtNames (.ite cond thenBranch elseBranch) ++ inScopeNames) [] rest = Except.ok tailIR ∧ tempName = CompilationModel.pickFreshName "__ite_cond" @@ -8274,7 +8335,7 @@ theorem compileStmtList_core_ok (hcore : StmtListCompileCore scope stmts) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR := by + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR := by induction hcore generalizing inScopeNames case nil => exact ⟨[], rfl⟩ @@ -8328,6 +8389,26 @@ theorem compileStmtList_core_ok dsimp rw [htailIR] rfl + case mstore scope offset value rest hoffset _ hvalue _ hrest ih => + rcases compileStmt_core_ok_any_scope (fields := fields) (inScopeNames := inScopeNames) + (stmt := .mstore offset value) (.mstore hoffset hvalue) with ⟨headIR, hheadIR⟩ + rcases ih (inScopeNames := collectStmtNames (.mstore offset value) ++ inScopeNames) with + ⟨tailIR, htailIR⟩ + refine ⟨headIR ++ tailIR, ?_⟩ + rw [CompilationModel.compileStmtList, hheadIR] + dsimp + rw [htailIR] + rfl + case tstore scope offset value rest hoffset _ hvalue _ hrest ih => + rcases compileStmt_core_ok_any_scope (fields := fields) (inScopeNames := inScopeNames) + (stmt := .tstore offset value) (.tstore hoffset hvalue) with ⟨headIR, hheadIR⟩ + rcases ih (inScopeNames := collectStmtNames (.tstore offset value) ++ inScopeNames) with + ⟨tailIR, htailIR⟩ + refine ⟨headIR ++ tailIR, ?_⟩ + rw [CompilationModel.compileStmtList, hheadIR] + dsimp + rw [htailIR] + rfl theorem compileStmtList_terminal_core_ok {fields : List Field} @@ -8336,7 +8417,7 @@ theorem compileStmtList_terminal_core_ok (hterminal : StmtListTerminalCore scope stmts) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR := by + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR := by induction hterminal generalizing inScopeNames case letVar scope name value rest hvalue _ hrest ih => rcases compileStmt_core_ok_any_scope (fields := fields) (inScopeNames := inScopeNames) @@ -8394,6 +8475,26 @@ theorem compileStmtList_terminal_core_ok dsimp rw [htailIR] rfl + case mstore scope offset value rest hoffset _ hvalue _ hrest ih => + rcases compileStmt_core_ok_any_scope (fields := fields) (inScopeNames := inScopeNames) + (stmt := .mstore offset value) (.mstore hoffset hvalue) with ⟨headIR, hheadIR⟩ + rcases ih (inScopeNames := collectStmtNames (.mstore offset value) ++ inScopeNames) with + ⟨tailIR, htailIR⟩ + refine ⟨headIR ++ tailIR, ?_⟩ + rw [CompilationModel.compileStmtList, hheadIR] + dsimp + rw [htailIR] + rfl + case tstore scope offset value rest hoffset _ hvalue _ hrest ih => + rcases compileStmt_core_ok_any_scope (fields := fields) (inScopeNames := inScopeNames) + (stmt := .tstore offset value) (.tstore hoffset hvalue) with ⟨headIR, hheadIR⟩ + rcases ih (inScopeNames := collectStmtNames (.tstore offset value) ++ inScopeNames) with + ⟨tailIR, htailIR⟩ + refine ⟨headIR ++ tailIR, ?_⟩ + rw [CompilationModel.compileStmtList, hheadIR] + dsimp + rw [htailIR] + rfl case ite scope cond thenBranch elseBranch rest hcond _ hthen helse hrest ihThen ihElse => rcases compileExpr_core_ok (fields := fields) hcond with ⟨condIR, hcondIR⟩ rcases ihThen (inScopeNames := inScopeNames) with ⟨thenIR, hthenIR⟩ @@ -8447,7 +8548,7 @@ theorem compileStmtList_terminal_core_ok_nonempty (hterminal : StmtListTerminalCore scope stmts) (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR) : + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR) : bodyIR ≠ [] := by induction hterminal generalizing inScopeNames bodyIR with | letVar hvalue hinScope hrest ih => @@ -8499,6 +8600,28 @@ theorem compileStmtList_terminal_core_ok_nonempty injection hhead with hheadEq subst hheadEq simp [hbody] + | mstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + rcases compileExpr_core_ok (fields := fields) hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok (fields := fields) hvalue with ⟨valueIR, hvalueIR⟩ + rcases compileStmtList_cons_ok_inv (fields := fields) (inScopeNames := inScopeNames) + (stmt := .mstore offset value) (rest := rest) hcompile with + ⟨headIR, tailIR, hhead, _, hbody⟩ + rw [CompilationModel.compileStmt, hoffsetIR, hvalueIR] at hhead + injection hhead with hheadEq + subst hheadEq + simp [hbody] + | tstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + rcases compileExpr_core_ok (fields := fields) hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok (fields := fields) hvalue with ⟨valueIR, hvalueIR⟩ + rcases compileStmtList_cons_ok_inv (fields := fields) (inScopeNames := inScopeNames) + (stmt := .tstore offset value) (rest := rest) hcompile with + ⟨headIR, tailIR, hhead, _, hbody⟩ + rw [CompilationModel.compileStmt, hoffsetIR, hvalueIR] at hhead + injection hhead with hheadEq + subst hheadEq + simp [hbody] | ite hcond hinScope hthen helse hrest ihThen ihElse => rename_i scope cond thenBranch elseBranch rest rcases compileStmtList_cons_ok_inv (fields := fields) (inScopeNames := inScopeNames) @@ -8525,14 +8648,14 @@ theorem compileStmtList_terminal_core_ok_nonempty | ok condIR => cases hthenIR : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames thenBranch with + fields [] [] .calldata [] false inScopeNames [] thenBranch with | error err => rw [hthenOk] at hthenIR cases hthenIR | ok thenIR => cases helseIR : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames elseBranch with + fields [] [] .calldata [] false inScopeNames [] elseBranch with | error err => rw [helseOk] at helseIR cases helseIR @@ -9664,7 +9787,7 @@ theorem exec_compileStmtList_core (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtList fields runtime stmts let irExec := execIRStmts (bodyIR.length + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -9944,6 +10067,128 @@ theorem exec_compileStmtList_core rw [SourceSemantics.execStmtList, SourceSemantics.execStmt] simp [hirExec] exact ⟨hruntime, ⟨hexact, hbounded⟩⟩ + | mstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + have hpresentOffset : exprBoundNamesPresent offset runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeOffset + have hpresentValue : exprBoundNamesPresent value runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeValue + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + have hevalOffset := eval_compileExpr_core hoffset hexact hbounded hpresentOffset hruntime + rw [hoffsetIR] at hevalOffset; simp [Except.toOption] at hevalOffset + have hevalValue := eval_compileExpr_core hvalue hexact hbounded hpresentValue hruntime + rw [hvalueIR] at hevalValue; simp [Except.toOption] at hevalValue + rcases hIROffset : evalIRExpr state offsetIR with _ | offsetNat + · simp [hIROffset, Option.bind] at hevalOffset + · simp [hIROffset, Option.bind] at hevalOffset + rcases hIRValue : evalIRExpr state valueIR with _ | valueNat + · simp [hIRValue, Option.bind] at hevalValue + · simp [hIRValue, Option.bind] at hevalValue + have hOffsetSrc : SourceSemantics.evalExpr fields runtime offset = some offsetNat := + hevalOffset.symm + have hValueSrc : SourceSemantics.evalExpr fields runtime value = some valueNat := + hevalValue.symm + let runtime' := + { runtime with + world := { + runtime.world with + memory := fun o => if o = offsetNat then valueNat else runtime.world.memory o + } } + let state' := { state with memory := fun o => if o = offsetNat then valueNat else state.memory o } + have hvalueLt := evalExpr_lt_evmModulus_core_onExpr hvalue + (bindingsExactlyMatchIRVars_implies_onExpr hexact) hbounded hpresentValue hruntime + rw [hValueSrc] at hvalueLt + have hruntime' : runtimeStateMatchesIR fields runtime' state' := + runtimeStateMatchesIR_setBothMemory hruntime offsetNat valueNat hvalueLt + have hexact' : bindingsExactlyMatchIRVars runtime'.bindings state' := + bindingsExactlyMatchIRVars_setMemory hexact offsetNat valueNat + have hbounded' : bindingsBounded runtime'.bindings := by + simpa [runtime'] using hbounded + rcases ih (runtime := runtime') (state := state') + (inScopeNames := collectStmtNames (.mstore offset value) ++ inScopeNames) + hscope hexact' hbounded' hruntime' with + ⟨tailIR, htailCompile, htailSem, htailExact⟩ + refine ⟨[YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])] ++ tailIR, ?_, ?_⟩ + · unfold CompilationModel.compileStmtList CompilationModel.compileStmt + rw [hoffsetIR, hvalueIR] + simp [htailCompile] + exact rfl + · have hstmt : + execIRStmt (tailIR.length + 1) state + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])) = .continue state' := by + simp [execIRStmt, evalIRExprs, hIROffset, hIRValue, state'] + have hirExec : + execIRStmts (tailIR.length + 2) state + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR]) :: tailIR) = + execIRStmts (tailIR.length + 1) state' tailIR := by + simpa using + (execIRStmts_cons_of_execIRStmt_continue state state' + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])) tailIR hstmt) + rw [SourceSemantics.execStmtList, SourceSemantics.execStmt, hOffsetSrc, hValueSrc] + simp [hirExec] + exact ⟨htailSem, htailExact⟩ + | tstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + have hpresentOffset : exprBoundNamesPresent offset runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeOffset + have hpresentValue : exprBoundNamesPresent value runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeValue + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + have hevalOffset := eval_compileExpr_core hoffset hexact hbounded hpresentOffset hruntime + rw [hoffsetIR] at hevalOffset; simp [Except.toOption] at hevalOffset + have hevalValue := eval_compileExpr_core hvalue hexact hbounded hpresentValue hruntime + rw [hvalueIR] at hevalValue; simp [Except.toOption] at hevalValue + rcases hIROffset : evalIRExpr state offsetIR with _ | offsetNat + · simp [hIROffset, Option.bind] at hevalOffset + · simp [hIROffset, Option.bind] at hevalOffset + rcases hIRValue : evalIRExpr state valueIR with _ | valueNat + · simp [hIRValue, Option.bind] at hevalValue + · simp [hIRValue, Option.bind] at hevalValue + have hOffsetSrc : SourceSemantics.evalExpr fields runtime offset = some offsetNat := + hevalOffset.symm + have hValueSrc : SourceSemantics.evalExpr fields runtime value = some valueNat := + hevalValue.symm + let runtime' := + { runtime with + world := { + runtime.world with + transientStorage := fun o => if o = offsetNat then valueNat else runtime.world.transientStorage o + } } + let state' := { state with transientStorage := fun o => if o = offsetNat then valueNat else state.transientStorage o } + have hvalueLt := evalExpr_lt_evmModulus_core_onExpr hvalue + (bindingsExactlyMatchIRVars_implies_onExpr hexact) hbounded hpresentValue hruntime + rw [hValueSrc] at hvalueLt + have hruntime' : runtimeStateMatchesIR fields runtime' state' := + runtimeStateMatchesIR_setTransientStorage hruntime offsetNat valueNat hvalueLt + have hexact' : bindingsExactlyMatchIRVars runtime'.bindings state' := by + intro name; simpa [IRState.getVar, state'] using hexact name + have hbounded' : bindingsBounded runtime'.bindings := by + simpa [runtime'] using hbounded + rcases ih (runtime := runtime') (state := state') + (inScopeNames := collectStmtNames (.tstore offset value) ++ inScopeNames) + hscope hexact' hbounded' hruntime' with + ⟨tailIR, htailCompile, htailSem, htailExact⟩ + refine ⟨[YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])] ++ tailIR, ?_, ?_⟩ + · unfold CompilationModel.compileStmtList CompilationModel.compileStmt + rw [hoffsetIR, hvalueIR] + simp [htailCompile] + exact rfl + · have hstmt : + execIRStmt (tailIR.length + 1) state + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])) = .continue state' := by + simp [execIRStmt, evalIRExprs, hIROffset, hIRValue, state'] + have hirExec : + execIRStmts (tailIR.length + 2) state + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR]) :: tailIR) = + execIRStmts (tailIR.length + 1) state' tailIR := by + simpa using + (execIRStmts_cons_of_execIRStmt_continue state state' + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])) tailIR hstmt) + rw [SourceSemantics.execStmtList, SourceSemantics.execStmt, hOffsetSrc, hValueSrc] + simp [hirExec] + exact ⟨htailSem, htailExact⟩ theorem exec_compileStmtList_core_extraFuel {fields : List Field} @@ -9959,7 +10204,7 @@ theorem exec_compileStmtList_core_extraFuel (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtList fields runtime stmts let irExec := execIRStmts (bodyIR.length + extraFuel + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec ∧ @@ -10286,6 +10531,138 @@ theorem exec_compileStmtList_core_extraFuel rw [SourceSemantics.execStmtList, SourceSemantics.execStmt] simp [hirExec'] exact ⟨hruntime, ⟨hexact, hbounded⟩⟩ + | mstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + have hpresentOffset : exprBoundNamesPresent offset runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeOffset + have hpresentValue : exprBoundNamesPresent value runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeValue + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + have hevalOffset := eval_compileExpr_core hoffset hexact hbounded hpresentOffset hruntime + rw [hoffsetIR] at hevalOffset; simp [Except.toOption] at hevalOffset + have hevalValue := eval_compileExpr_core hvalue hexact hbounded hpresentValue hruntime + rw [hvalueIR] at hevalValue; simp [Except.toOption] at hevalValue + rcases hIROffset : evalIRExpr state offsetIR with _ | offsetNat + · simp [hIROffset, Option.bind] at hevalOffset + · simp [hIROffset, Option.bind] at hevalOffset + rcases hIRValue : evalIRExpr state valueIR with _ | valueNat + · simp [hIRValue, Option.bind] at hevalValue + · simp [hIRValue, Option.bind] at hevalValue + have hOffsetSrc : SourceSemantics.evalExpr fields runtime offset = some offsetNat := + hevalOffset.symm + have hValueSrc : SourceSemantics.evalExpr fields runtime value = some valueNat := + hevalValue.symm + let runtime' := + { runtime with + world := { + runtime.world with + memory := fun o => if o = offsetNat then valueNat else runtime.world.memory o + } } + let state' := { state with memory := fun o => if o = offsetNat then valueNat else state.memory o } + have hvalueLt := evalExpr_lt_evmModulus_core_onExpr hvalue + (bindingsExactlyMatchIRVars_implies_onExpr hexact) hbounded hpresentValue hruntime + rw [hValueSrc] at hvalueLt + have hruntime' : runtimeStateMatchesIR fields runtime' state' := + runtimeStateMatchesIR_setBothMemory hruntime offsetNat valueNat hvalueLt + have hexact' : bindingsExactlyMatchIRVars runtime'.bindings state' := + bindingsExactlyMatchIRVars_setMemory hexact offsetNat valueNat + have hbounded' : bindingsBounded runtime'.bindings := by + simpa [runtime'] using hbounded + rcases ih (runtime := runtime') (state := state') + (inScopeNames := collectStmtNames (.mstore offset value) ++ inScopeNames) + hscope hexact' hbounded' hruntime' with + ⟨tailIR, htailCompile, htailSem, htailExact⟩ + refine ⟨[YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])] ++ tailIR, ?_, ?_⟩ + · unfold CompilationModel.compileStmtList CompilationModel.compileStmt + rw [hoffsetIR, hvalueIR] + simp [htailCompile] + exact rfl + · have hstmt : + execIRStmt (tailIR.length + extraFuel + 1) state + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])) = .continue state' := by + simp [execIRStmt, evalIRExprs, hIROffset, hIRValue, state'] + have hirExec : + execIRStmts (tailIR.length + extraFuel + 2) state + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR]) :: tailIR) = + execIRStmts (tailIR.length + extraFuel + 1) state' tailIR := by + simpa using + (execIRStmts_cons_of_execIRStmt_continue_extraFuel extraFuel state state' + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])) tailIR hstmt) + have hirExec' : + execIRStmts (tailIR.length + 1 + extraFuel + 1) state + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR]) :: tailIR) = + execIRStmts (tailIR.length + extraFuel + 1) state' tailIR := by + simpa [Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using hirExec + rw [SourceSemantics.execStmtList, SourceSemantics.execStmt, hOffsetSrc, hValueSrc] + simp [hirExec'] + exact ⟨htailSem, htailExact⟩ + | tstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + have hpresentOffset : exprBoundNamesPresent offset runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeOffset + have hpresentValue : exprBoundNamesPresent value runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeValue + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + have hevalOffset := eval_compileExpr_core hoffset hexact hbounded hpresentOffset hruntime + rw [hoffsetIR] at hevalOffset; simp [Except.toOption] at hevalOffset + have hevalValue := eval_compileExpr_core hvalue hexact hbounded hpresentValue hruntime + rw [hvalueIR] at hevalValue; simp [Except.toOption] at hevalValue + rcases hIROffset : evalIRExpr state offsetIR with _ | offsetNat + · simp [hIROffset, Option.bind] at hevalOffset + · simp [hIROffset, Option.bind] at hevalOffset + rcases hIRValue : evalIRExpr state valueIR with _ | valueNat + · simp [hIRValue, Option.bind] at hevalValue + · simp [hIRValue, Option.bind] at hevalValue + have hOffsetSrc : SourceSemantics.evalExpr fields runtime offset = some offsetNat := + hevalOffset.symm + have hValueSrc : SourceSemantics.evalExpr fields runtime value = some valueNat := + hevalValue.symm + let runtime' := + { runtime with + world := { + runtime.world with + transientStorage := fun o => if o = offsetNat then valueNat else runtime.world.transientStorage o + } } + let state' := { state with transientStorage := fun o => if o = offsetNat then valueNat else state.transientStorage o } + have hvalueLt := evalExpr_lt_evmModulus_core_onExpr hvalue + (bindingsExactlyMatchIRVars_implies_onExpr hexact) hbounded hpresentValue hruntime + rw [hValueSrc] at hvalueLt + have hruntime' : runtimeStateMatchesIR fields runtime' state' := + runtimeStateMatchesIR_setTransientStorage hruntime offsetNat valueNat hvalueLt + have hexact' : bindingsExactlyMatchIRVars runtime'.bindings state' := by + intro name; simpa [IRState.getVar, state'] using hexact name + have hbounded' : bindingsBounded runtime'.bindings := by + simpa [runtime'] using hbounded + rcases ih (runtime := runtime') (state := state') + (inScopeNames := collectStmtNames (.tstore offset value) ++ inScopeNames) + hscope hexact' hbounded' hruntime' with + ⟨tailIR, htailCompile, htailSem, htailExact⟩ + refine ⟨[YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])] ++ tailIR, ?_, ?_⟩ + · unfold CompilationModel.compileStmtList CompilationModel.compileStmt + rw [hoffsetIR, hvalueIR] + simp [htailCompile] + exact rfl + · have hstmt : + execIRStmt (tailIR.length + extraFuel + 1) state + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])) = .continue state' := by + simp [execIRStmt, evalIRExprs, hIROffset, hIRValue, state'] + have hirExec : + execIRStmts (tailIR.length + extraFuel + 2) state + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR]) :: tailIR) = + execIRStmts (tailIR.length + extraFuel + 1) state' tailIR := by + simpa using + (execIRStmts_cons_of_execIRStmt_continue_extraFuel extraFuel state state' + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])) tailIR hstmt) + have hirExec' : + execIRStmts (tailIR.length + 1 + extraFuel + 1) state + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR]) :: tailIR) = + execIRStmts (tailIR.length + extraFuel + 1) state' tailIR := by + simpa [Nat.add_assoc, Nat.add_left_comm, Nat.add_comm] using hirExec + rw [SourceSemantics.execStmtList, SourceSemantics.execStmt, hOffsetSrc, hValueSrc] + simp [hirExec'] + exact ⟨htailSem, htailExact⟩ private theorem compiled_terminal_ite_body_block_extraFuel_eq (extraFuel : Nat) @@ -12264,6 +12641,16 @@ theorem execStmtList_terminal_core_not_continue | stop hrest => intro next simp [SourceSemantics.execStmtList, SourceSemantics.execStmt] + | mstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + intro next + simp only [SourceSemantics.execStmtList, SourceSemantics.execStmt] + cases SourceSemantics.evalExpr fields runtime _ <;> simp_all + cases SourceSemantics.evalExpr fields runtime _ <;> simp_all + | tstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + intro next + simp only [SourceSemantics.execStmtList, SourceSemantics.execStmt] + cases SourceSemantics.evalExpr fields runtime _ <;> simp_all + cases SourceSemantics.evalExpr fields runtime _ <;> simp_all | ite hcond hinScope hthen helse hrest ih_then ih_else => intro next simp only [SourceSemantics.execStmtList, SourceSemantics.execStmt] @@ -13743,7 +14130,7 @@ theorem exec_compileStmtList_terminal_core_sizeOf_extraFuel (hruntime : runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtList fields runtime stmts let irExec := execIRStmts (sizeOf bodyIR + extraFuel + 1) state bodyIR stmtResultMatchesIRExec fields sourceResult irExec := by @@ -13971,6 +14358,142 @@ theorem exec_compileStmtList_terminal_core_sizeOf_extraFuel refine ⟨[YulStmt.expr (YulExpr.call "stop" [])] ++ tailIR, ?_, ?_⟩ · simpa [CompilationModel.compileStmtList, CompilationModel.compileStmt, htailCompile] · exact stmtResultMatchesIRExec_compiled_stop_core_append_wholeFuel hruntime + | mstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + have hpresentOffset : exprBoundNamesPresent offset runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeOffset + have hpresentValue : exprBoundNamesPresent value runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeValue + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + have hevalOffset := eval_compileExpr_core_of_scope hoffset hexact hinScopeOffset hbounded hpresentOffset hruntime + rw [hoffsetIR] at hevalOffset; simp [Except.toOption] at hevalOffset + have hevalValue := eval_compileExpr_core_of_scope hvalue hexact hinScopeValue hbounded hpresentValue hruntime + rw [hvalueIR] at hevalValue; simp [Except.toOption] at hevalValue + rcases hIROffset : evalIRExpr state offsetIR with _ | offsetNat + · simp [hIROffset, Option.bind] at hevalOffset + · simp [hIROffset, Option.bind] at hevalOffset + rcases hIRValue : evalIRExpr state valueIR with _ | valueNat + · simp [hIRValue, Option.bind] at hevalValue + · simp [hIRValue, Option.bind] at hevalValue + have hOffsetSrc : SourceSemantics.evalExpr fields runtime offset = some offsetNat := + hevalOffset.symm + have hValueSrc : SourceSemantics.evalExpr fields runtime value = some valueNat := + hevalValue.symm + let runtime' := + { runtime with + world := { + runtime.world with + memory := fun o => if o = offsetNat then valueNat else runtime.world.memory o + } } + let state' := { state with memory := fun o => if o = offsetNat then valueNat else state.memory o } + have hvalueLt := evalExpr_lt_evmModulus_core_of_scope hvalue hexact hinScopeValue hbounded hpresentValue hruntime + rw [hValueSrc] at hvalueLt; simp at hvalueLt + have hruntime' : runtimeStateMatchesIR fields runtime' state' := + runtimeStateMatchesIR_setBothMemory hruntime offsetNat valueNat hvalueLt + have hexact' : bindingsExactlyMatchIRVarsOnScope scope runtime'.bindings state' := + bindingsExactlyMatchIRVarsOnScope_setMemory hexact offsetNat valueNat + have hbounded' : bindingsBounded runtime'.bindings := by + simpa [runtime'] using hbounded + have hscope' : scopeNamesPresent scope runtime'.bindings := by + simpa [runtime'] using hscope + have hincluded' : scopeNamesIncluded scope + (collectStmtNames (.mstore offset value) ++ inScopeNames) := + scopeNamesIncluded_collectStmtNames_tail hincluded + rcases ih (extraFuel + sizeOf (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR]))) + (runtime := runtime') (state := state') + (inScopeNames := collectStmtNames (.mstore offset value) ++ inScopeNames) + hincluded' hscope' hexact' hbounded' hruntime' with + ⟨tailIR, htailCompile, htailSem⟩ + refine ⟨[YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])] ++ tailIR, ?_, ?_⟩ + · unfold CompilationModel.compileStmtList CompilationModel.compileStmt + rw [hoffsetIR, hvalueIR] + simp [htailCompile] + exact rfl + · have hstmt : + execIRStmt (sizeOf ([YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])] ++ tailIR) + extraFuel) state + (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])) = .continue state' := by + have hfuelNe : sizeOf ([YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])] ++ tailIR) + extraFuel ≠ 0 := + sizeOf_singleton_append_extraFuel_ne_zero _ _ _ + cases hfuel : sizeOf ([YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])] ++ tailIR) + extraFuel with + | zero => exact absurd hfuel hfuelNe + | succ n => simp [execIRStmt, evalIRExprs, hIROffset, hIRValue, state'] + have hirExec := + execIRStmts_singleton_append_of_execIRStmt_continue_wholeFuel + extraFuel state state' (YulStmt.expr (YulExpr.call "mstore" [offsetIR, valueIR])) tailIR hstmt + simp only [SourceSemantics.execStmtList, SourceSemantics.execStmt, hOffsetSrc, hValueSrc, hirExec] + dsimp [runtime', state'] + convert htailSem using 2 + simp + omega + | tstore hoffset hinScopeOffset hvalue hinScopeValue hrest ih => + rename_i scope offset value rest + have hpresentOffset : exprBoundNamesPresent offset runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeOffset + have hpresentValue : exprBoundNamesPresent value runtime.bindings := + exprBoundNamesPresent_of_scope hscope hinScopeValue + rcases compileExpr_core_ok hoffset with ⟨offsetIR, hoffsetIR⟩ + rcases compileExpr_core_ok hvalue with ⟨valueIR, hvalueIR⟩ + have hevalOffset := eval_compileExpr_core_of_scope hoffset hexact hinScopeOffset hbounded hpresentOffset hruntime + rw [hoffsetIR] at hevalOffset; simp [Except.toOption] at hevalOffset + have hevalValue := eval_compileExpr_core_of_scope hvalue hexact hinScopeValue hbounded hpresentValue hruntime + rw [hvalueIR] at hevalValue; simp [Except.toOption] at hevalValue + rcases hIROffset : evalIRExpr state offsetIR with _ | offsetNat + · simp [hIROffset, Option.bind] at hevalOffset + · simp [hIROffset, Option.bind] at hevalOffset + rcases hIRValue : evalIRExpr state valueIR with _ | valueNat + · simp [hIRValue, Option.bind] at hevalValue + · simp [hIRValue, Option.bind] at hevalValue + have hOffsetSrc : SourceSemantics.evalExpr fields runtime offset = some offsetNat := + hevalOffset.symm + have hValueSrc : SourceSemantics.evalExpr fields runtime value = some valueNat := + hevalValue.symm + let runtime' := + { runtime with + world := { + runtime.world with + transientStorage := fun o => if o = offsetNat then valueNat else runtime.world.transientStorage o + } } + let state' := { state with transientStorage := fun o => if o = offsetNat then valueNat else state.transientStorage o } + have hvalueLt := evalExpr_lt_evmModulus_core_of_scope hvalue hexact hinScopeValue hbounded hpresentValue hruntime + rw [hValueSrc] at hvalueLt; simp at hvalueLt + have hruntime' : runtimeStateMatchesIR fields runtime' state' := + runtimeStateMatchesIR_setTransientStorage hruntime offsetNat valueNat hvalueLt + have hexact' : bindingsExactlyMatchIRVarsOnScope scope runtime'.bindings state' := by + intro name hname; simpa [IRState.getVar, state'] using hexact name hname + have hbounded' : bindingsBounded runtime'.bindings := by + simpa [runtime'] using hbounded + have hscope' : scopeNamesPresent scope runtime'.bindings := by + simpa [runtime'] using hscope + have hincluded' : scopeNamesIncluded scope + (collectStmtNames (.tstore offset value) ++ inScopeNames) := + scopeNamesIncluded_collectStmtNames_tail hincluded + rcases ih (extraFuel + sizeOf (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR]))) + (runtime := runtime') (state := state') + (inScopeNames := collectStmtNames (.tstore offset value) ++ inScopeNames) + hincluded' hscope' hexact' hbounded' hruntime' with + ⟨tailIR, htailCompile, htailSem⟩ + refine ⟨[YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])] ++ tailIR, ?_, ?_⟩ + · unfold CompilationModel.compileStmtList CompilationModel.compileStmt + rw [hoffsetIR, hvalueIR] + simp [htailCompile] + exact rfl + · have hstmt : + execIRStmt (sizeOf ([YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])] ++ tailIR) + extraFuel) state + (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])) = .continue state' := by + have hfuelNe : sizeOf ([YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])] ++ tailIR) + extraFuel ≠ 0 := + sizeOf_singleton_append_extraFuel_ne_zero _ _ _ + cases hfuel : sizeOf ([YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])] ++ tailIR) + extraFuel with + | zero => exact absurd hfuel hfuelNe + | succ n => simp [execIRStmt, evalIRExprs, hIROffset, hIRValue, state'] + have hirExec := + execIRStmts_singleton_append_of_execIRStmt_continue_wholeFuel + extraFuel state state' (YulStmt.expr (YulExpr.call "tstore" [offsetIR, valueIR])) tailIR hstmt + simp only [SourceSemantics.execStmtList, SourceSemantics.execStmt, hOffsetSrc, hValueSrc, hirExec] + dsimp [runtime', state'] + convert htailSem using 2 + simp + omega | ite hcond hinScope hthen helse hrest ih_then ih_else => rename_i scope cond thenBranch elseBranch rest have hpresent : exprBoundNamesPresent cond runtime.bindings := diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index e16050760..ca4abbc67 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -112,7 +112,7 @@ structure CompiledStmtStep (stmt : Stmt) (compiledIR : List YulStmt) : Prop where compileOk : - CompilationModel.compileStmt fields [] [] .calldata [] false scope stmt = + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] stmt = Except.ok compiledIR preserves : ∀ (runtime : SourceSemantics.RuntimeState) @@ -138,7 +138,7 @@ structure CompiledStmtStepWithHelpers (stmt : Stmt) (compiledIR : List YulStmt) : Prop where compileOk : - CompilationModel.compileStmt fields spec.events spec.errors .calldata [] false scope stmt = + CompilationModel.compileStmt fields spec.events spec.errors .calldata [] false scope [] stmt = Except.ok compiledIR preserves : ∀ (runtime : SourceSemantics.RuntimeState) @@ -168,7 +168,7 @@ structure CompiledStmtStepWithHelpersAndHelperIR (stmt : Stmt) (compiledIR : List YulStmt) : Prop where compileOk : - CompilationModel.compileStmt fields spec.events spec.errors .calldata [] false scope stmt = + CompilationModel.compileStmt fields spec.events spec.errors .calldata [] false scope [] stmt = Except.ok compiledIR preserves : ∀ (runtime : SourceSemantics.RuntimeState) @@ -310,7 +310,7 @@ inductive StmtListCompiledLegacyCompatible StmtListCompiledLegacyCompatible fields scope [] | cons {scope : List String} {stmt : Stmt} {rest : List Stmt} : (∀ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope stmt = + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] stmt = Except.ok compiledIR → LegacyCompatibleExternalStmtList compiledIR) → StmtListCompiledLegacyCompatible fields (stmtNextScope scope stmt) rest → @@ -327,7 +327,7 @@ inductive StmtListHelperFreeCompiledLegacyCompatible | cons {scope : List String} {stmt : Stmt} {rest : List Stmt} : (stmtTouchesUnsupportedHelperSurface stmt = false → ∀ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope stmt = + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] stmt = Except.ok compiledIR → LegacyCompatibleExternalStmtList compiledIR) → StmtListHelperFreeCompiledLegacyCompatible fields (stmtNextScope scope stmt) rest → @@ -345,7 +345,7 @@ inductive StmtListHelperFreeCompiledCallsDisjoint | cons {scope : List String} {stmt : Stmt} {rest : List Stmt} : (stmtTouchesUnsupportedHelperSurface stmt = false → ∀ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope stmt = + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] stmt = Except.ok compiledIR → YulStmtListCallsDisjointFromInternalTable runtimeContract compiledIR) → StmtListHelperFreeCompiledCallsDisjoint runtimeContract fields (stmtNextScope scope stmt) rest → @@ -608,15 +608,19 @@ private theorem legacyCompatibleExternalStmtList_of_compileSetStorage_ok_of_noPa rcases hve : CompilationModel.compileExpr fields .calldata value with err | valueExpr · simp [hve, Bind.bind, Except.bind] at hcompile · simp only [hve, Except.ok.injEq] at hcompile - cases hslots : f.aliasSlots with - | nil => - simp [hslots, hunpacked] at hcompile; subst hcompile - exact .expr _ [] .nil - | cons s rest => - simp [hslots, hunpacked] at hcompile; subst hcompile - refine .block _ [] (.let_ _ _ _ ?_) .nil - simp only [← List.map_cons, ← List.map_map, ← Function.comp_def] - exact legacyCompatibleExternalStmtList_of_exprStmtExprs _ + cases hty : f.ty with + | adt name maxFields => + simp [hty] at hcompile + | uint256 | address | dynamicArray | mappingTyped | mappingStruct | mappingStruct2 => + cases hslots : f.aliasSlots with + | nil => + simp [hslots, hunpacked, hty] at hcompile; subst hcompile + exact .expr _ [] .nil + | cons s rest => + simp [hslots, hunpacked, hty] at hcompile; subst hcompile + refine .block _ [] (.let_ _ _ _ ?_) .nil + simp only [← List.map_cons, ← List.map_map, ← Function.comp_def] + exact legacyCompatibleExternalStmtList_of_exprStmtExprs _ | true => simp only [ite_true, Bind.bind, Except.bind, pure, Except.pure] at hcompile cases hty : f.ty <;> simp [hty, Bind.bind, Except.bind, pure, Except.pure] at hcompile @@ -672,13 +676,15 @@ theorem legacyCompatibleExternalStmtList_of_compileSetStorage_ok_of_noPackedFiel private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_letVar {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {name : String} {value : Expr} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames (.letVar name value) = + fields events errors .calldata [] false inScopeNames [] (.letVar name value) = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -690,13 +696,15 @@ private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_letVar private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_assignVar {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {name : String} {value : Expr} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames (.assignVar name value) = + fields events errors .calldata [] false inScopeNames [] (.assignVar name value) = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -708,13 +716,15 @@ private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_assignVar private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_require {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {cond : Expr} {message : String} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames (.require cond message) = + fields events errors .calldata [] false inScopeNames [] (.require cond message) = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -731,12 +741,14 @@ private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_require private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_return {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {value : Expr} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames (.return value) = + fields events errors .calldata [] false inScopeNames [] (.return value) = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -754,11 +766,13 @@ private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_return private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_stop {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames .stop = + fields events errors .calldata [] false inScopeNames [] .stop = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -771,12 +785,14 @@ private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_stop private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_mstore {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {offset value : Expr} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames (.mstore offset value) = + fields events errors .calldata [] false inScopeNames [] (.mstore offset value) = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -795,12 +811,14 @@ private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_mstore private theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_tstore {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {offset value : Expr} {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames (.tstore offset value) = + fields events errors .calldata [] false inScopeNames [] (.tstore offset value) = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by unfold CompilationModel.compileStmt at hcompile @@ -824,6 +842,8 @@ the compiled-side compatibility fact needed to reuse already-proved helper-free cases inside the exact helper-aware compiled seam. -/ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractSurface {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {stmt : Stmt} {bodyIR : List YulStmt} @@ -831,7 +851,7 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS (hsurface : stmtTouchesUnsupportedContractSurface stmt = false) (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames stmt = Except.ok bodyIR) : + fields events errors .calldata [] false inScopeNames [] stmt = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by cases stmt with | letVar name value => @@ -873,11 +893,11 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS | error e => simp [hcond] at hcompile | ok condIR => simp only [hcond] at hcompile - cases hthen : CompilationModel.compileStmtList fields [] [] .calldata [] false inScopeNames thenBranch with + cases hthen : CompilationModel.compileStmtList fields events errors .calldata [] false inScopeNames [] thenBranch with | error e => simp [hthen] at hcompile | ok thenIR => simp only [hthen] at hcompile - cases helse : CompilationModel.compileStmtList fields [] [] .calldata [] false inScopeNames elseBranch with + cases helse : CompilationModel.compileStmtList fields events errors .calldata [] false inScopeNames [] elseBranch with | error e => simp [helse] at hcompile | ok elseIR => simp only [helse] at hcompile @@ -905,6 +925,8 @@ termination_by sizeOf stmt compilation stays inside the legacy helper-free external Yul subset. -/ theorem legacyCompatibleExternalStmtList_of_compileStmtList_ok_on_supportedContractSurface {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {stmts : List Stmt} {bodyIR : List YulStmt} @@ -912,7 +934,7 @@ theorem legacyCompatibleExternalStmtList_of_compileStmtList_ok_on_supportedContr (hsurface : stmtListTouchesUnsupportedContractSurface stmts = false) (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR) : + fields events errors .calldata [] false inScopeNames [] stmts = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by match stmts with | [] => @@ -1537,6 +1559,8 @@ mapping-write fragment instead of forcing it back onto the stricter default surface. -/ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractSurface_exceptMappingWrites {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {stmt : Stmt} {bodyIR : List YulStmt} @@ -1544,7 +1568,7 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS (hsurface : stmtTouchesUnsupportedContractSurfaceExceptMappingWrites stmt = false) (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false inScopeNames stmt = Except.ok bodyIR) : + fields events errors .calldata [] false inScopeNames [] stmt = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by cases stmt with | setMapping field key value => @@ -1602,7 +1626,8 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS | returndataCopy _ _ _ | revertReturndata | stop | ite _ _ _ | forEach _ _ _ | emit _ _ | internalCall _ _ | internalCallAssign _ _ _ | rawLog _ _ _ - | externalCallBind _ _ _ | ecm _ _ => + | externalCallBind _ _ _ | tryExternalCallBind _ _ _ _ | ecm _ _ + | unsafeBlock _ _ | matchAdt _ _ _ => exact legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractSurface hnoPacked (by simpa [stmtTouchesUnsupportedContractSurfaceExceptMappingWrites] using hsurface) @@ -1635,6 +1660,8 @@ mapping-write surface. This is the direct `compileStmtList` analogue of the single-statement theorem above. -/ theorem legacyCompatibleExternalStmtList_of_compileStmtList_ok_on_supportedContractSurface_exceptMappingWrites {fields : List Field} + {events : List EventDef} + {errors : List ErrorDef} {inScopeNames : List String} {stmts : List Stmt} {bodyIR : List YulStmt} @@ -1642,7 +1669,7 @@ theorem legacyCompatibleExternalStmtList_of_compileStmtList_ok_on_supportedContr (hsurface : stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites stmts = false) (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR) : + fields events errors .calldata [] false inScopeNames [] stmts = Except.ok bodyIR) : LegacyCompatibleExternalStmtList bodyIR := by match stmts with | [] => @@ -1752,14 +1779,14 @@ structure EventHeadStepBridgeCatalog args.any exprTouchesUnsupportedContractSurface = false → ∃ compiledIR, CompilationModel.compileStmt fields spec.events spec.errors .calldata - [] false scope (Stmt.emit eventName args) = Except.ok compiledIR + [] false scope [] (Stmt.emit eventName args) = Except.ok compiledIR bridge : ∀ {scope : List String} {eventName : String} {args : List Expr} {compiledIR : List YulStmt}, eventEmissionProofSupported spec.events eventName args = true → args.any exprTouchesUnsupportedContractSurface = false → CompilationModel.compileStmt fields spec.events spec.errors .calldata - [] false scope (Stmt.emit eventName args) = Except.ok compiledIR → + [] false scope [] (Stmt.emit eventName args) = Except.ok compiledIR → ∀ (runtime : SourceSemantics.RuntimeState) (state : IRState) (helperFuel : Nat) @@ -1792,7 +1819,7 @@ structure EventHeadStepSemanticBridgeCatalog eventEmissionProofSupported spec.events eventName args = true → args.any exprTouchesUnsupportedContractSurface = false → CompilationModel.compileStmt fields spec.events spec.errors .calldata - [] false scope (Stmt.emit eventName args) = Except.ok compiledIR → + [] false scope [] (Stmt.emit eventName args) = Except.ok compiledIR → ∀ (runtime : SourceSemantics.RuntimeState) (state : IRState) (helperFuel : Nat) @@ -3179,13 +3206,13 @@ private theorem compileStmt_ok_of_compileStmtList_append_cons {bodyIR : List YulStmt} (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope («prefix» ++ stmt :: «suffix») = + fields [] [] .calldata [] false scope [] («prefix» ++ stmt :: «suffix») = Except.ok bodyIR) : ∃ stmtIR, CompilationModel.compileStmt fields [] [] .calldata [] false (List.foldl (fun acc s => collectStmtNames s ++ acc) scope «prefix») - stmt = Except.ok stmtIR := by + [] stmt = Except.ok stmtIR := by induction «prefix» generalizing scope bodyIR with | nil => rcases FunctionBody.compileStmtList_cons_ok_inv hcompile with ⟨hd, _, hstmt, _⟩; exact ⟨hd, hstmt⟩ | cons s rest ih => @@ -3200,7 +3227,7 @@ private theorem isMapping_false_of_compileStmt_setStorage_ok {compiledIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false scope (.setStorage fieldName value) = + fields [] [] .calldata [] false scope [] (.setStorage fieldName value) = Except.ok compiledIR) : isMapping fields fieldName = false := by simp only [CompilationModel.compileStmt] at hcompile @@ -3214,26 +3241,26 @@ private theorem compileStmt_ite_ok_inv {compiledIR : List YulStmt} (hcompile : CompilationModel.compileStmt - fields [] [] .calldata [] false scope (.ite cond thenBranch elseBranch) = + fields [] [] .calldata [] false scope [] (.ite cond thenBranch elseBranch) = Except.ok compiledIR) : ∃ condIR thenIR elseIR, CompilationModel.compileExpr fields .calldata cond = Except.ok condIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false scope thenBranch = Except.ok thenIR ∧ + fields [] [] .calldata [] false scope [] thenBranch = Except.ok thenIR ∧ CompilationModel.compileStmtList - fields [] [] .calldata [] false scope elseBranch = Except.ok elseIR := by + fields [] [] .calldata [] false scope [] elseBranch = Except.ok elseIR := by unfold CompilationModel.compileStmt at hcompile rcases hcond : CompilationModel.compileExpr fields .calldata cond with _ | condIR · simp [hcond] at hcompile cases hcompile · simp [hcond] at hcompile rcases hthen : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope thenBranch with _ | thenIR + fields [] [] .calldata [] false scope [] thenBranch with _ | thenIR · simp [hthen] at hcompile cases hcompile · simp [hthen] at hcompile rcases helse : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope elseBranch with _ | elseIR + fields [] [] .calldata [] false scope [] elseBranch with _ | elseIR · simp [helse] at hcompile cases hcompile · @@ -3252,7 +3279,7 @@ private theorem stmtListScopeCore_of_unsupportedContractSurface_eq_false (hsurface : stmtListTouchesUnsupportedContractSurface stmts = false) (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope stmts = Except.ok bodyIR) : + fields [] [] .calldata [] false scope [] stmts = Except.ok bodyIR) : StmtListScopeCore (fields.map (·.name)) stmts := by match stmts with | [] => exact StmtListScopeCore.nil @@ -3330,7 +3357,7 @@ theorem stmtListScopeCore_prefix_of_compileStmtList_ok_of_stmtListTouchesUnsuppo stmtListTouchesUnsupportedContractSurface («prefix» ++ «suffix») = false) (hcompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope («prefix» ++ «suffix») = + fields [] [] .calldata [] false scope [] («prefix» ++ «suffix») = Except.ok bodyIR) : StmtListScopeCore (fields.map (·.name)) «prefix» := by induction «prefix» generalizing scope «suffix» bodyIR with @@ -3420,7 +3447,8 @@ theorem stmtListScopeCore_prefix_of_compileStmtList_ok_of_stmtListTouchesUnsuppo | returnBytes _ | returnStorageWords _ | calldatacopy _ _ _ | returndataCopy _ _ _ | revertReturndata | forEach _ _ _ | emit _ _ | internalCall _ _ | internalCallAssign _ _ _ - | rawLog _ _ _ | externalCallBind _ _ _ | ecm _ _ => + | rawLog _ _ _ | externalCallBind _ _ _ | tryExternalCallBind _ _ _ _ | ecm _ _ + | unsafeBlock _ _ | matchAdt _ _ _ => simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface private theorem stmtTouchesUnsupportedContractSurface_of_stmtListTouchesUnsupportedContractSurface_append_cons @@ -5323,9 +5351,69 @@ private theorem encodeStorageAt_eq_copy private def fieldWriteEntriesAt (idx : Nat) (field : Field) : List (Nat × String × Option PackedBits) := - (field.slot.getD idx, field.name, field.packedBits) :: - (field.aliasSlots.zipIdx.map (fun (slot, aliasIdx) => - (slot, s!"{field.name}.aliasSlots[{aliasIdx}]", field.packedBits))) + firstFieldWriteSlotConflict.fieldOccupiedSlots field (field.slot.getD idx) + +private theorem fieldWriteEntriesAt_base_mem + (idx : Nat) (field : Field) : + field.slot.getD idx ∈ (fieldWriteEntriesAt idx field).map (fun entry => entry.1) := by + obtain ⟨name, ty, slotOpt, packedBits, aliasSlots⟩ := field + cases ty <;> + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + +private theorem exists_mem_zipIdx_of_mem + {α : Type} {x : α} {xs : List α} {start : Nat} + (hmem : x ∈ xs) : + ∃ i, (x, i) ∈ xs.zipIdx start := by + induction xs generalizing start with + | nil => simp at hmem + | cons y ys ih => + simp at hmem + rcases hmem with rfl | hmem + · exact ⟨start, by simp [List.zipIdx]⟩ + · obtain ⟨i, hi⟩ := ih (start := start + 1) hmem + exact ⟨i, by simp [List.zipIdx, hi]⟩ + +private theorem fieldWriteEntriesAt_alias_mem + {idx : Nat} {field : Field} {slot : Nat} + (hmem : slot ∈ field.aliasSlots) : + slot ∈ (fieldWriteEntriesAt idx field).map (fun entry => entry.1) := by + obtain ⟨name, ty, slotOpt, packedBits, aliasSlots⟩ := field + obtain ⟨aliasIdx, halias⟩ : ∃ i, (slot, i) ∈ aliasSlots.zipIdx := + exists_mem_zipIdx_of_mem hmem + cases ty with + | uint256 => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨_, _, _, _, halias, rfl, rfl, rfl⟩ + | address => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨_, _, _, _, halias, rfl, rfl, rfl⟩ + | adt _ maxFields => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨s!"{name}.aliasSlots[{aliasIdx}]", none, slot, aliasIdx, halias, 0, by omega, by simp⟩ + | dynamicArray _ => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨_, _, _, _, halias, rfl, rfl, rfl⟩ + | mappingTyped _ => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨_, _, _, _, halias, rfl, rfl, rfl⟩ + | mappingStruct _ _ => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨_, _, _, _, halias, rfl, rfl, rfl⟩ + | mappingStruct2 _ _ _ => + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] + exact Or.inr ⟨_, _, _, _, halias, rfl, rfl, rfl⟩ + +private theorem fieldWriteEntriesAt_packed_none_of_unpacked + {idx : Nat} {field : Field} {packed : Option PackedBits} + (hunpacked : field.packedBits = none) + (hmem : packed ∈ (fieldWriteEntriesAt idx field).map (fun entry => entry.2.2)) : + packed = none := by + obtain ⟨name, ty, slotOpt, packedBits, aliasSlots⟩ := field + simp at hunpacked + subst hunpacked + cases ty <;> + simp [fieldWriteEntriesAt, firstFieldWriteSlotConflict.fieldOccupiedSlots] at hmem <;> + aesop private def firstInFieldConflictCopy (seen : List (Nat × String × Option PackedBits)) @@ -5449,28 +5537,18 @@ private theorem firstFieldWriteSlotConflictCopyFrom_some_of_seen_slot_member -- targetSlot ∈ writeSlots = (field.slot.getD idx :: field.aliasSlots) -- fieldWriteEntriesAt produces entries with first components matching writeSlots -- and all packed bits = field.packedBits = none - -- The first components of fieldWriteEntriesAt entries are exactly the write slots - have hwriteEntrySlots : - (fieldWriteEntriesAt idx field).map (fun entry => entry.1) = - field.slot.getD idx :: field.aliasSlots := by - simp only [fieldWriteEntriesAt, List.map_cons, List.map_map] - congr 1 - show List.map (fun x : Nat × Nat => x.1) - field.aliasSlots.zipIdx = field.aliasSlots - exact List.zipIdx_map_fst 0 field.aliasSlots have htarget_in_entries : targetSlot ∈ (fieldWriteEntriesAt idx field).map (fun entry => entry.1) := by - rw [hwriteEntrySlots]; exact hslot + simp only [List.mem_cons] at hslot + rcases hslot with hslot | halias + · subst targetSlot + exact fieldWriteEntriesAt_base_mem idx field + · exact fieldWriteEntriesAt_alias_mem halias have hunpacked_entries : ∀ packed ∈ (fieldWriteEntriesAt idx field).map (fun entry => entry.2.2), packed = none := by - unfold fieldWriteEntriesAt - simp only [List.map_cons, List.map_map, List.mem_cons] - rintro packed (rfl | hmem) - · exact hunpacked - · rw [List.mem_map] at hmem - obtain ⟨_, _, rfl⟩ := hmem - exact hunpacked + intro packed hmem + exact fieldWriteEntriesAt_packed_none_of_unpacked hunpacked hmem have hconflict := firstInFieldConflictCopy_ne_none_of_seen_slot_unpacked hseen htarget_in_entries hunpacked_entries cases hc : firstInFieldConflictCopy seen (fieldWriteEntriesAt idx field) with @@ -5546,17 +5624,11 @@ private theorem findResolvedFieldAtSlotCopyFrom_of_member by_cases hcapture : field.slot.getD idx = targetSlot ∨ targetSlot ∈ field.aliasSlots · exfalso - have hwriteEntrySlots : - (fieldWriteEntriesAt idx field).map (fun entry => entry.1) = - field.slot.getD idx :: field.aliasSlots := by - simp only [fieldWriteEntriesAt, List.map_cons, List.map_map] - congr 1; exact List.zipIdx_map_fst 0 field.aliasSlots have htargetInEntries : targetSlot ∈ (fieldWriteEntriesAt idx field).map (fun entry => entry.1) := by - rw [hwriteEntrySlots] rcases hcapture with rfl | h - · exact .head _ - · exact .tail _ h + · exact fieldWriteEntriesAt_base_mem idx field + · exact fieldWriteEntriesAt_alias_mem h have htargetInSeen : targetSlot ∈ ((fieldWriteEntriesAt idx field).reverse ++ seen).map (fun entry => entry.1) := by @@ -5601,11 +5673,20 @@ private theorem firstFieldWriteSlotConflict_go_eq_CopyFrom | cons fld rest ih => rw [firstFieldWriteSlotConflict_go_cons] dsimp only [] - simp only [firstFieldWriteSlotConflictCopyFrom, fieldWriteEntriesAt] rw [firstInFieldConflict_eq_Copy] - cases firstInFieldConflictCopy seen _ with - | none => exact ih _ _ - | some _ => rfl + change + (match firstInFieldConflictCopy seen (fieldWriteEntriesAt i fld) with + | some conflict => some conflict + | none => + firstFieldWriteSlotConflict.go + ((fieldWriteEntriesAt i fld).reverse ++ seen) (i + 1) rest) = + firstFieldWriteSlotConflictCopyFrom seen i (fld :: rest) + simp only [firstFieldWriteSlotConflictCopyFrom] + cases hc : firstInFieldConflictCopy seen (fieldWriteEntriesAt i fld) with + | none => + simpa [hc] using ih ((fieldWriteEntriesAt i fld).reverse ++ seen) (i + 1) + | some _ => + simp [hc] private theorem findResolvedFieldAtSlotCopy_of_findFieldWithResolvedSlot_member {fields : List Field} @@ -6959,6 +7040,7 @@ theorem compiledStmtStep_setStorage_singleSlot (hnotAddr : SourceSemantics.fieldUsesAddressStorage f = false) (hnotDyn : SourceSemantics.fieldUsesDynamicArrayStorage f = false) (hNotMapping : isMapping fields fieldName = false) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) (hvalueIR : CompilationModel.compileExpr fields .calldata value = Except.ok valueIR) : CompiledStmtStep fields scope (.setStorage fieldName value) [YulStmt.expr (YulExpr.call "sstore" [YulExpr.lit slot, valueIR])] where @@ -7650,7 +7732,7 @@ private theorem compileStmt_emit_scalar_supported_ok (hsurface : args.any exprTouchesUnsupportedContractSurface = false) : ∃ compiledIR, CompilationModel.compileStmt fields spec.events spec.errors .calldata - [] false scope (Stmt.emit eventName args) = Except.ok compiledIR := by + [] false scope [] (Stmt.emit eventName args) = Except.ok compiledIR := by have hcore : ∀ expr ∈ args, FunctionBody.ExprCompileCore expr := by intro expr hmem have hnotTrue : @@ -10558,6 +10640,7 @@ theorem compiledStmtStep_setStorage_aliasSlots (hnotAddr : SourceSemantics.fieldUsesAddressStorage f = false) (hnotDyn : SourceSemantics.fieldUsesDynamicArrayStorage f = false) (hNotMapping : isMapping fields fieldName = false) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) (hvalueIR : CompilationModel.compileExpr fields .calldata value = Except.ok valueIR) : CompiledStmtStep fields scope (.setStorage fieldName value) [YulStmt.block @@ -10566,8 +10649,13 @@ theorem compiledStmtStep_setStorage_aliasSlots YulStmt.expr (YulExpr.call "sstore" [YulExpr.lit writeSlot, YulExpr.ident "__compat_value"])))] where compileOk := by - simp [CompilationModel.compileStmt, CompilationModel.compileSetStorage, - hNotMapping, hfind, hwriteSlots, halias, hunpacked, hvalueIR] + cases hty : f.ty with + | adt name maxFields => + exact False.elim (hNotAdt name maxFields hty) + | uint256 | address | dynamicArray | mappingTyped | mappingStruct | mappingStruct2 => + simp [CompilationModel.compileStmt, CompilationModel.compileSetStorage, + hNotMapping, hfind, hwriteSlots, halias, hunpacked, hvalueIR, hty, + pure, Except.pure, Bind.bind, Except.bind] preserves runtime state extraFuel hexact hscope hbounded hruntime hslack := by let slots := slot :: f.aliasSlots let blockBody := @@ -10732,6 +10820,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes (hnotAddr : SourceSemantics.fieldUsesAddressStorage f = false) (hnotDyn : SourceSemantics.fieldUsesDynamicArrayStorage f = false) (hNotMapping : isMapping spec.fields fieldName = false) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) (hvalueIR : CompilationModel.compileExpr spec.fields .calldata value = Except.ok valueIR) : ∃ compiledIR, CompiledStmtStep spec.fields scope (.setStorage fieldName value) compiledIR := by by_cases halias : f.aliasSlots = [] @@ -10747,6 +10836,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes (hnotAddr := hnotAddr) (hnotDyn := hnotDyn) (hNotMapping := hNotMapping) + (hNotAdt := hNotAdt) (hvalueIR := hvalueIR) simpa [halias] using hwriteSlots · refine @@ -10769,6 +10859,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes (hnotAddr := hnotAddr) (hnotDyn := hnotDyn) (hNotMapping := hNotMapping) + (hNotAdt := hNotAdt) (hvalueIR := hvalueIR) theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_scopeDiscipline @@ -10800,6 +10891,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_scopeDiscipli (hnotAddr : SourceSemantics.fieldUsesAddressStorage f = false) (hnotDyn : SourceSemantics.fieldUsesDynamicArrayStorage f = false) (hNotMapping : isMapping spec.fields fieldName = false) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) (hvalueIR : CompilationModel.compileExpr spec.fields .calldata value = Except.ok valueIR) : ∃ compiledIR, CompiledStmtStep spec.fields @@ -10820,6 +10912,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_scopeDiscipli (hnotAddr := hnotAddr) (hnotDyn := hnotDyn) (hNotMapping := hNotMapping) + (hNotAdt := hNotAdt) (hvalueIR := hvalueIR) intro name hmem have hscopeNames := stmtListScopeDiscipline_scope_names hprefix name hmem @@ -10881,6 +10974,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_validateFunct (hnotAddr : SourceSemantics.fieldUsesAddressStorage f = false) (hnotDyn : SourceSemantics.fieldUsesDynamicArrayStorage f = false) (hNotMapping : isMapping spec.fields fieldName = false) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) (hvalueIR : CompilationModel.compileExpr spec.fields .calldata value = Except.ok valueIR) : ∃ compiledIR, CompiledStmtStep spec.fields @@ -10903,6 +10997,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_validateFunct (hnotAddr := hnotAddr) (hnotDyn := hnotDyn) (hNotMapping := hNotMapping) + (hNotAdt := hNotAdt) (hvalueIR := hvalueIR) -- NOTE: The _of_compileStmtList intermediate was superseded by _of_bodySurface below. @@ -10925,7 +11020,7 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_validateFunct (hbodySurface : stmtListTouchesUnsupportedContractSurface fn.body = false) (hbodyCompile : CompilationModel.compileStmtList - spec.fields [] [] .calldata [] false (fn.params.map (·.name)) fn.body = + spec.fields [] [] .calldata [] false (fn.params.map (·.name)) [] fn.body = Except.ok bodyIR) (hbody : fn.body = «prefix» ++ .setStorage fieldName value :: «suffix») (hfind : findFieldWithResolvedSlot spec.fields fieldName = some (f, slot)) @@ -10956,9 +11051,16 @@ theorem compiledStmtStep_setStorage_of_validateIdentifierShapes_of_validateFunct rcases compileStmt_ok_of_compileStmtList_append_cons (by simpa [hbody] using hbodyCompile) with ⟨stmtIR, hstmt⟩ exact isMapping_false_of_compileStmt_setStorage_ok hstmt + have hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields := by + intro name maxFields hty + rcases compileStmt_ok_of_compileStmtList_append_cons + (by simpa [hbody] using hbodyCompile) with ⟨stmtIR, hstmt⟩ + simp [CompilationModel.compileStmt, CompilationModel.compileSetStorage, + hNotMapping, hfind, hty, hvalueIR, pure, Pure.pure, Except.pure, + Bind.bind, Except.bind] at hstmt exact compiledStmtStep_setStorage_of_validateIdentifierShapes_of_validateFunctionIdentifierReferences hvalidateShapes hvalidateRefs hfn hparamScope hprefixCore hbody hvalueCore hinScope - hfind hwriteSlots hunpacked hnoConflict hnotAddr hnotDyn hNotMapping hvalueIR + hfind hwriteSlots hunpacked hnoConflict hnotAddr hnotDyn hNotMapping hNotAdt hvalueIR private theorem terminal_stmtResultMatchesIRExec_implies_stmtStepMatchesIRExec {fields : List Field} @@ -11008,7 +11110,7 @@ theorem compiledStmtStep_ite refine { compileOk := ?_ preserves := ?_ } - · show CompilationModel.compileStmt fields [] [] .calldata [] false scope + · show CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (.ite cond thenBranch elseBranch) = Except.ok compiledIR unfold CompilationModel.compileStmt simp only [hcondIR, hthenIR, helseIR, Except.bind, helseNonempty, ↓reduceIte] @@ -11592,6 +11694,9 @@ private theorem stmtListGenericCore_singleton_setStorage_singleSlot (hnotAddr := by rfl) (hnotDyn := by rfl) (hNotMapping := isMapping_false_of_findFieldWithResolvedSlot_uint256 hfind rfl) + (hNotAdt := by + intro name maxFields hty + cases hty) (hvalueIR := hvalueIR)) StmtListGenericCore.nil @@ -12800,6 +12905,18 @@ private theorem stmtListCompileCore_of_scopeNamesIncluded (ih hincluded) | stop hrest ih => exact .stop (ih hincluded) + | mstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + exact .mstore hcoreOffset + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + hcoreValue + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (ih hincluded) + | tstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + exact .tstore hcoreOffset + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + hcoreValue + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (ih hincluded) private theorem stmtListTerminalCore_of_scopeNamesIncluded {scope largerScope : List String} @@ -12826,6 +12943,18 @@ private theorem stmtListTerminalCore_of_scopeNamesIncluded (stmtListCompileCore_of_scopeNamesIncluded hrest hincluded) | stop hrest => exact .stop (stmtListCompileCore_of_scopeNamesIncluded hrest hincluded) + | mstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + exact .mstore hcoreOffset + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + hcoreValue + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (ih hincluded) + | tstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + exact .tstore hcoreOffset + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + hcoreValue + (exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (ih hincluded) | ite hcond hinScope hthen helse hrest ihThen ihElse => exact .ite hcond (exprBoundNamesInScope_of_scopeNamesIncluded hinScope hincluded) @@ -12884,6 +13013,36 @@ private theorem stmtListGenericCore_of_stmtListCompileCore_of_scopeNamesIncluded exact StmtListGenericCore.cons compiledStmtStep_stop (ih <| FunctionBody.scopeNamesIncluded_collectStmtNames_tail (stmt := .stop) hincluded) + | mstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreOffset with + ⟨offsetIR, hoffsetIR⟩ + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreValue with + ⟨valueIR, hvalueIR⟩ + exact StmtListGenericCore.cons + (compiledStmtStep_mstore_single + (hcoreOffset := hcoreOffset) + (hinScopeOffset := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + (hcoreValue := hcoreValue) + (hinScopeValue := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (hoffsetIR := hoffsetIR) + (hvalueIR := hvalueIR)) + (ih <| FunctionBody.scopeNamesIncluded_collectStmtNames_tail + (stmt := .mstore _ _) hincluded) + | tstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreOffset with + ⟨offsetIR, hoffsetIR⟩ + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreValue with + ⟨valueIR, hvalueIR⟩ + exact StmtListGenericCore.cons + (compiledStmtStep_tstore_single + (hcoreOffset := hcoreOffset) + (hinScopeOffset := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + (hcoreValue := hcoreValue) + (hinScopeValue := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (hoffsetIR := hoffsetIR) + (hvalueIR := hvalueIR)) + (ih <| FunctionBody.scopeNamesIncluded_collectStmtNames_tail + (stmt := .tstore _ _) hincluded) private theorem stmtListGenericCore_of_stmtListTerminalCore_of_scopeNamesIncluded {fields : List Field} @@ -12939,6 +13098,36 @@ private theorem stmtListGenericCore_of_stmtListTerminalCore_of_scopeNamesInclude hrest (FunctionBody.scopeNamesIncluded_collectStmtNames_tail (stmt := .stop) hincluded)) + | mstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreOffset with + ⟨offsetIR, hoffsetIR⟩ + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreValue with + ⟨valueIR, hvalueIR⟩ + exact StmtListGenericCore.cons + (compiledStmtStep_mstore_single + (hcoreOffset := hcoreOffset) + (hinScopeOffset := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + (hcoreValue := hcoreValue) + (hinScopeValue := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (hoffsetIR := hoffsetIR) + (hvalueIR := hvalueIR)) + (ih <| FunctionBody.scopeNamesIncluded_collectStmtNames_tail + (stmt := .mstore _ _) hincluded) + | tstore hcoreOffset hinScopeOffset hcoreValue hinScopeValue hrest ih => + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreOffset with + ⟨offsetIR, hoffsetIR⟩ + rcases FunctionBody.compileExpr_core_ok (fields := fields) hcoreValue with + ⟨valueIR, hvalueIR⟩ + exact StmtListGenericCore.cons + (compiledStmtStep_tstore_single + (hcoreOffset := hcoreOffset) + (hinScopeOffset := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeOffset hincluded) + (hcoreValue := hcoreValue) + (hinScopeValue := exprBoundNamesInScope_of_scopeNamesIncluded hinScopeValue hincluded) + (hoffsetIR := hoffsetIR) + (hvalueIR := hvalueIR)) + (ih <| FunctionBody.scopeNamesIncluded_collectStmtNames_tail + (stmt := .tstore _ _) hincluded) | ite hcond hinScope hthen helse hrest ihThen ihElse => rcases compiledStmtStep_ite (fields := fields) hcond (exprBoundNamesInScope_of_scopeNamesIncluded hinScope hincluded) @@ -13933,7 +14122,7 @@ private theorem stmtListGenericCore_of_requireClausesThenLetReturnLocalLiteral · refine FunctionBody.StmtListCompileCore.return_ (.localVar tmp) ?_ ?_ · intro name hmem simp [FunctionBody.exprBoundNames] at hmem - simpa [hmem] + simp [hmem] · exact FunctionBody.StmtListCompileCore.nil exact stmtListGenericCore_append (stmtListGenericCore_of_requireClausesOnly (fields := fields) (scope := scope) clauses) @@ -14188,7 +14377,7 @@ theorem compileStmtList_ok_of_stmtListGenericCore (hincluded : FunctionBody.scopeNamesIncluded scope inScopeNames) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false inScopeNames stmts = Except.ok bodyIR := by + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR := by induction hgeneric generalizing inScopeNames with | nil => exact ⟨[], rfl⟩ | cons hstep _hrest ih => @@ -14213,7 +14402,7 @@ theorem compileStmtList_ok_of_stmtListGenericWithHelpers (hincluded : FunctionBody.scopeNamesIncluded scope inScopeNames) : ∃ bodyIR, CompilationModel.compileStmtList - fields spec.events spec.errors .calldata [] false inScopeNames stmts = Except.ok bodyIR := by + fields spec.events spec.errors .calldata [] false inScopeNames [] stmts = Except.ok bodyIR := by induction hgeneric generalizing inScopeNames with | nil => exact ⟨[], rfl⟩ | cons hstep _hrest ih => @@ -14240,7 +14429,7 @@ theorem compileStmtList_ok_of_stmtListGenericWithHelpersAndHelperIR (hincluded : FunctionBody.scopeNamesIncluded scope inScopeNames) : ∃ bodyIR, CompilationModel.compileStmtList - fields spec.events spec.errors .calldata [] false inScopeNames stmts = Except.ok bodyIR := by + fields spec.events spec.errors .calldata [] false inScopeNames [] stmts = Except.ok bodyIR := by induction hgeneric generalizing inScopeNames with | nil => exact ⟨[], rfl⟩ | cons hstep _hrest ih => @@ -14361,18 +14550,18 @@ private theorem execIRStmts_append_of_continue | cons stmt rest ih => cases fuel with | zero => - simpa [execIRStmts] using hhead + simp [execIRStmts] at hhead | succ fuel => match hstmt : execIRStmt fuel state stmt with | .continue next' => simp [execIRStmts, hstmt] at hhead ⊢ - simpa using ih fuel next' hhead + exact ih fuel next' hhead | .return value state' => - simp [execIRStmts, hstmt] at hhead + simpa [execIRStmts, hstmt] using hhead | .stop state' => - simp [execIRStmts, hstmt] at hhead + simpa [execIRStmts, hstmt] using hhead | .revert state' => - simp [execIRStmts, hstmt] at hhead + simpa [execIRStmts, hstmt] using hhead private theorem execIRStmts_append_of_not_continue (fuel : Nat) @@ -14420,20 +14609,20 @@ private theorem execIRStmtsWithInternals_append_of_continue | cons stmt rest ih => cases fuel with | zero => - simpa [execIRStmtsWithInternals] using hhead + simp [execIRStmtsWithInternals] at hhead | succ fuel => match hstmt : execIRStmtWithInternals runtimeContract fuel state stmt with | .continue next' => simp [execIRStmtsWithInternals, hstmt] at hhead ⊢ - simpa using ih fuel next' hhead + exact ih fuel next' hhead | .return value state' => - simp [execIRStmtsWithInternals, hstmt] at hhead + simpa [execIRStmtsWithInternals, hstmt] using hhead | .stop state' => - simp [execIRStmtsWithInternals, hstmt] at hhead + simpa [execIRStmtsWithInternals, hstmt] using hhead | .revert state' => - simp [execIRStmtsWithInternals, hstmt] at hhead + simpa [execIRStmtsWithInternals, hstmt] using hhead | .leave state' => - simp [execIRStmtsWithInternals, hstmt] at hhead + simpa [execIRStmtsWithInternals, hstmt] using hhead private theorem execIRStmtsWithInternals_append_of_not_continue (runtimeContract : IRContract) @@ -14482,7 +14671,7 @@ theorem exec_compileStmtList_generic_sizeOf_extraFuel_step (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false scope stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false scope [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtList fields runtime stmts let irExec := execIRStmts (sizeOf bodyIR + extraFuel + 1) state bodyIR stmtStepMatchesIRExec @@ -14501,7 +14690,7 @@ theorem exec_compileStmtList_generic_sizeOf_extraFuel_step let bodyIR := compiledIR ++ tailIR have hbodyCompile : CompilationModel.compileStmtList - fields [] [] .calldata [] false scope (stmt :: rest) = + fields [] [] .calldata [] false scope [] (stmt :: rest) = Except.ok bodyIR := by exact FunctionBody.compileStmtList_cons_ok_of_compileStmt_ok hstep.compileOk htailCompile @@ -14642,7 +14831,7 @@ theorem exec_compileStmtList_generic_with_helpers_sizeOf_extraFuel_step (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields spec.events spec.errors .calldata [] false scope stmts = Except.ok bodyIR ∧ + fields spec.events spec.errors .calldata [] false scope [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtListWithHelpers spec fields helperFuel runtime stmts let irExec := execIRStmts (sizeOf bodyIR + extraFuel + 1) state bodyIR stmtStepMatchesIRExec @@ -14662,7 +14851,7 @@ theorem exec_compileStmtList_generic_with_helpers_sizeOf_extraFuel_step let bodyIR := compiledIR ++ tailIR have hbodyCompile : CompilationModel.compileStmtList - fields spec.events spec.errors .calldata [] false scope (stmt :: rest) = + fields spec.events spec.errors .calldata [] false scope [] (stmt :: rest) = Except.ok bodyIR := by exact FunctionBody.compileStmtList_cons_ok_of_compileStmt_ok_with_surface hstep.compileOk htailCompile @@ -14809,7 +14998,7 @@ theorem exec_compileStmtList_generic_with_helpers_and_helper_ir_sizeOf_extraFuel (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields spec.events spec.errors .calldata [] false scope stmts = Except.ok bodyIR ∧ + fields spec.events spec.errors .calldata [] false scope [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtListWithHelpers spec fields helperFuel runtime stmts let irExec := execIRStmtsWithInternals runtimeContract (sizeOf bodyIR + extraFuel + 1) state bodyIR stmtStepMatchesIRExecWithInternals @@ -14830,7 +15019,7 @@ theorem exec_compileStmtList_generic_with_helpers_and_helper_ir_sizeOf_extraFuel let bodyIR := compiledIR ++ tailIR have hbodyCompile : CompilationModel.compileStmtList - fields spec.events spec.errors .calldata [] false scope (stmt :: rest) = + fields spec.events spec.errors .calldata [] false scope [] (stmt :: rest) = Except.ok bodyIR := by exact FunctionBody.compileStmtList_cons_ok_of_compileStmt_ok_with_surface hstep.compileOk htailCompile @@ -14982,7 +15171,7 @@ theorem exec_compileStmtList_generic_sizeOf_extraFuel (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false scope stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false scope [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtList fields runtime stmts let irExec := execIRStmts (sizeOf bodyIR + extraFuel + 1) state bodyIR FunctionBody.stmtResultMatchesIRExec fields sourceResult irExec := by @@ -15020,7 +15209,7 @@ theorem exec_compileStmtList_generic_with_helpers_sizeOf_extraFuel (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false scope stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false scope [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtListWithHelpers spec fields helperFuel runtime stmts let irExec := execIRStmts (sizeOf bodyIR + extraFuel + 1) state bodyIR FunctionBody.stmtResultMatchesIRExec fields sourceResult irExec := by @@ -15065,7 +15254,7 @@ theorem exec_compileStmtList_generic_with_helpers_and_helper_ir_sizeOf_extraFuel (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : ∃ bodyIR, CompilationModel.compileStmtList - fields [] [] .calldata [] false scope stmts = Except.ok bodyIR ∧ + fields [] [] .calldata [] false scope [] stmts = Except.ok bodyIR ∧ let sourceResult := SourceSemantics.execStmtListWithHelpers spec fields helperFuel runtime stmts let irExec := execIRStmtsWithInternals runtimeContract (sizeOf bodyIR + extraFuel + 1) state bodyIR stmtResultMatchesIRExecWithInternals fields sourceResult irExec := by @@ -15104,6 +15293,7 @@ theorem supported_function_body_correct_from_exact_state_generic (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hgeneric : StmtListGenericCore (SourceSemantics.effectiveFields model) @@ -15111,7 +15301,7 @@ theorem supported_function_body_correct_from_exact_state_generic fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15143,8 +15333,8 @@ theorem supported_function_body_correct_from_exact_state_generic simpa [FunctionBody.runtimeStateMatchesIR] using hstateRuntime have hbodyCompile' : compileStmtList (SourceSemantics.effectiveFields model) [] [] .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by - simpa [hnormalized, hnoEvents, hnoErrors] using hbodyCompile + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by + simpa [hnormalized, hnoEvents, hnoErrors, hnoAdtTypes] using hbodyCompile have hscopeExact : FunctionBody.bindingsExactlyMatchIRVarsOnScope (fn.params.map (·.name)) bindings state := @@ -15200,6 +15390,7 @@ private theorem supported_function_body_correct_from_exact_state_generic_helper_ (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hgeneric : StmtListGenericWithHelpers model @@ -15208,7 +15399,7 @@ private theorem supported_function_body_correct_from_exact_state_generic_helper_ fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15243,8 +15434,8 @@ private theorem supported_function_body_correct_from_exact_state_generic_helper_ simpa [FunctionBody.runtimeStateMatchesIR] using hstateRuntime have hbodyCompile' : compileStmtList (SourceSemantics.effectiveFields model) [] [] .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by - simpa [hnormalized, hnoEvents, hnoErrors] using hbodyCompile + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by + simpa [hnormalized, hnoEvents, hnoErrors, hnoAdtTypes] using hbodyCompile have hscopeExact : FunctionBody.bindingsExactlyMatchIRVarsOnScope (fn.params.map (·.name)) bindings state := @@ -15338,6 +15529,7 @@ private theorem (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hgeneric : StmtListGenericWithHelpersAndHelperIR runtimeContract @@ -15347,7 +15539,7 @@ private theorem fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15373,8 +15565,8 @@ private theorem simpa [FunctionBody.runtimeStateMatchesIR] using hstateRuntime have hbodyCompile' : compileStmtList (SourceSemantics.effectiveFields model) [] [] .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts := by - simpa [hnormalized, hnoEvents, hnoErrors] using hbodyCompile + (fn.params.map (·.name)) [] fn.body = Except.ok bodyStmts := by + simpa [hnormalized, hnoEvents, hnoErrors, hnoAdtTypes] using hbodyCompile have hscopeExact : FunctionBody.bindingsExactlyMatchIRVarsOnScope (fn.params.map (·.name)) bindings state := @@ -15561,6 +15753,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_steps (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hgeneric : StmtListGenericWithHelpers model @@ -15569,7 +15762,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_steps fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15586,7 +15779,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_steps model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel := by exact supported_function_body_correct_from_exact_state_generic_helper_steps_raw model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings /-- Exact helper-aware body theorem for an exact helper-aware generic @@ -15609,6 +15802,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_steps_an (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hgeneric : StmtListGenericWithHelpersAndHelperIR runtimeContract @@ -15618,7 +15812,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_steps_an fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15638,7 +15832,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_steps_an supported_function_body_correct_from_exact_state_generic_helper_steps_and_helper_ir_raw runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings theorem supported_function_body_correct_from_exact_state_generic_helper_surface_steps_and_helper_ir @@ -15657,6 +15851,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_surface_ (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperFree : StmtListHelperFreeStepInterface (SourceSemantics.effectiveFields model) @@ -15676,7 +15871,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_surface_ fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15713,7 +15908,7 @@ theorem supported_function_body_correct_from_exact_state_generic_helper_surface_ supported_function_body_correct_from_exact_state_generic_helper_steps_and_helper_ir runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings /-- Body-level exact helper-aware bridge over the split helper-positive @@ -15736,6 +15931,7 @@ theorem supported_function_body_correct_from_exact_state_generic_internal_helper (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperFree : StmtListHelperFreeStepInterface (SourceSemantics.effectiveFields model) @@ -15762,7 +15958,7 @@ theorem supported_function_body_correct_from_exact_state_generic_internal_helper fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15800,7 +15996,7 @@ theorem supported_function_body_correct_from_exact_state_generic_internal_helper supported_function_body_correct_from_exact_state_generic_helper_steps_and_helper_ir runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings /-- Body-level exact helper-aware bridge over the fully split genuine-helper @@ -15823,6 +16019,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_int (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperFree : StmtListHelperFreeStepInterface (SourceSemantics.effectiveFields model) @@ -15870,7 +16067,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_int fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -15911,7 +16108,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_int supported_function_body_correct_from_exact_state_generic_helper_steps_and_helper_ir runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings /-- Body-level exact helper-aware bridge over the fully split genuine-helper @@ -15934,6 +16131,7 @@ theorem supported_function_body_correct_from_exact_state_generic_split_internal_ (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperFree : StmtListHelperFreeStepInterface (SourceSemantics.effectiveFields model) @@ -15974,7 +16172,7 @@ theorem supported_function_body_correct_from_exact_state_generic_split_internal_ fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16014,7 +16212,7 @@ theorem supported_function_body_correct_from_exact_state_generic_split_internal_ supported_function_body_correct_from_exact_state_generic_helper_steps_and_helper_ir runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings private theorem @@ -16114,6 +16312,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_int (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperFree : StmtListHelperFreeStepInterface (SourceSemantics.effectiveFields model) @@ -16162,7 +16361,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_int fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16192,7 +16391,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_int supported_function_body_correct_from_exact_state_generic_helper_steps_and_helper_ir runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings /-- Focused Tier 2 entry point for bodies whose only genuinely new helper work @@ -16249,9 +16448,10 @@ theorem (SourceSemantics.effectiveFields model) (fn.params.map (·.name)) fn.body) + (hnoAdtTypes : model.adtTypes = []) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16271,7 +16471,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_internal_helper_surface_steps_and_helper_ir_callsDisjoint runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hhelperFree + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hhelperFree (stmtListDirectInternalHelperCallStepInterface_of_directCallSurfaceClosed (runtimeContract := runtimeContract) (spec := model) @@ -16296,7 +16496,7 @@ theorem hstructClosed) hresidual hdisjoint - hbodyCompile + (by simpa [hnoAdtTypes] using hbodyCompile) hscope hbounded hstateRuntime hstateBindings /-- Current-fragment disjointness-based wrapper that lands directly in the exact @@ -16319,6 +16519,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hnoPacked : ∀ field ∈ model.fields, field.packedBits = none) (hcontractSurface : stmtListTouchesUnsupportedContractSurface fn.body = false) (hhelperFree : @@ -16328,7 +16529,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16357,7 +16558,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an supported_function_body_correct_from_exact_state_generic_finer_split_internal_helper_surface_steps_and_helper_ir_callsDisjoint runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hhelperFree + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hhelperFree (stmtListDirectInternalHelperCallStepInterface_of_helperSurfaceClosed (runtimeContract := runtimeContract) (spec := model) @@ -16418,6 +16619,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hnoPacked : ∀ field ∈ model.fields, field.packedBits = none) (hcontractSurface : stmtListTouchesUnsupportedContractSurface fn.body = false) (hhelperFree : @@ -16427,7 +16629,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16463,7 +16665,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an supported_function_body_correct_from_exact_state_generic_with_helpers_and_helper_ir_callsDisjoint runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoPacked hcontractSurface + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hnoPacked hcontractSurface hhelperFree hbodyCompile hscope hbounded hstateRuntime hstateBindings hdisjoint /-- Tier 2 disjointness-based exact helper-aware wrapper for the alternate @@ -16487,6 +16689,7 @@ theorem (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hnoPacked : ∀ field ∈ model.fields, field.packedBits = none) (hcontractSurface : stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites fn.body = false) @@ -16499,7 +16702,7 @@ theorem fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16525,7 +16728,7 @@ theorem supported_function_body_correct_from_exact_state_generic_finer_split_internal_helper_surface_steps_and_helper_ir_callsDisjoint runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hhelperFree + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hhelperFree (stmtListDirectInternalHelperCallStepInterface_of_helperSurfaceClosed (runtimeContract := runtimeContract) (spec := model) @@ -16584,6 +16787,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hnoPacked : ∀ field ∈ model.fields, field.packedBits = none) (hcontractSurface : stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites fn.body = false) @@ -16596,7 +16800,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16632,7 +16836,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_an supported_function_body_correct_from_exact_state_generic_with_helpers_and_helper_ir_except_mapping_writes_callsDisjoint runtimeContract model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoPacked hcontractSurface hhelperSurface + hextraFuel hfuelPos hnormalized hnoEvents hnoErrors hnoAdtTypes hnoPacked hcontractSurface hhelperSurface hhelperFree hbodyCompile hscope hbounded hstateRuntime hstateBindings hdisjoint /-- Goal-based helper-aware wrapper around the generic body/IR preservation @@ -16652,6 +16856,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_go (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperGoal : SourceSemantics.ExecStmtListWithHelpersConservativeExtensionGoal model @@ -16668,7 +16873,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_go fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16685,7 +16890,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers_go model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel := by rcases supported_function_body_correct_from_exact_state_generic model fn bodyStmts tx initialWorld state bindings extraFuel hextraFuel - hnormalized hnoEvents hnoErrors hgeneric hbodyCompile hscope hbounded + hnormalized hnoEvents hnoErrors hnoAdtTypes hgeneric hbodyCompile hscope hbounded hstateRuntime hstateBindings with ⟨sourceResult, irExec, hsource, hbodyExec, hmatch⟩ refine ⟨sourceResult, irExec, ?_, hbodyExec, hmatch⟩ @@ -16719,6 +16924,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers (hnormalized : SourceSemantics.effectiveFields model = model.fields) (hnoEvents : model.events = []) (hnoErrors : model.errors = []) + (hnoAdtTypes : model.adtTypes = []) (hhelperSurface : stmtListTouchesUnsupportedHelperSurface fn.body = false) (hhelperFree : StmtListHelperFreeStepInterface @@ -16727,7 +16933,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers fn.body) (hbodyCompile : compileStmtList model.fields model.events model.errors .calldata [] false - (fn.params.map (·.name)) fn.body = Except.ok bodyStmts) + (fn.params.map (·.name)) model.adtTypes fn.body = Except.ok bodyStmts) (hscope : FunctionBody.scopeNamesPresent (fn.params.map (·.name)) bindings) (hbounded : FunctionBody.bindingsBounded bindings) @@ -16756,7 +16962,7 @@ theorem supported_function_body_correct_from_exact_state_generic_with_helpers hhelperSurface exact supported_function_body_correct_from_exact_state_generic_helper_steps model fn bodyStmts helperFuel tx initialWorld state bindings extraFuel - hextraFuel hnormalized hnoEvents hnoErrors hgenericWithHelpers hbodyCompile + hextraFuel hnormalized hnoEvents hnoErrors hnoAdtTypes hgenericWithHelpers hbodyCompile hscope hbounded hstateRuntime hstateBindings /-- Constructor for the helper-aware single-step interface when the head @@ -16778,7 +16984,7 @@ theorem compiledStmtStepWithHelpersAndHelperIR_internalCallAssign {names : List String} {calleeName : String} {args : List Expr} {compiledIR : List YulStmt} {argExprs : List YulExpr} - (hcompile : CompilationModel.compileStmt fields [] [] .calldata [] false scope + (hcompile : CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR) (hargCompile : CompilationModel.compileExprList fields .calldata args = Except.ok argExprs) -- End-to-end source↔IR alignment bridge. @@ -16838,7 +17044,7 @@ theorem compiledStmtStepWithHelpersAndHelperIR_internalCall {calleeName : String} {args : List Expr} {compiledIR : List YulStmt} {argExprs : List YulExpr} - (hcompile : CompilationModel.compileStmt fields [] [] .calldata [] false scope + (hcompile : CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR) (hargCompile : CompilationModel.compileExprList fields .calldata args = Except.ok argExprs) -- End-to-end source↔IR alignment bridge. @@ -16972,12 +17178,12 @@ structure DirectInternalHelperCallHeadStepBridge compile : ∀ {scope : List String} {args : List Expr}, ∃ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR bridge : ∀ {scope : List String} {args : List Expr} {compiledIR : List YulStmt} {argExprs : List YulExpr}, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR → CompilationModel.compileExprList fields .calldata args = Except.ok argExprs → ∀ (runtime : SourceSemantics.RuntimeState) @@ -17005,12 +17211,12 @@ structure DirectInternalHelperAssignHeadStepBridge compile : ∀ {scope : List String} {names : List String} {args : List Expr}, ∃ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR bridge : ∀ {scope : List String} {names : List String} {args : List Expr} {compiledIR : List YulStmt} {argExprs : List YulExpr}, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR → CompilationModel.compileExprList fields .calldata args = Except.ok argExprs → ∀ (runtime : SourceSemantics.RuntimeState) @@ -17039,13 +17245,13 @@ structure DirectInternalHelperHeadStepBridgeCatalog ∀ {scope : List String} {calleeName : String} {args : List Expr}, calleeName ∈ helperCallNames fn → ∃ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR callBridge : ∀ {scope : List String} {calleeName : String} {args : List Expr} {compiledIR : List YulStmt} {argExprs : List YulExpr}, calleeName ∈ helperCallNames fn → - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR → CompilationModel.compileExprList fields .calldata args = Except.ok argExprs → ∀ (runtime : SourceSemantics.RuntimeState) @@ -17068,13 +17274,13 @@ structure DirectInternalHelperHeadStepBridgeCatalog ∀ {scope : List String} {names : List String} {calleeName : String} {args : List Expr}, calleeName ∈ helperCallNames fn → ∃ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR assignBridge : ∀ {scope : List String} {names : List String} {calleeName : String} {args : List Expr} {compiledIR : List YulStmt} {argExprs : List YulExpr}, calleeName ∈ helperCallNames fn → - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR → CompilationModel.compileExprList fields .calldata args = Except.ok argExprs → ∀ (runtime : SourceSemantics.RuntimeState) @@ -17140,7 +17346,7 @@ theorem directInternalHelperPerCalleeBridgeCatalog_of_supportedBody_and_assignBr refine ⟨?_, ?_⟩ · intro calleeName hmem exfalso - simpa [hbody.helperCallNames_nil] using hmem + simp [hbody.helperCallNames_nil] at hmem · intro calleeName hmem exact hassign.assign hmem @@ -17157,7 +17363,7 @@ structure DirectInternalHelperPerCalleeCallCompileCatalog calleeName ∈ helperCallNames fn → ∀ {scope : List String} {args : List Expr}, ∃ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR /-- Assign-only half of the compile-side Tier 4 inventory. This isolates the @@ -17172,7 +17378,7 @@ structure DirectInternalHelperPerCalleeAssignCompileCatalog calleeName ∈ helperCallNames fn → ∀ {scope : List String} {names : List String} {args : List Expr}, ∃ compiledIR, - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR /-- Reassemble the full compile-side Tier 4 inventory from independently @@ -17186,7 +17392,7 @@ theorem directInternalHelperPerCalleeCallCompileCatalog_of_supportedBody refine ⟨?_⟩ intro calleeName hmem exfalso - simpa [hbody.helperCallNames_nil] using hmem + simp [hbody.helperCallNames_nil] at hmem theorem directInternalHelperHeadStepBridgeCatalog_of_perCalleeBridgeCatalog {runtimeContract : IRContract} @@ -17796,7 +18002,7 @@ theorem execIRStmtsWithInternals_of_internalCallAssign_compiledHelperWitness {argVals : List Nat} {state' : IRState} (hcompile : - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCallAssign names calleeName args) = Except.ok compiledIR) (argExprs : List YulExpr) (hargCompile : @@ -17874,7 +18080,7 @@ theorem execIRStmtsWithInternals_of_internalCall_compiledHelperWitness {argVals : List Nat} {state' : IRState} (hcompile : - CompilationModel.compileStmt fields [] [] .calldata [] false scope + CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (Stmt.internalCall calleeName args) = Except.ok compiledIR) (argExprs : List YulExpr) (hargCompile : diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index 787abcdb6..1adb9ee3a 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -4133,8 +4133,9 @@ theorem compileInternalFunction_output_shape {fields : List CompilationModel.Field} {events : List CompilationModel.EventDef} {errors : List CompilationModel.ErrorDef} + {adtTypes : List CompilationModel.AdtTypeDef} {spec : CompilationModel.FunctionSpec} {stmt : YulStmt} - (hok : CompilationModel.compileInternalFunction fields events errors spec = Except.ok stmt) : + (hok : CompilationModel.compileInternalFunction fields events errors adtTypes spec = Except.ok stmt) : ∃ retNames bodyStmts, stmt = YulStmt.funcDef (CompilationModel.internalFunctionYulName spec.name) @@ -4154,7 +4155,7 @@ theorem compileInternalFunction_output_shape -- The private `freshInternalRetNames` is opaque; we just split on the -- remaining compileStmtList result. revert hok - generalize CompilationModel.compileStmtList _ _ _ _ _ _ _ _ = compileResult + generalize CompilationModel.compileStmtList _ _ _ _ _ _ _ _ _ = compileResult intro hok match compileResult with | .error e => simp at hok @@ -4169,9 +4170,10 @@ theorem findInternalFunction?_of_compileInternalFunction_mem {fields : List CompilationModel.Field} {events : List CompilationModel.EventDef} {errors : List CompilationModel.ErrorDef} + {adtTypes : List CompilationModel.AdtTypeDef} {spec : CompilationModel.FunctionSpec} {compiledStmt : YulStmt} {contract : IRContract} - (hok : CompilationModel.compileInternalFunction fields events errors spec = Except.ok compiledStmt) + (hok : CompilationModel.compileInternalFunction fields events errors adtTypes spec = Except.ok compiledStmt) (hmem : compiledStmt ∈ contract.internalFunctions) : (findInternalFunction? contract (CompilationModel.internalFunctionYulName spec.name)).isSome = true := by obtain ⟨retNames, bodyStmts, hshape⟩ := compileInternalFunction_output_shape hok @@ -4186,9 +4188,10 @@ theorem findInternalFunction?_exact_of_compileInternalFunction_mem_unique {fields : List CompilationModel.Field} {events : List CompilationModel.EventDef} {errors : List CompilationModel.ErrorDef} + {adtTypes : List CompilationModel.AdtTypeDef} {spec : CompilationModel.FunctionSpec} {compiledStmt : YulStmt} {contract : IRContract} - (hok : CompilationModel.compileInternalFunction fields events errors spec = Except.ok compiledStmt) + (hok : CompilationModel.compileInternalFunction fields events errors adtTypes spec = Except.ok compiledStmt) (hmem : compiledStmt ∈ contract.internalFunctions) (hunique : ∀ stmt ∈ contract.internalFunctions, ∀ p r b, irInternalFunctionDefOfStmt? stmt = @@ -4227,7 +4230,7 @@ theorem compileStmt_internalCallAssign_shape {names : List String} {functionName : String} {args : List CompilationModel.Expr} {compiledIR : List YulStmt} - (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope + (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (CompilationModel.Stmt.internalCallAssign names functionName args) = Except.ok compiledIR) : ∃ argExprs, CompilationModel.compileExprList fields .calldata args = Except.ok argExprs ∧ @@ -4248,7 +4251,7 @@ theorem compileStmt_internalCall_shape {fields : List CompilationModel.Field} {scope : List String} {functionName : String} {args : List CompilationModel.Expr} {compiledIR : List YulStmt} - (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope + (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (CompilationModel.Stmt.internalCall functionName args) = Except.ok compiledIR) : ∃ argExprs, CompilationModel.compileExprList fields .calldata args = Except.ok argExprs ∧ @@ -4304,7 +4307,7 @@ theorem execIRStmtsWithInternals_of_internalCallAssign_compile {compiledIR : List YulStmt} (contract : IRContract) (fuel : Nat) (state : IRState) (helper : IRInternalFunctionDef) (argVals : List Nat) (state' : IRState) - (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope + (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (CompilationModel.Stmt.internalCallAssign names functionName args) = Except.ok compiledIR) (hfind : findInternalFunction? contract (CompilationModel.internalFunctionYulName functionName) = some helper) @@ -4345,7 +4348,7 @@ theorem execIRStmtsWithInternals_of_internalCall_compile {compiledIR : List YulStmt} (contract : IRContract) (fuel : Nat) (state : IRState) (helper : IRInternalFunctionDef) (argVals : List Nat) (state' : IRState) - (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope + (hok : CompilationModel.compileStmt fields [] [] .calldata [] false scope [] (CompilationModel.Stmt.internalCall functionName args) = Except.ok compiledIR) (hfind : findInternalFunction? contract (CompilationModel.internalFunctionYulName functionName) = some helper) diff --git a/Compiler/Proofs/IRGeneration/SourceSemantics.lean b/Compiler/Proofs/IRGeneration/SourceSemantics.lean index 0c2924b27..e1e94e5b6 100644 --- a/Compiler/Proofs/IRGeneration/SourceSemantics.lean +++ b/Compiler/Proofs/IRGeneration/SourceSemantics.lean @@ -2044,6 +2044,8 @@ theorem findDynamicArrayElementAtSlot_withTransactionContext simpa [findDynamicArrayElementAtSlot.go, withTransactionContext, hty] using ih (idx + 1) | address => simpa [findDynamicArrayElementAtSlot.go, withTransactionContext, hty] using ih (idx + 1) + | adt name maxFields => + simpa [findDynamicArrayElementAtSlot.go, withTransactionContext, hty] using ih (idx + 1) | dynamicArray elemType => cases hscan : findDynamicArrayElementAtSlot.scanElements slot @@ -2084,6 +2086,8 @@ theorem findDynamicArrayElementAtSlot_congr_storageArray simpa [findDynamicArrayElementAtSlot.go, hty] using ih (idx + 1) | address => simpa [findDynamicArrayElementAtSlot.go, hty] using ih (idx + 1) + | adt name maxFields => + simpa [findDynamicArrayElementAtSlot.go, hty] using ih (idx + 1) | dynamicArray elemType => cases hscan : findDynamicArrayElementAtSlot.scanElements slot @@ -3458,6 +3462,8 @@ mutual cases expr with | internalCall _ _ => simp [exprTouchesUnsupportedHelperSurface] at hsurface + | adtConstruct _ _ _ | adtTag _ _ | adtField _ _ _ _ _ => + simp [exprTouchesUnsupportedHelperSurface] at hsurface | mappingChain _ _ => simp [exprTouchesUnsupportedHelperSurface] at hsurface | literal _ => @@ -4034,6 +4040,7 @@ private theorem execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux evalExprList_eq_mapM] | .rawLog _ _ _ => simp [execStmtWithHelpers, execStmtWithEvents] | .externalCallBind _ _ _ => simp [execStmtWithHelpers, execStmtWithEvents] + | .tryExternalCallBind _ _ _ _ => simp [execStmtWithHelpers, execStmtWithEvents] | .ecm _ _ => simp [execStmtWithHelpers, execStmtWithEvents] | .forEach _ _ _ => simp only [stmtTouchesUnsupportedHelperSurface, Bool.or_eq_false_iff] at hsurface diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index ae3c6357b..2a1d67044 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -71,23 +71,33 @@ theorem eventParamScalarProofSupported_eq_true_of_eventDefScalarProofSupported eventParamScalarProofSupported_eq_true_of_mem_all (eventDefScalarProofSupported_params_all hsupport) hmem -theorem eventParamScalarProofSupported_eventIsDynamicType_eq_false - {ty : ParamType} - (hsupport : eventParamScalarProofSupported ty = true) : - eventIsDynamicType ty = false := by - cases ty <;> - simp [eventParamScalarProofSupported, eventParamScalarCompileSupported, - eventIsDynamicType, isDynamicParamType] - at hsupport ⊢ - -theorem eventParamScalarProofSupported_eventHeadWordSize_eq_thirty_two - {ty : ParamType} - (hsupport : eventParamScalarProofSupported ty = true) : - eventHeadWordSize ty = 32 := by - cases ty <;> - simp [eventParamScalarProofSupported, eventParamScalarCompileSupported, - eventHeadWordSize, paramHeadSize] - at hsupport ⊢ +theorem eventParamScalarProofSupported_eventIsDynamicType_eq_false : + ∀ {ty : ParamType}, + eventParamScalarProofSupported ty = true → + eventIsDynamicType ty = false + | ty, hsupport => by + cases ty <;> + simp [eventParamScalarProofSupported, eventParamScalarCompileSupported, + eventIsDynamicType, isDynamicParamType] + at hsupport ⊢ + case newtypeOf name baseType => + have hbase := + eventParamScalarProofSupported_eventIsDynamicType_eq_false (ty := baseType) hsupport + simpa [eventIsDynamicType] using hbase + +theorem eventParamScalarProofSupported_eventHeadWordSize_eq_thirty_two : + ∀ {ty : ParamType}, + eventParamScalarProofSupported ty = true → + eventHeadWordSize ty = 32 + | ty, hsupport => by + cases ty <;> + simp [eventParamScalarProofSupported, eventParamScalarCompileSupported, + eventHeadWordSize, paramHeadSize] + at hsupport ⊢ + case newtypeOf name baseType => + have hbase := + eventParamScalarProofSupported_eventHeadWordSize_eq_thirty_two (ty := baseType) hsupport + simpa [eventHeadWordSize] using hbase theorem eventParamScalarProofSupported_ne_bytes {ty : ParamType} @@ -586,6 +596,9 @@ def exprTouchesUnsupportedConstructorRawCalldataSurface : Expr → Bool exprTouchesUnsupportedConstructorRawCalldataSurface is || exprTouchesUnsupportedConstructorRawCalldataSurface oo || exprTouchesUnsupportedConstructorRawCalldataSurface os + | .adtConstruct _ _ args => + exprListTouchesUnsupportedConstructorRawCalldataSurface args + | .adtTag _ _ | .adtField _ _ _ _ _ => false def exprListTouchesUnsupportedConstructorRawCalldataSurface : List Expr → Bool | [] => false @@ -613,7 +626,7 @@ def stmtTouchesUnsupportedConstructorRawCalldataSurface : Stmt → Bool exprTouchesUnsupportedConstructorRawCalldataSurface key2 || exprTouchesUnsupportedConstructorRawCalldataSurface value | .emit _ args | .internalCallAssign _ _ args | .internalCall _ args - | .externalCallBind _ _ args => + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args => exprListTouchesUnsupportedConstructorRawCalldataSurface args | .ite cond thenBranch elseBranch => exprTouchesUnsupportedConstructorRawCalldataSurface cond || @@ -626,6 +639,7 @@ def stmtTouchesUnsupportedConstructorRawCalldataSurface : Stmt → Bool | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata | .rawLog _ _ _ | .ecm _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true def stmtListTouchesUnsupportedConstructorRawCalldataSurface : List Stmt → Bool | [] => false @@ -688,7 +702,8 @@ def exprTouchesUnsupportedCoreSurface : Expr → Bool | .returndataSize | .extcodesize _ | .returndataOptionalBoolAt _ | .externalCall _ _ | .internalCall _ _ | .arrayLength _ | .arrayElement _ _ | .storageArrayLength _ | .storageArrayElement _ _ - | .dynamicBytesEq _ _ => true + | .dynamicBytesEq _ _ + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true /-- Stateful expression surfaces not yet carried by the generic Layer 2 body interface. These are the next storage/layout-style widening targets. -/ @@ -725,6 +740,7 @@ def exprTouchesUnsupportedStateSurface : Expr → Bool | .arrayLength _ | .arrayElement _ _ | .dynamicBytesEq _ _ => false | .mload a | .tload a | .calldataload a => exprTouchesUnsupportedStateSurface a + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true /-- Call-related surfaces that still sit outside the current generic Layer 2 body theorem: internal helper reuse, low-level calls, and foreign call hooks. -/ @@ -764,6 +780,7 @@ def exprTouchesUnsupportedCallSurface : Expr → Bool | .shl a b | .shr a b | .sar a b | .signextend a b => exprTouchesUnsupportedCallSurface a || exprTouchesUnsupportedCallSurface b | .dynamicBytesEq _ _ => false + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true /-- Internal helper-call surfaces not yet modeled compositionally in the current generic whole-contract theorem. -/ @@ -803,6 +820,7 @@ def exprTouchesUnsupportedHelperSurface : Expr → Bool | .shl a b | .shr a b | .sar a b | .signextend a b => exprTouchesUnsupportedHelperSurface a || exprTouchesUnsupportedHelperSurface b | .dynamicBytesEq _ _ => false + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true def exprListTouchesUnsupportedHelperSurface : List Expr → Bool | [] => false @@ -852,6 +870,7 @@ def exprTouchesInternalHelperSurface : Expr → Bool | .shl a b | .shr a b | .sar a b | .signextend a b => exprTouchesInternalHelperSurface a || exprTouchesInternalHelperSurface b | .dynamicBytesEq _ _ => false + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true /-- Foreign-call/library-hook surfaces still outside the current generic whole-contract theorem. -/ @@ -891,6 +910,7 @@ def exprTouchesUnsupportedForeignSurface : Expr → Bool | .shl a b | .shr a b | .sar a b | .signextend a b => exprTouchesUnsupportedForeignSurface a || exprTouchesUnsupportedForeignSurface b | .dynamicBytesEq _ _ => false + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true /-- Low-level call/runtime-mechanic surfaces still outside the current generic whole-contract theorem. -/ @@ -929,6 +949,7 @@ def exprTouchesUnsupportedLowLevelSurface : Expr → Bool | .shl a b | .shr a b | .sar a b | .signextend a b => exprTouchesUnsupportedLowLevelSurface a || exprTouchesUnsupportedLowLevelSurface b | .dynamicBytesEq _ _ => false + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true /-- Compatibility expression scan retained for the current generic-induction proofs. This intentionally preserves the pre-interface split meaning so the @@ -967,7 +988,8 @@ def exprTouchesUnsupportedContractSurface (expr : Expr) : Bool := | .returndataSize | .extcodesize _ | .returndataOptionalBoolAt _ | .externalCall _ _ | .internalCall _ _ | .arrayLength _ | .arrayElement _ _ | .storageArrayLength _ | .storageArrayElement _ _ - | .dynamicBytesEq _ _ => true + | .dynamicBytesEq _ _ + | .adtConstruct _ _ _ | .adtTag _ _ | .adtField _ _ _ _ _ => true mutual /-- Observable/effect-rich surfaces outside the current generic whole-contract @@ -975,7 +997,7 @@ theorem: richer returns, logs, typed errors, and raw external effect hooks. -/ def stmtTouchesUnsupportedEffectSurface : Stmt → Bool | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .emit _ _ | .rawLog _ _ _ - | .externalCallBind _ _ _ | .ecm _ _ => true + | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ => true | .letVar _ _ | .assignVar _ _ | .setStorage _ _ | .setStorageAddr _ _ | .require _ _ | .return _ | .mstore _ _ | .tstore _ _ | .stop | .setMapping _ _ _ | .setMappingWord _ _ _ _ @@ -985,6 +1007,7 @@ def stmtTouchesUnsupportedEffectSurface : Stmt → Bool | .storageArrayPush _ _ | .storageArrayPop _ | .setStorageArrayElement _ _ _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata | .internalCall _ _ | .internalCallAssign _ _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite _ thenBranch elseBranch => stmtListTouchesUnsupportedEffectSurface thenBranch || stmtListTouchesUnsupportedEffectSurface elseBranch @@ -1033,7 +1056,8 @@ def stmtTouchesUnsupportedCoreSurface : Stmt → Bool | .returnBytes _ | .returnStorageWords _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata | .emit _ _ | .internalCall _ _ | .internalCallAssign _ _ _ - | .rawLog _ _ _ | .externalCallBind _ _ _ | .ecm _ _ => false + | .rawLog _ _ _ | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true /-- State/layout-rich statement surfaces still outside the current whole-contract theorem. -/ @@ -1057,7 +1081,8 @@ def stmtTouchesUnsupportedStateSurface : Stmt → Bool | .returnBytes _ | .returnStorageWords _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata | .emit _ _ | .internalCall _ _ | .internalCallAssign _ _ _ - | .rawLog _ _ _ | .externalCallBind _ _ _ | .ecm _ _ => false + | .rawLog _ _ _ | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite cond thenBranch elseBranch => exprTouchesUnsupportedStateSurface cond || stmtListTouchesUnsupportedStateSurface thenBranch || @@ -1103,12 +1128,13 @@ def stmtTouchesUnsupportedCallSurface : Stmt → Bool exprTouchesUnsupportedCallSurface cond | .internalCall _ _ | .internalCallAssign _ _ _ => true | .calldatacopy _ _ _ - | .returndataCopy _ _ _ | .revertReturndata | .externalCallBind _ _ _ + | .returndataCopy _ _ _ | .revertReturndata | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ => true | .stop | .storageArrayPop _ | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .rawLog _ _ _ => false | .emit _ args => args.any exprTouchesUnsupportedCallSurface + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite cond thenBranch elseBranch => exprTouchesUnsupportedCallSurface cond || stmtListTouchesUnsupportedCallSurface thenBranch || @@ -1142,11 +1168,12 @@ def stmtTouchesUnsupportedHelperSurface : Stmt → Bool exprTouchesUnsupportedHelperSurface cond | .internalCall _ _ | .internalCallAssign _ _ _ => true | .stop | .calldatacopy _ _ _ - | .returndataCopy _ _ _ | .revertReturndata | .externalCallBind _ _ _ + | .returndataCopy _ _ _ | .revertReturndata | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ | .storageArrayPop _ | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .rawLog _ _ _ => false | .emit _ args => exprListTouchesUnsupportedHelperSurface args + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite cond thenBranch elseBranch => exprTouchesUnsupportedHelperSurface cond || stmtListTouchesUnsupportedHelperSurface thenBranch || @@ -1183,11 +1210,12 @@ def stmtTouchesInternalHelperSurface : Stmt → Bool exprTouchesInternalHelperSurface cond | .internalCall _ _ | .internalCallAssign _ _ _ => true | .stop | .calldatacopy _ _ _ - | .returndataCopy _ _ _ | .revertReturndata | .externalCallBind _ _ _ + | .returndataCopy _ _ _ | .revertReturndata | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ | .storageArrayPop _ | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .emit _ _ | .rawLog _ _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite cond thenBranch elseBranch => exprTouchesInternalHelperSurface cond || stmtListTouchesInternalHelperSurface thenBranch || @@ -1251,11 +1279,12 @@ def stmtTouchesExprInternalHelperSurface : Stmt → Bool exprTouchesInternalHelperSurface count | .internalCall _ _ | .internalCallAssign _ _ _ | .stop | .calldatacopy _ _ _ | .returndataCopy _ _ _ - | .revertReturndata | .externalCallBind _ _ _ | .ecm _ _ + | .revertReturndata | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ | .storageArrayPop _ | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .emit _ _ | .rawLog _ _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true /-- Recursive structural internal-helper transport at the current statement head. This isolates `ite` / `forEach` obligations whose proof burden is mainly @@ -1270,7 +1299,7 @@ def stmtTouchesStructuralInternalHelperSurface : Stmt → Bool | .return _ | .internalCall _ _ | .internalCallAssign _ _ _ | .stop | .setStorageAddr _ _ | .mstore _ _ | .tstore _ _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ - | .revertReturndata | .externalCallBind _ _ _ | .ecm _ _ + | .revertReturndata | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ | .setMapping _ _ _ | .setMappingWord _ _ _ _ | .setMappingPackedWord _ _ _ _ _ | .setMapping2 _ _ _ _ | .setMapping2Word _ _ _ _ _ | .setMappingUint _ _ _ @@ -1281,6 +1310,7 @@ def stmtTouchesStructuralInternalHelperSurface : Stmt → Bool | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .emit _ _ | .rawLog _ _ _ => false + | .unsafeBlock _ _ | .matchAdt _ _ _ => true def stmtTouchesUnsupportedForeignSurface : Stmt → Bool | .letVar _ value | .assignVar _ value | .setStorage _ value @@ -1305,7 +1335,7 @@ def stmtTouchesUnsupportedForeignSurface : Stmt → Bool exprTouchesUnsupportedForeignSurface value | .require cond _ | .return cond => exprTouchesUnsupportedForeignSurface cond - | .externalCallBind _ _ _ | .ecm _ _ => true + | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ => true | .stop | .internalCall _ _ | .internalCallAssign _ _ _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata @@ -1313,6 +1343,7 @@ def stmtTouchesUnsupportedForeignSurface : Stmt → Bool | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .rawLog _ _ _ => false | .emit _ args => args.any exprTouchesUnsupportedForeignSurface + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite cond thenBranch elseBranch => exprTouchesUnsupportedForeignSurface cond || stmtListTouchesUnsupportedForeignSurface thenBranch || @@ -1347,11 +1378,12 @@ def stmtTouchesUnsupportedLowLevelSurface : Stmt → Bool | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata => true | .stop - | .internalCall _ _ | .internalCallAssign _ _ _ | .externalCallBind _ _ _ + | .internalCall _ _ | .internalCallAssign _ _ _ | .externalCallBind _ _ _ | .tryExternalCallBind _ _ _ _ | .ecm _ _ | .storageArrayPop _ | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .rawLog _ _ _ => false | .emit _ args => args.any exprTouchesUnsupportedLowLevelSurface + | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .ite cond thenBranch elseBranch => exprTouchesUnsupportedLowLevelSurface cond || stmtListTouchesUnsupportedLowLevelSurface thenBranch || @@ -1385,7 +1417,8 @@ def stmtTouchesUnsupportedContractSurface (stmt : Stmt) : Bool := | .returnBytes _ | .returnStorageWords _ | .calldatacopy _ _ _ | .returndataCopy _ _ _ | .revertReturndata | .forEach _ _ _ | .emit _ _ | .internalCall _ _ | .internalCallAssign _ _ _ - | .rawLog _ _ _ | .externalCallBind _ _ _ | .ecm _ _ => true + | .rawLog _ _ _ | .externalCallBind _ _ _ | .ecm _ _ + | .tryExternalCallBind _ _ _ _ | .unsafeBlock _ _ | .matchAdt _ _ _ => true def stmtTouchesUnsupportedContractSurfaceWithEvents (events : List EventDef) (stmt : Stmt) : Bool := @@ -1615,7 +1648,7 @@ mutual | .requireError cond _ args => exprInternalHelperCallNames cond ++ exprListInternalHelperCallNames args | .revertError _ args | .emit _ args | .returnValues args - | .externalCallBind _ _ args | .ecm _ args => + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args | .ecm _ args => exprListInternalHelperCallNames args | .mstore offset value | .tstore offset value => exprInternalHelperCallNames offset ++ exprInternalHelperCallNames value @@ -1646,9 +1679,19 @@ mutual | .storageArrayPop _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .revertReturndata | .stop => [] + | .unsafeBlock _ body => stmtListExprHelperCallNames body + | .matchAdt _ _ branches => + matchAdtBranchesExprHelperCallNames branches termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega + def matchAdtBranchesExprHelperCallNames : List (String × List String × List Stmt) → List String + | [] => [] + | (_, _, body) :: rest => + stmtListExprHelperCallNames body ++ matchAdtBranchesExprHelperCallNames rest + termination_by bs => sizeOf bs + decreasing_by all_goals simp_wf; all_goals omega + def stmtListExprHelperCallNames : List Stmt → List String | [] => [] | stmt :: rest => @@ -1668,7 +1711,7 @@ mutual | .requireError cond _ args => exprInternalHelperCallNames cond ++ exprListInternalHelperCallNames args | .revertError _ args | .emit _ args | .returnValues args - | .externalCallBind _ _ args | .ecm _ args => + | .externalCallBind _ _ args | .tryExternalCallBind _ _ _ args | .ecm _ args => exprListInternalHelperCallNames args | .mstore offset value | .tstore offset value => exprInternalHelperCallNames offset ++ exprInternalHelperCallNames value @@ -1701,9 +1744,19 @@ mutual | .storageArrayPop _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .revertReturndata | .stop => [] + | .unsafeBlock _ body => stmtListInternalHelperCallNames body + | .matchAdt _ _ branches => + matchAdtBranchesInternalHelperCallNames branches termination_by s => sizeOf s decreasing_by all_goals simp_wf; all_goals omega + def matchAdtBranchesInternalHelperCallNames : List (String × List String × List Stmt) → List String + | [] => [] + | (_, _, body) :: rest => + stmtListInternalHelperCallNames body ++ matchAdtBranchesInternalHelperCallNames rest + termination_by bs => sizeOf bs + decreasing_by all_goals simp_wf; all_goals omega + def stmtListInternalHelperCallNames : List Stmt → List String | [] => [] | stmt :: rest => @@ -1782,6 +1835,25 @@ theorem exprHelperCallNames_nodup (fn : FunctionSpec) : (exprHelperCallNames fn).Nodup := by simpa [exprHelperCallNames] using List.eraseDups_nodup (stmtListExprHelperCallNames fn.body) +private theorem matchAdtBranchesExprSubsetInternal_aux + (listSubset : ∀ (stmts : List Stmt) {calleeName : String}, + calleeName ∈ stmtListExprHelperCallNames stmts → + calleeName ∈ stmtListInternalHelperCallNames stmts) + (branches : List (String × List String × List Stmt)) + {calleeName : String} + (hmem : calleeName ∈ matchAdtBranchesExprHelperCallNames branches) : + calleeName ∈ matchAdtBranchesInternalHelperCallNames branches := by + induction branches with + | nil => simp [matchAdtBranchesExprHelperCallNames] at hmem + | cons hd tl ih => + obtain ⟨vn, vl, body⟩ := hd + simp only [matchAdtBranchesExprHelperCallNames, + matchAdtBranchesInternalHelperCallNames, List.mem_append] at hmem ⊢ + rcases hmem with hbody | hrest + · exact Or.inl (listSubset body hbody) + · exact Or.inr (ih hrest) + +mutual private theorem stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNames (stmts : List Stmt) {calleeName : String} @@ -1820,13 +1892,34 @@ private theorem stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNam exact hstmt | revertError errorName args => simpa [stmtExprHelperCallNames, stmtInternalHelperCallNames] using hstmt + | unsafeBlock reason body => + simp only [stmtExprHelperCallNames, stmtInternalHelperCallNames] at hstmt ⊢ + exact stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNames body hstmt + | matchAdt adtName scrutinee branches => + simp only [stmtExprHelperCallNames, stmtInternalHelperCallNames] at hstmt ⊢ + exact matchAdtBranchesExprHelperCallNames_subset_internalHelperCallNames branches hstmt | _ => all_goals simpa [stmtExprHelperCallNames, stmtInternalHelperCallNames, List.mem_append, or_left_comm, or_assoc] using hstmt · exact Or.inr (stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNames rest hrest) -termination_by sizeOf stmts + 1 -decreasing_by all_goals (subst_vars; simp_wf; simp_all [List.cons.sizeOf_spec]; omega) +termination_by sizeOf stmts + +private theorem matchAdtBranchesExprHelperCallNames_subset_internalHelperCallNames + (branches : List (String × List String × List Stmt)) + {calleeName : String} + (hmem : calleeName ∈ matchAdtBranchesExprHelperCallNames branches) : + calleeName ∈ matchAdtBranchesInternalHelperCallNames branches := by + match branches with + | [] => simp [matchAdtBranchesExprHelperCallNames] at hmem + | (vn, vl, body) :: rest => + simp only [matchAdtBranchesExprHelperCallNames, + matchAdtBranchesInternalHelperCallNames, List.mem_append] at hmem ⊢ + rcases hmem with hbody | hrest + · exact Or.inl (stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNames body hbody) + · exact Or.inr (matchAdtBranchesExprHelperCallNames_subset_internalHelperCallNames rest hrest) +termination_by sizeOf branches +end theorem stmtExprHelperCallNames_subset_stmtInternalHelperCallNames (stmt : Stmt) : @@ -1931,6 +2024,7 @@ structure SupportedCompiledInternalHelperWitness (applySlotAliasRanges spec.fields spec.slotAliasRanges) spec.events spec.errors + spec.adtTypes sourceWitness.callee = Except.ok compiledStmt presentInRuntime : @@ -2070,6 +2164,7 @@ structure SupportedSpecSurface (spec : CompilationModel) : Prop where noEvents : spec.events = [] noErrors : spec.errors = [] noExternals : spec.externals = [] + noAdtTypes : spec.adtTypes = [] noFallback : ∀ fn ∈ spec.functions, fn.name != "fallback" noReceive : @@ -2288,6 +2383,20 @@ private theorem stmtListCompileCore_helperSurfaceClosed stmtTouchesUnsupportedHelperSurface, ih, Bool.or_false, Bool.false_or] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprCompileCore_helperSurfaceClosed hoffset, + exprCompileCore_helperSurfaceClosed hvalue, + ih, + Bool.or_false, Bool.false_or] + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprCompileCore_helperSurfaceClosed hoffset, + exprCompileCore_helperSurfaceClosed hvalue, + ih, + Bool.or_false, Bool.false_or] private theorem stmtListCompileCore_internalHelperCallNames_nil {scope : List String} @@ -2313,6 +2422,18 @@ private theorem stmtListCompileCore_internalHelperCallNames_nil simp only [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, ih, List.nil_append, List.append_nil] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListInternalHelperCallNames, + stmtInternalHelperCallNames, + exprCompileCore_internalHelperCallNames_nil hoffset, + exprCompileCore_internalHelperCallNames_nil hvalue, + ih, List.nil_append, List.append_nil] + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListInternalHelperCallNames, + stmtInternalHelperCallNames, + exprCompileCore_internalHelperCallNames_nil hoffset, + exprCompileCore_internalHelperCallNames_nil hvalue, + ih, List.nil_append, List.append_nil] private theorem stmtListTerminalCore_internalHelperCallNames_nil @@ -2350,6 +2471,18 @@ private theorem stmtListTerminalCore_internalHelperCallNames_nil ihThen, ihElse, stmtListCompileCore_internalHelperCallNames_nil hrest, List.nil_append, List.append_nil] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListInternalHelperCallNames, + stmtInternalHelperCallNames, + exprCompileCore_internalHelperCallNames_nil hoffset, + exprCompileCore_internalHelperCallNames_nil hvalue, + ih, List.nil_append, List.append_nil] + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListInternalHelperCallNames, + stmtInternalHelperCallNames, + exprCompileCore_internalHelperCallNames_nil hoffset, + exprCompileCore_internalHelperCallNames_nil hvalue, + ih, List.nil_append, List.append_nil] private theorem stmtListTerminalCore_helperSurfaceClosed @@ -2389,6 +2522,20 @@ private theorem stmtListTerminalCore_helperSurfaceClosed ihThen, ihElse, stmtListCompileCore_helperSurfaceClosed hrest, Bool.or_false, Bool.false_or] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprCompileCore_helperSurfaceClosed hoffset, + exprCompileCore_helperSurfaceClosed hvalue, + ih, + Bool.or_false, Bool.false_or] + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprCompileCore_helperSurfaceClosed hoffset, + exprCompileCore_helperSurfaceClosed hvalue, + ih, + Bool.or_false, Bool.false_or] private theorem supportedStmtList_letStorageField_helperSurfaceClosed {tmp fieldName : String} : @@ -2918,6 +3065,8 @@ mutual | arrayLength _ | storageArrayLength _ | dynamicBytesEq _ _ | externalCall _ _ => simp [exprTouchesInternalHelperSurface] + | adtConstruct _ _ _ | adtTag _ _ | adtField _ _ _ _ _ => + simp [exprTouchesUnsupportedHelperSurface] at hsurface | extcodesize a | returndataOptionalBoolAt a => simp [exprTouchesInternalHelperSurface] @@ -3051,8 +3200,10 @@ mutual simp [stmtTouchesInternalHelperSurface, exprTouchesInternalHelperSurface_eq_false_of_helperSurfaceClosed hsurface.1, stmtListTouchesInternalHelperSurface_eq_false_of_helperSurfaceClosed hsurface.2] + | unsafeBlock _ _ | matchAdt _ _ _ => + simp [stmtTouchesUnsupportedHelperSurface] at hsurface | stop | calldatacopy _ _ _ | returndataCopy _ _ _ | revertReturndata - | externalCallBind _ _ _ | ecm _ _ | storageArrayPop _ | requireError _ _ _ + | externalCallBind _ _ _ | tryExternalCallBind _ _ _ _ | ecm _ _ | storageArrayPop _ | requireError _ _ _ | revertError _ _ | returnValues _ | returnArray _ | returnBytes _ | returnStorageWords _ | emit _ _ | rawLog _ _ _ => simp [stmtTouchesInternalHelperSurface] @@ -3300,6 +3451,9 @@ private theorem exprTouchesUnsupportedCallSurface_eq_featureOr | constructorArg _ | blobbasefee | calldatasize | returndataSize => simp [exprTouchesUnsupportedCallSurface, exprTouchesUnsupportedHelperSurface, exprTouchesUnsupportedForeignSurface, exprTouchesUnsupportedLowLevelSurface] + | adtConstruct _ _ _ | adtTag _ _ | adtField _ _ _ _ _ => + simp [exprTouchesUnsupportedCallSurface, exprTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedForeignSurface, exprTouchesUnsupportedLowLevelSurface] | internalCall _ _ | externalCall _ _ => simp [exprTouchesUnsupportedCallSurface, exprTouchesUnsupportedHelperSurface, exprTouchesUnsupportedForeignSurface, exprTouchesUnsupportedLowLevelSurface] @@ -3565,6 +3719,8 @@ private theorem exprTouchesUnsupportedContractSurface_eq_false_of_featureClosed simp only [exprTouchesUnsupportedCallSurface] at hcalls simp [exprTouchesUnsupportedContractSurface, exprTouchesUnsupportedContractSurface_eq_false_of_featureClosed a hcore hstate hcalls] + | adtConstruct _ _ _ | adtTag _ _ | adtField _ _ _ _ _ => + cases hcore | mapping _ _ | mappingWord _ _ _ | mappingPackedWord _ _ _ _ | mapping2 _ _ _ | mapping2Word _ _ _ _ | mappingUint _ _ | mappingChain _ _ | structMember _ _ _ | structMember2 _ _ _ _ @@ -3854,6 +4010,8 @@ theorem exprTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed | chainid | msgValue | blockTimestamp | blockNumber | blobbasefee | calldatasize => simp [exprTouchesUnsupportedHelperSurface] + | adtConstruct _ _ _ | adtTag _ _ | adtField _ _ _ _ _ => + simp [exprTouchesUnsupportedContractSurface] at hsurface | storage _ | storageAddr _ | internalCall _ _ | externalCall _ _ | constructorArg _ | keccak256 _ _ | returndataSize | extcodesize _ @@ -3951,6 +4109,7 @@ theorem stmtTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed exact ⟨⟨exprTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed hsurface.1.1, stmtListTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed hsurface.1.2⟩, stmtListTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed hsurface.2⟩ + | tryExternalCallBind _ _ _ _ | unsafeBlock _ _ | matchAdt _ _ _ | setMapping _ _ _ | setMappingWord _ _ _ _ | setMappingPackedWord _ _ _ _ _ | setMapping2 _ _ _ _ | setMapping2Word _ _ _ _ _ | setMappingUint _ _ _ @@ -4343,6 +4502,14 @@ private theorem stmtListCompileCore_usesArrayElement_false exprCompileCore_usesArrayElement_false hvalue, Bool.false_or]; assumption | stop _ ih => simp only [stmtListUsesArrayElement, stmtUsesArrayElement, Bool.false_or]; assumption + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesArrayElement, stmtUsesArrayElement, + exprCompileCore_usesArrayElement_false hoffset, + exprCompileCore_usesArrayElement_false hvalue, Bool.false_or]; assumption + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesArrayElement, stmtUsesArrayElement, + exprCompileCore_usesArrayElement_false hoffset, + exprCompileCore_usesArrayElement_false hvalue, Bool.false_or]; assumption -- Helper: StmtListTerminalCore never uses arrayElement private theorem stmtListTerminalCore_usesArrayElement_false @@ -4370,6 +4537,14 @@ private theorem stmtListTerminalCore_usesArrayElement_false simp only [stmtListUsesArrayElement, stmtUsesArrayElement, exprCompileCore_usesArrayElement_false hcond, ih_then, ih_else, stmtListCompileCore_usesArrayElement_false hCompile, Bool.false_or] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesArrayElement, stmtUsesArrayElement, + exprCompileCore_usesArrayElement_false hoffset, + exprCompileCore_usesArrayElement_false hvalue, Bool.false_or]; assumption + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesArrayElement, stmtUsesArrayElement, + exprCompileCore_usesArrayElement_false hoffset, + exprCompileCore_usesArrayElement_false hvalue, Bool.false_or]; assumption -- Helper: StmtListCompileCore never uses storageArrayElement private theorem stmtListCompileCore_usesStorageArrayElement_false @@ -4392,6 +4567,14 @@ private theorem stmtListCompileCore_usesStorageArrayElement_false exprCompileCore_usesStorageArrayElement_false hvalue, Bool.false_or]; assumption | stop _ ih => simp only [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, Bool.false_or]; assumption + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprCompileCore_usesStorageArrayElement_false hoffset, + exprCompileCore_usesStorageArrayElement_false hvalue, Bool.false_or]; assumption + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprCompileCore_usesStorageArrayElement_false hoffset, + exprCompileCore_usesStorageArrayElement_false hvalue, Bool.false_or]; assumption -- Helper: StmtListTerminalCore never uses storageArrayElement private theorem stmtListTerminalCore_usesStorageArrayElement_false @@ -4419,6 +4602,14 @@ private theorem stmtListTerminalCore_usesStorageArrayElement_false simp only [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, exprCompileCore_usesStorageArrayElement_false hcond, ih_then, ih_else, stmtListCompileCore_usesStorageArrayElement_false hCompile, Bool.false_or] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprCompileCore_usesStorageArrayElement_false hoffset, + exprCompileCore_usesStorageArrayElement_false hvalue, Bool.false_or]; assumption + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprCompileCore_usesStorageArrayElement_false hoffset, + exprCompileCore_usesStorageArrayElement_false hvalue, Bool.false_or]; assumption -- Helper: StmtListCompileCore never uses dynamicBytesEq private theorem stmtListCompileCore_usesDynamicBytesEq_false @@ -4441,6 +4632,14 @@ private theorem stmtListCompileCore_usesDynamicBytesEq_false exprCompileCore_usesDynamicBytesEq_false hvalue, Bool.false_or]; assumption | stop _ ih => simp only [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, Bool.false_or]; assumption + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprCompileCore_usesDynamicBytesEq_false hoffset, + exprCompileCore_usesDynamicBytesEq_false hvalue, Bool.false_or]; assumption + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprCompileCore_usesDynamicBytesEq_false hoffset, + exprCompileCore_usesDynamicBytesEq_false hvalue, Bool.false_or]; assumption -- Helper: StmtListTerminalCore never uses dynamicBytesEq private theorem stmtListTerminalCore_usesDynamicBytesEq_false @@ -4468,6 +4667,14 @@ private theorem stmtListTerminalCore_usesDynamicBytesEq_false simp only [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, exprCompileCore_usesDynamicBytesEq_false hcond, ih_then, ih_else, stmtListCompileCore_usesDynamicBytesEq_false hCompile, Bool.false_or] + | mstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprCompileCore_usesDynamicBytesEq_false hoffset, + exprCompileCore_usesDynamicBytesEq_false hvalue, Bool.false_or]; assumption + | tstore hoffset _ hvalue _ _ ih => + simp only [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprCompileCore_usesDynamicBytesEq_false hoffset, + exprCompileCore_usesDynamicBytesEq_false hvalue, Bool.false_or]; assumption -- Helper for append: stmtListUsesArrayElement distributes over append private theorem stmtListUsesArrayElement_append (xs ys : List Stmt) : @@ -5114,6 +5321,18 @@ theorem SupportedSpecExceptMappingWrites.noExternals spec.externals = [] := hSupported.surface.noExternals +theorem SupportedSpec.noAdtTypes + {spec : CompilationModel} {selectors : List Nat} + (hSupported : SupportedSpec spec selectors) : + spec.adtTypes = [] := + hSupported.surface.noAdtTypes + +theorem SupportedSpecExceptMappingWrites.noAdtTypes + {spec : CompilationModel} {selectors : List Nat} + (hSupported : SupportedSpecExceptMappingWrites spec selectors) : + spec.adtTypes = [] := + hSupported.surface.noAdtTypes + theorem SupportedSpec.noFallback {spec : CompilationModel} {selectors : List Nat} (hSupported : SupportedSpec spec selectors) : @@ -5316,6 +5535,7 @@ def counter_supported_spec : SupportedSpec counterSupportedSpecModel { noEvents := rfl noErrors := rfl noExternals := rfl + noAdtTypes := rfl noFallback := counter_noFallback noReceive := counter_noReceive } constructor := by @@ -5398,6 +5618,7 @@ def simpleStorage_supported_spec : SupportedSpec simpleStorageSupportedSpecModel { noEvents := rfl noErrors := rfl noExternals := rfl + noAdtTypes := rfl noFallback := simpleStorage_noFallback noReceive := simpleStorage_noReceive } constructor := by diff --git a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanAdapter.lean b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanAdapter.lean index d3c1b9159..1bc7fea12 100644 --- a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanAdapter.lean +++ b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanAdapter.lean @@ -4,6 +4,7 @@ import Compiler.Proofs.MappingSlot import Compiler.Proofs.YulGeneration.Calldata import EvmYul.Yul.Ast import EvmYul.UInt256 +import Mathlib.Data.Finmap namespace Compiler.Proofs.YulGeneration.Backends @@ -79,6 +80,195 @@ def lowerProgram (stmts : List YulStmt) : Except AdapterError EvmYul.Yul.Ast.Stm let stmts' ← lowerStmts stmts pure (.Block stmts') +/-! ## Native EVMYulLean runtime lowering + +The historical `lowerExpr` path above is intentionally preserved because the +existing bridge/report machinery reasons about the old structural adapter. +The native runtime path below is the #1737 migration entry point: it lowers +known Yul builtins to EVMYulLean primops (`.inl`) and leaves user/helper calls +as Yul function calls (`.inr`). +-/ + +/-- Map runtime Yul builtin names to native EVMYulLean primops. + +This is broader than `lookupPrimOp`: it includes effectful/runtime operations +needed by `EvmYul.Yul.exec`, while unknown names remain user/helper functions. +-/ +def lookupRuntimePrimOp : String → Option (EvmYul.Operation .Yul) + | "stop" => some .STOP + | "add" => some .ADD + | "sub" => some .SUB + | "mul" => some .MUL + | "div" => some .DIV + | "sdiv" => some .SDIV + | "mod" => some .MOD + | "smod" => some .SMOD + | "addmod" => some .ADDMOD + | "mulmod" => some .MULMOD + | "exp" => some .EXP + | "signextend" => some .SIGNEXTEND + | "lt" => some .LT + | "gt" => some .GT + | "slt" => some .SLT + | "sgt" => some .SGT + | "eq" => some .EQ + | "iszero" => some .ISZERO + | "and" => some .AND + | "or" => some .OR + | "xor" => some .XOR + | "not" => some .NOT + | "byte" => some .BYTE + | "shl" => some .SHL + | "shr" => some .SHR + | "sar" => some .SAR + | "keccak256" => some .KECCAK256 + | "address" => some .ADDRESS + | "balance" => some .BALANCE + | "origin" => some .ORIGIN + | "caller" => some .CALLER + | "callvalue" => some .CALLVALUE + | "calldataload" => some .CALLDATALOAD + | "calldatacopy" => some .CALLDATACOPY + | "calldatasize" => some .CALLDATASIZE + | "codesize" => some .CODESIZE + | "codecopy" => some .CODECOPY + | "gasprice" => some .GASPRICE + | "extcodesize" => some .EXTCODESIZE + | "extcodecopy" => some .EXTCODECOPY + | "extcodehash" => some .EXTCODEHASH + | "returndatasize" => some .RETURNDATASIZE + | "returndatacopy" => some .RETURNDATACOPY + | "blockhash" => some .BLOCKHASH + | "coinbase" => some .COINBASE + | "timestamp" => some .TIMESTAMP + | "number" => some .NUMBER + | "gaslimit" => some .GASLIMIT + | "chainid" => some .CHAINID + | "blobbasefee" => some .BLOBBASEFEE + | "selfbalance" => some .SELFBALANCE + | "mload" => some .MLOAD + | "mstore" => some .MSTORE + | "mstore8" => some .MSTORE8 + | "sload" => some .SLOAD + | "sstore" => some .SSTORE + | "tload" => some .TLOAD + | "tstore" => some .TSTORE + | "msize" => some .MSIZE + | "gas" => some .GAS + | "pop" => some .POP + | "log0" => some .LOG0 + | "log1" => some .LOG1 + | "log2" => some .LOG2 + | "log3" => some .LOG3 + | "log4" => some .LOG4 + | "return" => some .RETURN + | "revert" => some .REVERT + | "call" => some .CALL + | "staticcall" => some .STATICCALL + | "delegatecall" => some .DELEGATECALL + | "callcode" => some .CALLCODE + | _ => none + +def lowerExprNative : YulExpr → EvmYul.Yul.Ast.Expr + | .lit n => .Lit (EvmYul.UInt256.ofNat n) + | .hex n => .Lit (EvmYul.UInt256.ofNat n) + | .str s => .Var s + | .ident name => .Var name + | .call func args => + let loweredArgs := args.map lowerExprNative + match lookupRuntimePrimOp func with + | some prim => .Call (.inl prim) loweredArgs + | none => .Call (.inr func) loweredArgs + +mutual +partial def lowerStmtsNative : + List YulStmt → Except AdapterError (List EvmYul.Yul.Ast.Stmt) + | [] => pure [] + | stmt :: rest => do + let stmts' ← lowerStmtGroupNative stmt + let rest' ← lowerStmtsNative rest + pure (stmts' ++ rest') + +/-- Lower a statement for native `EvmYul.Yul.exec`. + +Top-level function definitions are intentionally rejected here; callers that +need executable contracts should use `lowerRuntimeContractNative`, which places +them in `YulContract.functions`. +-/ +partial def lowerStmtGroupNative : + YulStmt → Except AdapterError (List EvmYul.Yul.Ast.Stmt) + | .comment _ => pure [.Block []] + | .let_ name value => pure [.Let [name] (some (lowerExprNative value))] + | .letMany names value => pure [.Let names (some (lowerExprNative value))] + | .assign name value => pure [.Let [name] (some (lowerExprNative value))] + | .expr e => pure [.ExprStmtCall (lowerExprNative e)] + | .leave => pure [.Leave] + | .if_ cond body => do + let body' ← lowerStmtsNative body + pure [.If (lowerExprNative cond) body'] + | .for_ init cond post body => do + let init' ← lowerStmtsNative init + let post' ← lowerStmtsNative post + let body' ← lowerStmtsNative body + pure (init' ++ [.For (lowerExprNative cond) post' body']) + | .switch expr cases defaultCase => do + let lowerCase := fun ((tag, block) : Nat × List YulStmt) => do + let block' ← lowerStmtsNative block + pure (EvmYul.UInt256.ofNat tag, block') + let cases' ← cases.mapM lowerCase + let default' ← lowerStmtsNative (defaultCase.getD []) + pure [.Switch (lowerExprNative expr) cases' default'] + | .block stmts => do + let stmts' ← lowerStmtsNative stmts + pure [.Block stmts'] + | .funcDef name _ _ _ => + throw s!"native EVMYulLean statement lowering cannot inline function definition '{name}'; use lowerRuntimeContractNative" +end + +def lowerFunctionDefinitionNative (params rets : List String) (body : List YulStmt) : + Except AdapterError EvmYul.Yul.Ast.FunctionDefinition := do + let body' ← lowerStmtsNative body + pure (.Def params rets body') + +abbrev NativeFunctionMap := + Finmap (fun (_ : EvmYul.Yul.Ast.YulFunctionName) => + EvmYul.Yul.Ast.FunctionDefinition) + +private def insertNativeFunction + (functions : NativeFunctionMap) + (name : String) (definition : EvmYul.Yul.Ast.FunctionDefinition) : + Except AdapterError NativeFunctionMap := + if functions.lookup name |>.isSome then + throw s!"duplicate native Yul function definition '{name}'" + else + pure (functions.insert name definition) + +partial def lowerRuntimeContractNativeAux + (stmts : List YulStmt) + (dispatcherAcc : List EvmYul.Yul.Ast.Stmt) + (functionsAcc : NativeFunctionMap) : + Except AdapterError (List EvmYul.Yul.Ast.Stmt × NativeFunctionMap) := do + match stmts with + | [] => pure (dispatcherAcc.reverse, functionsAcc) + | .funcDef name params rets body :: rest => + let definition ← lowerFunctionDefinitionNative params rets body + let functionsAcc ← insertNativeFunction functionsAcc name definition + lowerRuntimeContractNativeAux rest dispatcherAcc functionsAcc + | stmt :: rest => + let lowered ← lowerStmtGroupNative stmt + lowerRuntimeContractNativeAux rest (lowered.reverse ++ dispatcherAcc) functionsAcc + +/-- Lower generated runtime Yul into an executable EVMYulLean contract shape. -/ +def lowerRuntimeContractNative (stmts : List YulStmt) : + Except AdapterError EvmYul.Yul.Ast.YulContract := do + let emptyFunctions : NativeFunctionMap := ∅ + let (dispatcher, functions) ← + lowerRuntimeContractNativeAux stmts [] emptyFunctions + pure { + dispatcher := .Block dispatcher, + functions := functions + } + /-- Map a Verity builtin name to the corresponding EVMYulLean PrimOp. Returns `none` for Verity-specific helpers (e.g. `mappingSlot`) that have no direct EVMYulLean counterpart. -/ diff --git a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean index aeb231cf6..7d72e376b 100644 --- a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean +++ b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean @@ -323,7 +323,7 @@ private theorem genParamLoadBodyFrom_cons_scalar match hTy : param.ty, hScalar with | ParamType.uint256, _ | ParamType.int256, _ | ParamType.uint8, _ | ParamType.address, _ | ParamType.bool, _ | ParamType.bytes32, _ => - simp [genParamLoadBodyFrom, hTy] + simp [genParamLoadBodyFrom, genSingleParamLoad, hTy] /-- For scalar-only parameter lists, `genParamLoadBodyFrom` with the calldata loader produces only bridged statements. Each per-parameter stmt block is @@ -399,21 +399,21 @@ private theorem genParamLoadBodyFrom_calldataload_static_scalar_bridged · exact genScalarLoad_calldataload_bridged paramName paramTy headOffset hScalar · exact hTail | @fixedArray elemTy n hElem => - simp only [genParamLoadBodyFrom, + simp [genParamLoadBodyFrom, genSingleParamLoad, isDynamicParamType_false_of_static_scalar _ (IsStaticScalarParamType.fixedArray hElem)] apply BridgedStmts_append - · by_cases hN : n == 0 - · simp [hN] - exact (genStaticTypeLoads_calldataload_bridged paramName - (.fixedArray elemTy n) headOffset (IsStaticScalarParamType.fixedArray hElem)) - · simp [hN] - apply BridgedStmts_append - · exact (genStaticTypeLoads_calldataload_bridged paramName + · by_cases hN : n = 0 + · simpa only [if_pos hN] using + (genStaticTypeLoads_calldataload_bridged paramName (.fixedArray elemTy n) headOffset (IsStaticScalarParamType.fixedArray hElem)) - · simpa [hN] using fixedArrayFirstAlias_bridged paramName elemTy n + · simpa only [if_neg hN] using + (BridgedStmts_append + (genStaticTypeLoads_calldataload_bridged paramName + (.fixedArray elemTy n) headOffset (IsStaticScalarParamType.fixedArray hElem)) + (by simpa [hN] using fixedArrayFirstAlias_bridged paramName elemTy n)) · exact hTail | @tuple elemTys hElems => - simp only [genParamLoadBodyFrom, + simp [genParamLoadBodyFrom, genSingleParamLoad, isDynamicParamType_false_of_static_scalar _ (IsStaticScalarParamType.tuple hElems)] apply BridgedStmts_append · exact genStaticTypeLoads_calldataload_bridged paramName (.tuple elemTys) @@ -499,7 +499,7 @@ theorem compileStmt_binding_leaf_bridged ∀ {stmt : Stmt}, BridgedSourceBindingStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -545,7 +545,7 @@ theorem compileStmtList_binding_leaf_bridged BridgedSourceBindingStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmts = .ok out → + inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -559,13 +559,13 @@ theorem compileStmtList_binding_leaf_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - isInternal (collectStmtNames head ++ inScopeNames) tail with + isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -590,7 +590,7 @@ theorem compileStmt_pure_binding_bridged ∀ {stmt : Stmt}, BridgedSourcePureBindingStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -635,7 +635,7 @@ theorem compileStmtList_pure_binding_bridged BridgedSourcePureBindingStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmts = .ok out → + inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -649,13 +649,13 @@ theorem compileStmtList_pure_binding_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - isInternal (collectStmtNames head ++ inScopeNames) tail with + isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -688,7 +688,8 @@ inductive BridgedSourceStorageStmt (fields : List Field) : Stmt → Prop (hNotMapping : isMapping fields field = false) (hFind : findFieldWithResolvedSlot fields field = - some ({ f with packedBits := none, aliasSlots := [] }, slot)) : + some ({ f with packedBits := none, aliasSlots := [] }, slot)) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) : BridgedSourceStorageStmt fields (.setStorage field value) def BridgedSourceStorageStmts (fields : List Field) (stmts : List Stmt) : Prop := @@ -706,28 +707,33 @@ theorem compileStmt_setStorage_singleSlot_pure_bridged (hNotMapping : isMapping fields field = false) (hFind : findFieldWithResolvedSlot fields field = - some ({ f with packedBits := none, aliasSlots := [] }, slot)) : + some ({ f with packedBits := none, aliasSlots := [] }, slot)) + (hNotAdt : ∀ name maxFields, f.ty ≠ FieldType.adt name maxFields) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStorage field value) = .ok out → + inScopeNames [] (.setStorage field value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk unfold compileSetStorage at hOk simp [hNotMapping, hFind] at hOk - cases hExpr : compileExpr fields dynamicSource value with - | error err => - simp [hExpr] at hOk - | ok valueExpr => - simp [hExpr] at hOk - subst out - have hBridged : BridgedExpr valueExpr := - compileExpr_bridgedSource fields dynamicSource hValue hExpr - intro yulStmt hMem - simp only [List.mem_singleton] at hMem - subst yulStmt - exact BridgedStmt.straight _ - (BridgedStraightStmt.expr_sstore_lit slot valueExpr hBridged) + cases hty : f.ty with + | adt name maxFields => + exact False.elim (hNotAdt name maxFields hty) + | uint256 | address | dynamicArray | mappingTyped | mappingStruct | mappingStruct2 => + cases hExpr : compileExpr fields dynamicSource value with + | error err => + simp [hExpr, hty] at hOk + | ok valueExpr => + simp [hExpr, hty] at hOk + subst out + have hBridged : BridgedExpr valueExpr := + compileExpr_bridgedSource fields dynamicSource hValue hExpr + intro yulStmt hMem + simp only [List.mem_singleton] at hMem + subst yulStmt + exact BridgedStmt.straight _ + (BridgedStraightStmt.expr_sstore_lit slot valueExpr hBridged) /-- Each statement in the storage fragment compiles to Yul satisfying `BridgedStmts`. -/ @@ -738,17 +744,17 @@ theorem compileStmt_storage_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceStorageStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with | pureBinding hPure => exact compileStmt_pure_binding_bridged fields events errors dynamicSource internalRetNames isInternal inScopeNames hPure hOk - | setStorage field value f slot hValue hNotMapping hFind => + | setStorage field value f slot hValue hNotMapping hFind hNotAdt => exact compileStmt_setStorage_singleSlot_pure_bridged fields events errors dynamicSource internalRetNames isInternal inScopeNames field value f slot - hValue hNotMapping hFind hOk + hValue hNotMapping hFind hNotAdt hOk /-- Lists made of pure `letVar`/`assignVar` statements and unpacked single-slot `setStorage` statements compile to Yul lists satisfying `BridgedStmts`. -/ @@ -760,7 +766,7 @@ theorem compileStmtList_storage_fragment_bridged BridgedSourceStorageStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmts = .ok out → + inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -774,13 +780,13 @@ theorem compileStmtList_storage_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - isInternal (collectStmtNames head ++ inScopeNames) tail with + isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -831,7 +837,7 @@ private theorem compileStmt_stop_bridged (isInternal : Bool) (inScopeNames : List String) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames .stop = .ok out → + inScopeNames [] .stop = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, Pure.pure, Except.pure] at hOk @@ -851,7 +857,7 @@ private theorem compileStmt_return_external_bridged {value : Expr} (hValue : BridgedSourceExpr value) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.return value) = .ok out → + (isInternal := false) inScopeNames [] (.return value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -880,7 +886,7 @@ theorem compileStmt_terminator_external_bridged ∀ {stmt : Stmt}, BridgedSourceTerminatorStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -900,7 +906,7 @@ theorem compileStmtList_terminator_external_bridged BridgedSourceTerminatorStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -914,13 +920,13 @@ theorem compileStmtList_terminator_external_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - false (collectStmtNames head ++ inScopeNames) tail with + false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -963,7 +969,7 @@ theorem compileStmt_return_internal_bridged {value : Expr} (hValue : BridgedSourceExpr value) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames (.return value) = .ok out → + (isInternal := true) inScopeNames [] (.return value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -994,7 +1000,7 @@ theorem compileStmt_internal_return_bridged ∀ {stmt : Stmt}, BridgedSourceInternalReturnStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -1011,7 +1017,7 @@ theorem compileStmtList_internal_return_bridged BridgedSourceInternalReturnStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1025,13 +1031,13 @@ theorem compileStmtList_internal_return_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - true (collectStmtNames head ++ inScopeNames) tail with + true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1119,7 +1125,7 @@ theorem compileStmt_require_bridged BridgedExpr failCond) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.require cond message) = .ok out → + inScopeNames [] (.require cond message) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1145,7 +1151,7 @@ theorem compileStmtList_require_bridged BridgedSourceRequireStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmts = .ok out → + inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1159,13 +1165,13 @@ theorem compileStmtList_require_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - isInternal (collectStmtNames head ++ inScopeNames) tail with + isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1246,7 +1252,7 @@ theorem compileStmt_setMapping_singleSlot_bridged (hSlots : findFieldWriteSlots fields field = some [slot]) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping field key value) = .ok out → + inScopeNames [] (.setMapping field key value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1276,7 +1282,7 @@ theorem compileStmt_setMappingUint_singleSlot_bridged (hSlots : findFieldWriteSlots fields field = some [slot]) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingUint field key value) = .ok out → + inScopeNames [] (.setMappingUint field key value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1302,7 +1308,7 @@ theorem compileStmt_mappingWrite_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWriteStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -1325,7 +1331,7 @@ theorem compileStmtList_mappingWrite_bridged BridgedSourceMappingWriteStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1339,13 +1345,13 @@ theorem compileStmtList_mappingWrite_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - isInternal (collectStmtNames head ++ inScopeNames) tail with + isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1411,7 +1417,7 @@ theorem compileStmt_external_body_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceExternalBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -1436,7 +1442,7 @@ theorem compileStmtList_external_body_fragment_bridged BridgedSourceExternalBodyStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1450,13 +1456,13 @@ theorem compileStmtList_external_body_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - false (collectStmtNames head ++ inScopeNames) tail with + false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1483,7 +1489,7 @@ theorem compileStmt_internal_body_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceInternalBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -1511,7 +1517,7 @@ theorem compileStmtList_internal_body_fragment_bridged BridgedSourceInternalBodyStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1525,13 +1531,13 @@ theorem compileStmtList_internal_body_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - true (collectStmtNames head ++ inScopeNames) tail with + true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1606,7 +1612,7 @@ theorem compileStmt_ite_external_body_fragment_bridged (hElse : BridgedSourceExternalBodyStmts fields dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := false) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1615,12 +1621,12 @@ theorem compileStmt_ite_external_body_fragment_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -1671,7 +1677,7 @@ theorem compileStmt_ite_internal_body_fragment_bridged (hElse : BridgedSourceInternalBodyStmts fields dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := true) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1680,12 +1686,12 @@ theorem compileStmt_ite_internal_body_fragment_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -1733,7 +1739,7 @@ theorem compileStmt_external_structured_body_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceExternalStructuredBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -1753,7 +1759,7 @@ theorem compileStmtList_external_structured_body_fragment_bridged BridgedSourceExternalStructuredBodyStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1767,13 +1773,13 @@ theorem compileStmtList_external_structured_body_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - false (collectStmtNames head ++ inScopeNames) tail with + false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1800,7 +1806,7 @@ theorem compileStmt_internal_structured_body_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceInternalStructuredBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -1820,7 +1826,7 @@ theorem compileStmtList_internal_structured_body_fragment_bridged BridgedSourceInternalStructuredBodyStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -1834,13 +1840,13 @@ theorem compileStmtList_internal_structured_body_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - true (collectStmtNames head ++ inScopeNames) tail with + true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -1913,7 +1919,7 @@ theorem compileStmt_ite_external_nested_body_fragment_bridged (hElse : BridgedSourceExternalStructuredBodyStmts fields dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := false) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1922,12 +1928,12 @@ theorem compileStmt_ite_external_nested_body_fragment_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -1978,7 +1984,7 @@ theorem compileStmt_ite_internal_nested_body_fragment_bridged (hElse : BridgedSourceInternalStructuredBodyStmts fields dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := true) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -1987,12 +1993,12 @@ theorem compileStmt_ite_internal_nested_body_fragment_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -2038,7 +2044,7 @@ theorem compileStmt_external_nested_body_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceExternalNestedBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -2056,7 +2062,7 @@ theorem compileStmtList_external_nested_body_fragment_bridged BridgedSourceExternalNestedBodyStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -2070,13 +2076,13 @@ theorem compileStmtList_external_nested_body_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - false (collectStmtNames head ++ inScopeNames) tail with + false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -2102,7 +2108,7 @@ theorem compileStmt_internal_nested_body_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceInternalNestedBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -2120,7 +2126,7 @@ theorem compileStmtList_internal_nested_body_fragment_bridged BridgedSourceInternalNestedBodyStmts fields dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -2134,13 +2140,13 @@ theorem compileStmtList_internal_nested_body_fragment_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - true (collectStmtNames head ++ inScopeNames) tail with + true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -2228,7 +2234,7 @@ mutual BridgedSourceExternalRecursiveBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -2242,12 +2248,12 @@ mutual simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -2293,7 +2299,7 @@ mutual BridgedSourceExternalRecursiveBodyStmts fields dynamicSource stmts → ∀ (inScopeNames : List String) {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts hSource inScopeNames out hOk cases hSource with @@ -2305,13 +2311,13 @@ mutual | @cons head tail hHead hTail => simp only [compileStmtList, bind, Except.bind] at hOk cases hHeadCompile : compileStmt fields events errors dynamicSource - internalRetNames false inScopeNames head with + internalRetNames false inScopeNames [] head with | error err => simp [hHeadCompile] at hOk | ok headOut => simp [hHeadCompile] at hOk cases hTailCompile : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTailCompile] at hOk | ok tailOut => @@ -2335,7 +2341,7 @@ mutual BridgedSourceInternalRecursiveBodyStmt fields dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -2349,12 +2355,12 @@ mutual simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -2400,7 +2406,7 @@ mutual BridgedSourceInternalRecursiveBodyStmts fields dynamicSource stmts → ∀ (inScopeNames : List String) {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts hSource inScopeNames out hOk cases hSource with @@ -2412,13 +2418,13 @@ mutual | @cons head tail hHead hTail => simp only [compileStmtList, bind, Except.bind] at hOk cases hHeadCompile : compileStmt fields events errors dynamicSource - internalRetNames true inScopeNames head with + internalRetNames true inScopeNames [] head with | error err => simp [hHeadCompile] at hOk | ok headOut => simp [hHeadCompile] at hOk cases hTailCompile : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTailCompile] at hOk | ok tailOut => @@ -2464,7 +2470,7 @@ theorem compileStmt_memoryWrite_bridged ∀ {stmt : Stmt}, BridgedSourceMemoryWriteStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -2519,7 +2525,7 @@ theorem compileStmtList_memoryWrite_bridged BridgedSourceMemoryWriteStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -2533,13 +2539,12 @@ theorem compileStmtList_memoryWrite_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -2575,11 +2580,11 @@ theorem compileStmt_forEach_with_bridged_body (hCount : BridgedSourceExpr count) (hBody : ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal (varName :: inScopeNames) body = .ok out → + isInternal (varName :: inScopeNames) [] body = .ok out → BridgedStmts out) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.forEach varName count body) = .ok out → + inScopeNames [] (.forEach varName count body) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -2588,7 +2593,7 @@ theorem compileStmt_forEach_with_bridged_body | ok countExpr => simp [hCExpr] at hOk cases hBodyOk : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (varName :: inScopeNames) body with + internalRetNames isInternal (varName :: inScopeNames) [] body with | error err => simp [hBodyOk] at hOk | ok bodyOut => simp [hBodyOk, Pure.pure, Except.pure] at hOk @@ -2796,7 +2801,7 @@ theorem compileStmt_revertError_zero_bridged (hZeroParams : errorDef.params = []) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.revertError errorName []) = .ok out → + inScopeNames [] (.revertError errorName []) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind, hLookup, compileExprList, @@ -2817,7 +2822,7 @@ theorem compileStmt_requireError_zero_bridged BridgedExpr failCond) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.requireError cond errorName []) = .ok out → + inScopeNames [] (.requireError cond errorName []) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -2847,7 +2852,7 @@ theorem compileStmt_customError_zero_bridged (hStmt : BridgedSourceCustomErrorStmt fields errors dynamicSource stmt) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by cases hStmt with | revertError errorName errorDef hLookup hZeroParams => @@ -2867,7 +2872,7 @@ theorem compileStmtList_customError_zero_bridged BridgedSourceCustomErrorStmts fields errors dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmts = .ok out → + inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -2881,12 +2886,12 @@ theorem compileStmtList_customError_zero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -2972,7 +2977,7 @@ theorem compileStmt_external_body_with_errors_bridged BridgedSourceExternalBodyWithErrorsStmt fields errors dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -2999,7 +3004,7 @@ theorem compileStmt_internal_body_with_errors_bridged BridgedSourceInternalBodyWithErrorsStmt fields errors dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3025,7 +3030,7 @@ theorem compileStmtList_external_body_with_errors_bridged BridgedSourceExternalBodyWithErrorsStmts fields errors dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3039,12 +3044,12 @@ theorem compileStmtList_external_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -3078,7 +3083,7 @@ theorem compileStmtList_internal_body_with_errors_bridged BridgedSourceInternalBodyWithErrorsStmts fields errors dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3092,12 +3097,12 @@ theorem compileStmtList_internal_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -3136,7 +3141,7 @@ theorem compileStmt_ite_external_body_with_errors_bridged dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := false) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -3145,12 +3150,12 @@ theorem compileStmt_ite_external_body_with_errors_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -3203,7 +3208,7 @@ theorem compileStmt_ite_internal_body_with_errors_bridged dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := true) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -3212,12 +3217,12 @@ theorem compileStmt_ite_internal_body_with_errors_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -3315,7 +3320,7 @@ theorem compileStmt_external_structured_body_with_errors_bridged dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3336,7 +3341,7 @@ theorem compileStmtList_external_structured_body_with_errors_bridged dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3350,13 +3355,13 @@ theorem compileStmtList_external_structured_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -3388,7 +3393,7 @@ theorem compileStmt_internal_structured_body_with_errors_bridged dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3409,7 +3414,7 @@ theorem compileStmtList_internal_structured_body_with_errors_bridged dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3423,13 +3428,13 @@ theorem compileStmtList_internal_structured_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -3520,7 +3525,7 @@ theorem compileStmt_ite_external_nested_body_with_errors_bridged dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := false) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -3529,12 +3534,12 @@ theorem compileStmt_ite_external_nested_body_with_errors_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -3587,7 +3592,7 @@ theorem compileStmt_ite_internal_nested_body_with_errors_bridged dynamicSource elseBranch) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames (.ite cond thenBranch elseBranch) = .ok out → + (isInternal := true) inScopeNames [] (.ite cond thenBranch elseBranch) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -3596,12 +3601,12 @@ theorem compileStmt_ite_internal_nested_body_with_errors_bridged simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -3651,7 +3656,7 @@ theorem compileStmt_external_nested_body_with_errors_bridged dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3672,7 +3677,7 @@ theorem compileStmtList_external_nested_body_with_errors_bridged dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3686,13 +3691,13 @@ theorem compileStmtList_external_nested_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -3724,7 +3729,7 @@ theorem compileStmt_internal_nested_body_with_errors_bridged dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3745,7 +3750,7 @@ theorem compileStmtList_internal_nested_body_with_errors_bridged dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3759,13 +3764,13 @@ theorem compileStmtList_internal_nested_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -3850,7 +3855,7 @@ theorem compileStmt_external_forEach_body_with_errors_bridged dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3876,7 +3881,7 @@ theorem compileStmtList_external_forEach_body_with_errors_bridged dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3890,13 +3895,13 @@ theorem compileStmtList_external_forEach_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -3928,7 +3933,7 @@ theorem compileStmt_internal_forEach_body_with_errors_bridged dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -3954,7 +3959,7 @@ theorem compileStmtList_internal_forEach_body_with_errors_bridged dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -3968,13 +3973,13 @@ theorem compileStmtList_internal_forEach_body_with_errors_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -4099,7 +4104,7 @@ mutual dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -4113,12 +4118,12 @@ mutual simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -4173,7 +4178,7 @@ mutual dynamicSource stmts → ∀ (inScopeNames : List String) {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts hSource inScopeNames out hOk cases hSource with @@ -4185,13 +4190,13 @@ mutual | @cons head tail hHead hTail => simp only [compileStmtList, bind, Except.bind] at hOk cases hHeadCompile : compileStmt fields events errors dynamicSource - internalRetNames false inScopeNames head with + internalRetNames false inScopeNames [] head with | error err => simp [hHeadCompile] at hOk | ok headOut => simp [hHeadCompile] at hOk cases hTailCompile : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTailCompile] at hOk | ok tailOut => @@ -4216,7 +4221,7 @@ mutual dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -4230,12 +4235,12 @@ mutual simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -4290,7 +4295,7 @@ mutual dynamicSource stmts → ∀ (inScopeNames : List String) {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts hSource inScopeNames out hOk cases hSource with @@ -4302,13 +4307,13 @@ mutual | @cons head tail hHead hTail => simp only [compileStmtList, bind, Except.bind] at hOk cases hHeadCompile : compileStmt fields events errors dynamicSource - internalRetNames true inScopeNames head with + internalRetNames true inScopeNames [] head with | error err => simp [hHeadCompile] at hOk | ok headOut => simp [hHeadCompile] at hOk cases hTailCompile : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTailCompile] at hOk | ok tailOut => @@ -5604,7 +5609,7 @@ theorem compileStmt_rawLog_bridged ∀ {stmt : Stmt}, BridgedSourceRawLogStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -5668,7 +5673,7 @@ theorem compileStmtList_rawLog_bridged BridgedSourceRawLogStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -5682,13 +5687,12 @@ theorem compileStmtList_rawLog_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -5760,7 +5764,7 @@ theorem compileStmt_external_body_with_raw_log_bridged BridgedSourceExternalBodyWithRawLogStmt fields errors dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -5781,7 +5785,7 @@ theorem compileStmt_internal_body_with_raw_log_bridged BridgedSourceInternalBodyWithRawLogStmt fields errors dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -5801,7 +5805,7 @@ theorem compileStmtList_external_body_with_raw_log_bridged BridgedSourceExternalBodyWithRawLogStmts fields errors dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -5815,12 +5819,12 @@ theorem compileStmtList_external_body_with_raw_log_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -5854,7 +5858,7 @@ theorem compileStmtList_internal_body_with_raw_log_bridged BridgedSourceInternalBodyWithRawLogStmts fields errors dynamicSource stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -5868,12 +5872,12 @@ theorem compileStmtList_internal_body_with_raw_log_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -6003,7 +6007,7 @@ mutual dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6017,12 +6021,12 @@ mutual simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames thenBranch with + internalRetNames false inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames false inScopeNames elseBranch with + internalRetNames false inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -6077,7 +6081,7 @@ mutual dynamicSource stmts → ∀ (inScopeNames : List String) {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts hSource inScopeNames out hOk cases hSource with @@ -6089,13 +6093,13 @@ mutual | @cons head tail hHead hTail => simp only [compileStmtList, bind, Except.bind] at hOk cases hHeadCompile : compileStmt fields events errors dynamicSource - internalRetNames false inScopeNames head with + internalRetNames false inScopeNames [] head with | error err => simp [hHeadCompile] at hOk | ok headOut => simp [hHeadCompile] at hOk cases hTailCompile : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTailCompile] at hOk | ok tailOut => @@ -6120,7 +6124,7 @@ mutual dynamicSource stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6134,12 +6138,12 @@ mutual simp [hCondExpr] at hOk | ok condExpr => cases hThenCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames thenBranch with + internalRetNames true inScopeNames [] thenBranch with | error err => simp [hCondExpr, hThenCompile] at hOk | ok thenOut => cases hElseCompile : compileStmtList fields events errors dynamicSource - internalRetNames true inScopeNames elseBranch with + internalRetNames true inScopeNames [] elseBranch with | error err => simp [hCondExpr, hThenCompile, hElseCompile] at hOk | ok elseOut => @@ -6194,7 +6198,7 @@ mutual dynamicSource stmts → ∀ (inScopeNames : List String) {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts hSource inScopeNames out hOk cases hSource with @@ -6206,13 +6210,13 @@ mutual | @cons head tail hHead hTail => simp only [compileStmtList, bind, Except.bind] at hOk cases hHeadCompile : compileStmt fields events errors dynamicSource - internalRetNames true inScopeNames head with + internalRetNames true inScopeNames [] head with | error err => simp [hHeadCompile] at hOk | ok headOut => simp [hHeadCompile] at hOk cases hTailCompile : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTailCompile] at hOk | ok tailOut => @@ -6264,7 +6268,7 @@ theorem compileStmt_setMapping2_singleSlot_bridged (hSlots : findFieldWriteSlots fields field = some [slot]) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping2 field key1 key2 value) = .ok out → + inScopeNames [] (.setMapping2 field key1 key2 value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -6313,7 +6317,7 @@ theorem compileStmt_mappingWrite2_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWrite2Stmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6332,7 +6336,7 @@ theorem compileStmtList_mappingWrite2_bridged BridgedSourceMappingWrite2Stmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -6346,13 +6350,13 @@ theorem compileStmtList_mappingWrite2_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource internalRetNames - isInternal (collectStmtNames head ++ inScopeNames) tail with + isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => @@ -6408,7 +6412,7 @@ theorem compileStmt_setStorageAddr_singleSlot_bridged some ({ f with packedBits := none, aliasSlots := [] }, slot)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStorageAddr field value) = .ok out → + inScopeNames [] (.setStorageAddr field value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -6446,7 +6450,7 @@ theorem compileStmt_storageAddr_bridged ∀ {stmt : Stmt}, BridgedSourceStorageAddrStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6465,7 +6469,7 @@ theorem compileStmtList_storageAddr_bridged BridgedSourceStorageAddrStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -6479,13 +6483,12 @@ theorem compileStmtList_storageAddr_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -6546,7 +6549,7 @@ theorem compileStmt_setStructMember_singleSlot_bridged (hSlots : findFieldWriteSlots fields field = some [slot]) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStructMember field key memberName value) = .ok out → + inScopeNames [] (.setStructMember field key memberName value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, compileSetStructMember, hNotMapping2, hMembers, @@ -6574,7 +6577,7 @@ theorem compileStmt_structMember_bridged ∀ {stmt : Stmt}, BridgedSourceStructMemberStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6595,7 +6598,7 @@ theorem compileStmtList_structMember_bridged BridgedSourceStructMemberStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -6609,13 +6612,12 @@ theorem compileStmtList_structMember_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -6678,7 +6680,7 @@ theorem compileStmt_setStructMember2_singleSlot_bridged (hSlots : findFieldWriteSlots fields field = some [slot]) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStructMember2 field key1 key2 memberName value) = .ok out → + inScopeNames [] (.setStructMember2 field key1 key2 memberName value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -6727,7 +6729,7 @@ theorem compileStmt_structMember2_bridged ∀ {stmt : Stmt}, BridgedSourceStructMember2Stmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6748,7 +6750,7 @@ theorem compileStmtList_structMember2_bridged BridgedSourceStructMember2Stmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -6762,13 +6764,12 @@ theorem compileStmtList_structMember2_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -6821,7 +6822,7 @@ theorem compileStmt_setMappingWord_singleSlot_bridged (hWordOffset : wordOffset = 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingWord field key wordOffset value) = .ok out → + inScopeNames [] (.setMappingWord field key wordOffset value) = .ok out → BridgedStmts out := by intro out hOk subst hWordOffset @@ -6848,7 +6849,7 @@ theorem compileStmt_mappingWord_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWordStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -6867,7 +6868,7 @@ theorem compileStmtList_mappingWord_bridged BridgedSourceMappingWordStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -6881,13 +6882,12 @@ theorem compileStmtList_mappingWord_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -6942,7 +6942,7 @@ theorem compileStmt_setMapping2Word_singleSlot_bridged (hWordOffset : wordOffset = 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping2Word field key1 key2 wordOffset value) = .ok out → + inScopeNames [] (.setMapping2Word field key1 key2 wordOffset value) = .ok out → BridgedStmts out := by intro out hOk subst hWordOffset @@ -6992,7 +6992,7 @@ theorem compileStmt_mapping2Word_bridged ∀ {stmt : Stmt}, BridgedSourceMapping2WordStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7011,7 +7011,7 @@ theorem compileStmtList_mapping2Word_bridged BridgedSourceMapping2WordStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7025,13 +7025,12 @@ theorem compileStmtList_mapping2Word_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7069,7 +7068,7 @@ private theorem compileStmt_returnValuesEmpty_external_bridged (inScopeNames : List String) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.returnValues []) = .ok out → + (isInternal := false) inScopeNames [] (.returnValues []) = .ok out → BridgedStmts out := by intro out hOk simp [compileStmt, Pure.pure, Except.pure] at hOk @@ -7090,7 +7089,7 @@ theorem compileStmt_returnValuesEmpty_bridged ∀ {stmt : Stmt}, BridgedSourceReturnValuesEmptyStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7107,7 +7106,7 @@ theorem compileStmtList_returnValuesEmpty_bridged BridgedSourceReturnValuesEmptyStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7121,13 +7120,12 @@ theorem compileStmtList_returnValuesEmpty_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7165,7 +7163,7 @@ private theorem compileStmt_returnValuesEmpty_internal_bridged (dynamicSource : DynamicDataSource) (inScopeNames : List String) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource (internalRetNames := []) - (isInternal := true) inScopeNames (.returnValues []) = .ok out → + (isInternal := true) inScopeNames [] (.returnValues []) = .ok out → BridgedStmts out := by intro out hOk simp [compileStmt, compileExprList, bind, Except.bind, Pure.pure, Except.pure] at hOk @@ -7183,7 +7181,7 @@ theorem compileStmt_returnValuesEmpty_internal_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceReturnValuesEmptyInternalStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource (internalRetNames := []) - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7200,7 +7198,7 @@ theorem compileStmtList_returnValuesEmpty_internal_bridged BridgedSourceReturnValuesEmptyInternalStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource (internalRetNames := []) - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7214,12 +7212,12 @@ theorem compileStmtList_returnValuesEmpty_internal_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource [] - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - [] true (collectStmtNames head ++ inScopeNames) tail with + [] true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7303,7 +7301,7 @@ private theorem compileStmt_returnValuesInternal_bridged (hLen : values.length = internalRetNames.length) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames (.returnValues values) = .ok out → + (isInternal := true) inScopeNames [] (.returnValues values) = .ok out → BridgedStmts out := by intro out hOk have hLenFalse : (values.length != internalRetNames.length) = false := by @@ -7331,7 +7329,7 @@ theorem compileStmt_returnValuesInternal_fragment_bridged BridgedSourceReturnValuesInternalStmt internalRetNames stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmt = .ok out → + (isInternal := true) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7349,7 +7347,7 @@ theorem compileStmtList_returnValuesInternal_bridged BridgedSourceReturnValuesInternalStmts internalRetNames stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := true) inScopeNames stmts = .ok out → + (isInternal := true) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7363,13 +7361,12 @@ theorem compileStmtList_returnValuesInternal_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - true inScopeNames head with + true inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames true (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames true (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7455,7 +7452,7 @@ private theorem compileStmt_returnValuesExternal_bridged (hValues : ∀ v ∈ values, BridgedSourceExpr v) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames (.returnValues values) = .ok out → + (isInternal := false) inScopeNames [] (.returnValues values) = .ok out → BridgedStmts out := by intro out hOk by_cases hValuesNil : values = [] @@ -7494,7 +7491,7 @@ theorem compileStmt_returnValuesExternal_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceReturnValuesExternalStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmt = .ok out → + (isInternal := false) inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7511,7 +7508,7 @@ theorem compileStmtList_returnValuesExternal_bridged BridgedSourceReturnValuesExternalStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - (isInternal := false) inScopeNames stmts = .ok out → + (isInternal := false) inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7525,13 +7522,12 @@ theorem compileStmtList_returnValuesExternal_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - false inScopeNames head with + false inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames false (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames false (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7574,7 +7570,7 @@ private theorem compileStmt_mstore_bridged (hOffset : BridgedSourceExpr offset) (hValue : BridgedSourceExpr value) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames (.mstore offset value) = .ok out → + isInternal inScopeNames [] (.mstore offset value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind, Pure.pure, Except.pure] at hOk @@ -7604,7 +7600,7 @@ theorem compileStmt_mstore_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceMstoreStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7620,7 +7616,7 @@ theorem compileStmtList_mstore_bridged BridgedSourceMstoreStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7634,13 +7630,12 @@ theorem compileStmtList_mstore_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7673,7 +7668,7 @@ private theorem compileStmt_tstore_bridged (hOffset : BridgedSourceExpr offset) (hValue : BridgedSourceExpr value) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames (.tstore offset value) = .ok out → + isInternal inScopeNames [] (.tstore offset value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind, Pure.pure, Except.pure] at hOk @@ -7703,7 +7698,7 @@ theorem compileStmt_tstore_fragment_bridged ∀ {stmt : Stmt}, BridgedSourceTstoreStmt stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7719,7 +7714,7 @@ theorem compileStmtList_tstore_bridged BridgedSourceTstoreStmts stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7733,13 +7728,12 @@ theorem compileStmtList_tstore_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7797,7 +7791,7 @@ theorem compileStmt_storageArrayPush_singleSlot_bridged (hDynArr : f.ty = .dynamicArray elemType) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.storageArrayPush field value) = .ok out → + inScopeNames [] (.storageArrayPush field value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -7860,7 +7854,7 @@ theorem compileStmt_storageArrayPush_bridged ∀ {stmt : Stmt}, BridgedSourceStorageArrayPushStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -7879,7 +7873,7 @@ theorem compileStmtList_storageArrayPush_bridged BridgedSourceStorageArrayPushStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -7893,13 +7887,12 @@ theorem compileStmtList_storageArrayPush_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -7959,7 +7952,7 @@ theorem compileStmt_storageArrayPop_singleSlot_bridged (hDynArr : f.ty = .dynamicArray elemType) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.storageArrayPop field) = .ok out → + inScopeNames [] (.storageArrayPop field) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -8036,7 +8029,7 @@ theorem compileStmt_storageArrayPop_bridged ∀ {stmt : Stmt}, BridgedSourceStorageArrayPopStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -8055,7 +8048,7 @@ theorem compileStmtList_storageArrayPop_bridged BridgedSourceStorageArrayPopStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -8069,13 +8062,12 @@ theorem compileStmtList_storageArrayPop_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -8139,7 +8131,7 @@ theorem compileStmt_setStorageArrayElement_singleSlot_bridged (hDynArr : f.ty = .dynamicArray elemType) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStorageArrayElement field index value) = .ok out → + inScopeNames [] (.setStorageArrayElement field index value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -8224,7 +8216,7 @@ theorem compileStmt_setStorageArrayElement_bridged ∀ {stmt : Stmt}, BridgedSourceSetStorageArrayElementStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -8244,7 +8236,7 @@ theorem compileStmtList_setStorageArrayElement_bridged BridgedSourceSetStorageArrayElementStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -8258,13 +8250,12 @@ theorem compileStmtList_setStorageArrayElement_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -8354,7 +8345,7 @@ theorem compileStmt_setMappingWord_singleSlot_nonzero_bridged (hNonzero : wordOffset ≠ 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingWord field key wordOffset value) = .ok out → + inScopeNames [] (.setMappingWord field key wordOffset value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -8380,7 +8371,7 @@ theorem compileStmt_mappingWordNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWordNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -8399,7 +8390,7 @@ theorem compileStmtList_mappingWordNonzero_bridged BridgedSourceMappingWordNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -8413,13 +8404,12 @@ theorem compileStmtList_mappingWordNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -8475,7 +8465,7 @@ theorem compileStmt_setMapping2Word_singleSlot_nonzero_bridged (hNonzero : wordOffset ≠ 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping2Word field key1 key2 wordOffset value) = .ok out → + inScopeNames [] (.setMapping2Word field key1 key2 wordOffset value) = .ok out → BridgedStmts out := by intro out hOk have hBeq : (wordOffset == 0) = false := by @@ -8540,7 +8530,7 @@ theorem compileStmt_mapping2WordNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceMapping2WordNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -8559,7 +8549,7 @@ theorem compileStmtList_mapping2WordNonzero_bridged BridgedSourceMapping2WordNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -8573,13 +8563,12 @@ theorem compileStmtList_mapping2WordNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -8668,7 +8657,7 @@ theorem compileStmt_setMappingChain_singleSlot_bridged (hSlots : findFieldWriteSlots fields field = some [slot]) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingChain field keys value) = .ok out → + inScopeNames [] (.setMappingChain field keys value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -8721,7 +8710,7 @@ theorem compileStmt_mappingChain_bridged ∀ {stmt : Stmt}, BridgedSourceMappingChainStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -8740,7 +8729,7 @@ theorem compileStmtList_mappingChain_bridged BridgedSourceMappingChainStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -8754,13 +8743,12 @@ theorem compileStmtList_mappingChain_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -8917,7 +8905,7 @@ theorem compileStmt_setMapping_multiSlot_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping field key value) = .ok out → + inScopeNames [] (.setMapping field key value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -8949,7 +8937,7 @@ theorem compileStmt_setMappingUint_multiSlot_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingUint field key value) = .ok out → + inScopeNames [] (.setMappingUint field key value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -8975,7 +8963,7 @@ theorem compileStmt_mappingWriteMultiSlot_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWriteMultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -8998,7 +8986,7 @@ theorem compileStmtList_mappingWriteMultiSlot_bridged BridgedSourceMappingWriteMultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -9012,13 +9000,12 @@ theorem compileStmtList_mappingWriteMultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -9130,7 +9117,7 @@ theorem compileStmt_setMapping2_multiSlot_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping2 field key1 key2 value) = .ok out → + inScopeNames [] (.setMapping2 field key1 key2 value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt] at hOk @@ -9222,7 +9209,7 @@ theorem compileStmt_mappingWrite2MultiSlot_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWrite2MultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -9241,7 +9228,7 @@ theorem compileStmtList_mappingWrite2MultiSlot_bridged BridgedSourceMappingWrite2MultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -9255,13 +9242,12 @@ theorem compileStmtList_mappingWrite2MultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -9331,7 +9317,7 @@ theorem compileStmt_setStructMember_multiSlot_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStructMember field key memberName value) = .ok out → + inScopeNames [] (.setStructMember field key memberName value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, compileSetStructMember, hNotMapping2, hMembers, @@ -9359,7 +9345,7 @@ theorem compileStmt_structMemberMultiSlot_bridged ∀ {stmt : Stmt}, BridgedSourceStructMemberMultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -9380,7 +9366,7 @@ theorem compileStmtList_structMemberMultiSlot_bridged BridgedSourceStructMemberMultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -9394,13 +9380,12 @@ theorem compileStmtList_structMemberMultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -9471,7 +9456,7 @@ theorem compileStmt_setStructMember2_multiSlot_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStructMember2 field key1 key2 memberName value) = + inScopeNames [] (.setStructMember2 field key1 key2 memberName value) = .ok out → BridgedStmts out := by intro out hOk @@ -9564,7 +9549,7 @@ theorem compileStmt_structMember2MultiSlot_bridged ∀ {stmt : Stmt}, BridgedSourceStructMember2MultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -9585,7 +9570,7 @@ theorem compileStmtList_structMember2MultiSlot_bridged BridgedSourceStructMember2MultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -9599,13 +9584,12 @@ theorem compileStmtList_structMember2MultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -9669,7 +9653,7 @@ theorem compileStmt_setMappingWord_multiSlot_bridged (hWordOffset : wordOffset = 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingWord field key wordOffset value) = .ok out → + inScopeNames [] (.setMappingWord field key wordOffset value) = .ok out → BridgedStmts out := by intro out hOk subst hWordOffset @@ -9696,7 +9680,7 @@ theorem compileStmt_mappingWordMultiSlot_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWordMultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -9715,7 +9699,7 @@ theorem compileStmtList_mappingWordMultiSlot_bridged BridgedSourceMappingWordMultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -9729,13 +9713,12 @@ theorem compileStmtList_mappingWordMultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -9800,7 +9783,7 @@ theorem compileStmt_setMapping2Word_multiSlot_bridged (hWordOffset : wordOffset = 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping2Word field key1 key2 wordOffset value) = .ok out → + inScopeNames [] (.setMapping2Word field key1 key2 wordOffset value) = .ok out → BridgedStmts out := by intro out hOk subst hWordOffset @@ -9890,7 +9873,7 @@ theorem compileStmt_mapping2WordMultiSlot_bridged ∀ {stmt : Stmt}, BridgedSourceMapping2WordMultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -9909,7 +9892,7 @@ theorem compileStmtList_mapping2WordMultiSlot_bridged BridgedSourceMapping2WordMultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -9923,13 +9906,12 @@ theorem compileStmtList_mapping2WordMultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -10080,7 +10062,7 @@ theorem compileStmt_setMappingWord_multiSlot_nonzero_bridged (hNonzero : wordOffset ≠ 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMappingWord field key wordOffset value) = .ok out → + inScopeNames [] (.setMappingWord field key wordOffset value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, bind, Except.bind] at hOk @@ -10106,7 +10088,7 @@ theorem compileStmt_mappingWordMultiSlotNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceMappingWordMultiSlotNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -10125,7 +10107,7 @@ theorem compileStmtList_mappingWordMultiSlotNonzero_bridged BridgedSourceMappingWordMultiSlotNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -10139,13 +10121,12 @@ theorem compileStmtList_mappingWordMultiSlotNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -10281,7 +10262,7 @@ theorem compileStmt_setMapping2Word_multiSlot_nonzero_bridged (hNonzero : wordOffset ≠ 0) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setMapping2Word field key1 key2 wordOffset value) = .ok out → + inScopeNames [] (.setMapping2Word field key1 key2 wordOffset value) = .ok out → BridgedStmts out := by intro out hOk have hBeq : (wordOffset == 0) = false := by @@ -10400,7 +10381,7 @@ theorem compileStmt_mapping2WordMultiSlotNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceMapping2WordMultiSlotNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -10419,7 +10400,7 @@ theorem compileStmtList_mapping2WordMultiSlotNonzero_bridged BridgedSourceMapping2WordMultiSlotNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -10433,13 +10414,12 @@ theorem compileStmtList_mapping2WordMultiSlotNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -10511,7 +10491,7 @@ theorem compileStmt_setStructMember_multiSlot_nonzero_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStructMember field key memberName value) = .ok out → + inScopeNames [] (.setStructMember field key memberName value) = .ok out → BridgedStmts out := by intro out hOk simp only [compileStmt, compileSetStructMember, hNotMapping2, hMembers, @@ -10540,7 +10520,7 @@ theorem compileStmt_structMemberMultiSlotNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceStructMemberMultiSlotNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -10561,7 +10541,7 @@ theorem compileStmtList_structMemberMultiSlotNonzero_bridged BridgedSourceStructMemberMultiSlotNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -10575,13 +10555,12 @@ theorem compileStmtList_structMemberMultiSlotNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -10657,7 +10636,7 @@ theorem compileStmt_setStructMember2_multiSlot_nonzero_bridged some (slot0 :: slot1 :: slotsRest)) : ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames (.setStructMember2 field key1 key2 memberName value) = + inScopeNames [] (.setStructMember2 field key1 key2 memberName value) = .ok out → BridgedStmts out := by intro out hOk @@ -10777,7 +10756,7 @@ theorem compileStmt_structMember2MultiSlotNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceStructMember2MultiSlotNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal - inScopeNames stmt = .ok out → + inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -10798,7 +10777,7 @@ theorem compileStmtList_structMember2MultiSlotNonzero_bridged BridgedSourceStructMember2MultiSlotNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -10812,13 +10791,12 @@ theorem compileStmtList_structMember2MultiSlotNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames head with + isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -10886,7 +10864,7 @@ theorem compileStmt_setMappingPackedWord_singleSlot_bridged ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal inScopeNames - (.setMappingPackedWord field key wordOffset packed value) = .ok out → + [] (.setMappingPackedWord field key wordOffset packed value) = .ok out → BridgedStmts out := by intro out hOk subst hWordOffset @@ -10994,7 +10972,7 @@ theorem compileStmt_mappingPackedWord_bridged ∀ {stmt : Stmt}, BridgedSourceMappingPackedWordStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -11014,7 +10992,7 @@ theorem compileStmtList_mappingPackedWord_bridged BridgedSourceMappingPackedWordStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -11028,13 +11006,12 @@ theorem compileStmtList_mappingPackedWord_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource - internalRetNames isInternal inScopeNames head with + internalRetNames isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -11098,7 +11075,7 @@ theorem compileStmt_setMappingPackedWord_singleSlot_nonzero_bridged ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal inScopeNames - (.setMappingPackedWord field key wordOffset packed value) = .ok out → + [] (.setMappingPackedWord field key wordOffset packed value) = .ok out → BridgedStmts out := by intro out hOk have hBeq : (wordOffset == 0) = false := by @@ -11223,7 +11200,7 @@ theorem compileStmt_mappingPackedWordNonzero_bridged ∀ {stmt : Stmt}, BridgedSourceMappingPackedWordNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -11243,7 +11220,7 @@ theorem compileStmtList_mappingPackedWordNonzero_bridged BridgedSourceMappingPackedWordNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -11257,13 +11234,12 @@ theorem compileStmtList_mappingPackedWordNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource - internalRetNames isInternal inScopeNames head with + internalRetNames isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -11462,7 +11438,7 @@ theorem compileStmt_setMappingPackedWord_multiSlot_bridged ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal inScopeNames - (.setMappingPackedWord field key wordOffset packed value) = .ok out → + [] (.setMappingPackedWord field key wordOffset packed value) = .ok out → BridgedStmts out := by intro out hOk subst hWordOffset @@ -11522,7 +11498,7 @@ theorem compileStmt_mappingPackedWordMultiSlot_bridged BridgedSourceMappingPackedWordMultiSlotStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -11542,7 +11518,7 @@ theorem compileStmtList_mappingPackedWordMultiSlot_bridged BridgedSourceMappingPackedWordMultiSlotStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -11556,13 +11532,12 @@ theorem compileStmtList_mappingPackedWordMultiSlot_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource - internalRetNames isInternal inScopeNames head with + internalRetNames isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -11779,7 +11754,7 @@ theorem compileStmt_setMappingPackedWord_multiSlot_nonzero_bridged ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames isInternal inScopeNames - (.setMappingPackedWord field key wordOffset packed value) = .ok out → + [] (.setMappingPackedWord field key wordOffset packed value) = .ok out → BridgedStmts out := by intro out hOk have hBeq : (wordOffset == 0) = false := by @@ -11842,7 +11817,7 @@ theorem compileStmt_mappingPackedWordMultiSlotNonzero_bridged BridgedSourceMappingPackedWordMultiSlotNonzeroStmt fields stmt → ∀ {out : List YulStmt}, compileStmt fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmt = .ok out → + isInternal inScopeNames [] stmt = .ok out → BridgedStmts out := by intro stmt hStmt out hOk cases hStmt with @@ -11862,7 +11837,7 @@ theorem compileStmtList_mappingPackedWordMultiSlotNonzero_bridged BridgedSourceMappingPackedWordMultiSlotNonzeroStmts fields stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts induction stmts with @@ -11876,13 +11851,12 @@ theorem compileStmtList_mappingPackedWordMultiSlotNonzero_bridged intro inScopeNames hSource out hOk simp only [compileStmtList, bind, Except.bind] at hOk cases hHead : compileStmt fields events errors dynamicSource - internalRetNames isInternal inScopeNames head with + internalRetNames isInternal inScopeNames [] head with | error err => simp [hHead] at hOk | ok headOut => simp [hHead] at hOk cases hTail : compileStmtList fields events errors dynamicSource - internalRetNames isInternal (collectStmtNames head ++ inScopeNames) - tail with + internalRetNames isInternal (collectStmtNames head ++ inScopeNames) [] tail with | error err => simp [hTail] at hOk | ok tailOut => simp [hTail, Pure.pure, Except.pure] at hOk @@ -12005,7 +11979,7 @@ theorem compileStmtList_always_bridged isInternal stmts → ∀ {out : List YulStmt}, compileStmtList fields events errors dynamicSource internalRetNames - isInternal inScopeNames stmts = .ok out → + isInternal inScopeNames [] stmts = .ok out → BridgedStmts out := by intro stmts inScopeNames hSafe out hOk cases hSafe with diff --git a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanNativeHarness.lean b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanNativeHarness.lean new file mode 100644 index 000000000..04aef565a --- /dev/null +++ b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanNativeHarness.lean @@ -0,0 +1,140 @@ +import Compiler.Proofs.YulGeneration.Backends.EvmYulLeanAdapter +import Compiler.Proofs.YulGeneration.Backends.EvmYulLeanStateBridge +import Compiler.Proofs.YulGeneration.ReferenceOracle.Semantics +import EvmYul.Yul.Interpreter + +namespace Compiler.Proofs.YulGeneration.Backends.Native + +open Compiler.Yul +open Compiler.Proofs.YulGeneration +open Compiler.Proofs.YulGeneration.Backends.StateBridge + +/-! +Executable native EVMYulLean runtime harness for #1737. + +This module deliberately sits beside the historical adapter. The adapter is +part of the existing proof/report dependency graph; importing the state bridge +there would create a cycle through the reference oracle. Keeping the native +harness separate lets tests and future proofs run `EvmYul.Yul.callDispatcher` +directly without disturbing the current verified backend path. +-/ + +/-- Build a native EVMYulLean state for a generated runtime contract. + +The bridge starts from the flat Verity `YulState` projection, then installs the +lowered `YulContract` both in the execution environment and in the current +account. Runtime entrypoints are mutable by default (`perm := true`); +static-call-specific harnesses can override this later when #1737 widens to +external-call semantics. +-/ +def initialState + (contract : EvmYul.Yul.Ast.YulContract) + (tx : YulTransaction) + (storage : Nat → Nat) + (observableSlots : List Nat) : + EvmYul.Yul.State := + let verityState := YulState.initial tx storage + let shared := toSharedState verityState observableSlots + let addr := natToAddress tx.thisAddress + let account : EvmYul.Account .Yul := + match shared.accountMap.find? addr with + | some acc => { acc with code := contract } + | none => + { nonce := ⟨0⟩ + balance := ⟨0⟩ + storage := projectStorage storage observableSlots + code := contract + tstorage := Batteries.RBMap.empty } + let shared' : EvmYul.SharedState .Yul := + { shared with + accountMap := shared.accountMap.insert addr account + executionEnv := + { shared.executionEnv with + code := contract + perm := true } } + .Ok shared' ∅ + +/-- Project the account storage for the current contract back to Verity's + `Nat → Nat` storage view. -/ +def projectStorageFromState (tx : YulTransaction) (state : EvmYul.Yul.State) : + Nat → Nat := + extractStorage state.sharedState (natToAddress tx.thisAddress) + +/-- Decode one 32-byte big-endian word from an EVMYulLean byte array. -/ +def byteArrayWord (bytes : ByteArray) (offset : Nat) : Nat := + (List.range 32).foldl + (fun acc i => (acc * 256 + ((bytes.get? (offset + i)).getD 0).toNat) % + Compiler.Constants.evmModulus) + 0 + +/-- Decode the word-granular payload used by Verity's proof-side log model. -/ +def byteArrayLogWords (bytes : ByteArray) : List Nat := + (List.range (bytes.size / 32)).map (fun i => byteArrayWord bytes (i * 32)) + +/-- Project native EVMYulLean logs to the current Verity observable event shape: + topics followed by word-aligned log data. -/ +def projectLogEntry (entry : EvmYul.LogEntry) : List Nat := + entry.topics.toList.map uint256ToNat ++ byteArrayLogWords entry.data + +def projectLogsFromState (state : EvmYul.Yul.State) : List (List Nat) := + state.sharedState.substate.logSeries.toList.map projectLogEntry + +/-- Project a native Yul halt produced by `return`/`stop` to Verity's single-word + return observable. EVMYulLean represents `stop` as `YulHalt _ 0`; `return` + goes through `H_return`, matching the proof oracle's 32-byte return case. -/ +def projectHaltReturn (state : EvmYul.Yul.State) (haltValue : EvmYul.Yul.Ast.Literal) : + Option Nat := + if haltValue = ⟨0⟩ then + none + else if state.sharedState.H_return.size = 32 then + some (byteArrayWord state.sharedState.H_return 0) + else + some 0 + +/-- Convert a native `callDispatcher` result to the current Verity observable + result shape. Reverts and hard native errors conservatively roll storage + back to the supplied initial storage function. -/ +def projectResult + (tx : YulTransaction) + (initialStorage : Nat → Nat) + (result : + Except EvmYul.Yul.Exception + (EvmYul.Yul.State × List EvmYul.Yul.Ast.Literal)) : + YulResult := + match result with + | .ok (state, values) => + let finalStorage := projectStorageFromState tx state + { success := true + returnValue := values.head?.map uint256ToNat + finalStorage := finalStorage + finalMappings := Compiler.Proofs.storageAsMappings finalStorage + events := projectLogsFromState state } + | .error (.YulHalt state value) => + let finalStorage := projectStorageFromState tx state + { success := true + returnValue := projectHaltReturn state value + finalStorage := finalStorage + finalMappings := Compiler.Proofs.storageAsMappings finalStorage + events := projectLogsFromState state } + | .error _ => + { success := false + returnValue := none + finalStorage := initialStorage + finalMappings := Compiler.Proofs.storageAsMappings initialStorage + events := [] } + +/-- Lower and execute Verity runtime Yul through EVMYulLean's native + dispatcher. -/ +def interpretRuntimeNative + (fuel : Nat) + (runtimeCode : List YulStmt) + (tx : YulTransaction) + (storage : Nat → Nat) + (observableSlots : List Nat := []) : + Except AdapterError YulResult := do + let contract ← lowerRuntimeContractNative runtimeCode + let initial := initialState contract tx storage observableSlots + let result := EvmYul.Yul.callDispatcher fuel (some contract) initial + pure (projectResult tx storage result) + +end Compiler.Proofs.YulGeneration.Backends.Native diff --git a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanNativeSmokeTest.lean b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanNativeSmokeTest.lean new file mode 100644 index 000000000..a69483b31 --- /dev/null +++ b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanNativeSmokeTest.lean @@ -0,0 +1,128 @@ +import Compiler.Proofs.YulGeneration.Backends.EvmYulLeanNativeHarness +import EvmYul.Yul.Interpreter + +namespace Compiler.Proofs.YulGeneration.Backends + +open Compiler.Yul + +private def runNativeProgram (stmts : List YulStmt) : Option EvmYul.Yul.State := + match lowerStmtsNative stmts with + | .error _ => none + | .ok lowered => + let initial : EvmYul.Yul.State := Inhabited.default + match EvmYul.Yul.exec 64 (.Block lowered) none initial with + | .error _ => none + | .ok state => some state + +private def varIs (name : String) (value : Nat) (state : EvmYul.Yul.State) : Bool := + match state with + | .Ok _ store => + match store.lookup name with + | some got => got == EvmYul.UInt256.ofNat value + | none => false + | _ => false + +private def sampleTx : Compiler.Proofs.YulGeneration.YulTransaction := + { sender := 0xCAFE + msgValue := 7 + thisAddress := 0x1234 + functionSelector := 0x01020304 + args := [41] } + +private def zeroStorage : Nat → Nat := fun _ => 0 + +private def lowersAddAsPrim : Bool := + match lowerExprNative (.call "add" [.lit 1, .lit 2]) with + | .Call (.inl op) args => + op == (EvmYul.Operation.ADD : EvmYul.Operation .Yul) && args.length == 2 + | _ => false + +private def lowersHelperAsUserFunction : Bool := + match lowerExprNative (.call "helper" [.lit 1]) with + | .Call (.inr name) args => name == "helper" && args.length == 1 + | _ => false + +example : lowersAddAsPrim = true := by + native_decide + +example : lowersHelperAsUserFunction = true := by + native_decide + +example : + (match runNativeProgram [ + .let_ "x" (.call "add" [.lit 40, .lit 2]) + ] with + | some state => varIs "x" 42 state + | none => false) = true := by + native_decide + +example : + (match lowerRuntimeContractNative [ + .funcDef "inc" ["x"] ["r"] [ + .let_ "r" (.call "add" [.ident "x", .lit 1]) + ], + .letMany ["y"] (.call "inc" [.lit 41]) + ] with + | .ok contract => + contract.functions.lookup "inc" |>.isSome + | .error _ => false) = true := by + native_decide + +example : + (match Native.interpretRuntimeNative 128 [ + .funcDef "inc" ["x"] ["r"] [ + .let_ "r" (.call "add" [.ident "x", .lit 1]) + ], + .expr (.call "sstore" [.lit 7, .call "inc" [.lit 41]]) + ] sampleTx zeroStorage [7] with + | .ok result => result.success && result.finalStorage 7 == 42 + | .error _ => false) = true := by + native_decide + +example : + (match Native.interpretRuntimeNative 128 + [.expr (.call "sstore" [.lit 7, .lit 99])] + sampleTx zeroStorage [7] with + | .ok result => result.success && result.finalStorage 7 == 99 + | .error _ => false) = true := by + native_decide + +example : + Native.byteArrayWord + (ByteArray.ofFn fun i : Fin 32 => + if i.1 = 31 then UInt8.ofNat 210 else UInt8.ofNat 0) + 0 = 210 := by + native_decide + +example : + Native.projectLogEntry + { address := StateBridge.natToAddress sampleTx.thisAddress + topics := #[EvmYul.UInt256.ofNat 7] + data := + ByteArray.ofFn fun i : Fin 32 => + if i.1 = 31 then UInt8.ofNat 55 else UInt8.ofNat 0 } = + [7, 55] := by + native_decide + +example : + (let state : EvmYul.Yul.State := + .Ok + { (StateBridge.toSharedState + (Compiler.Proofs.YulGeneration.YulState.initial sampleTx zeroStorage) []) with + H_return := + ByteArray.ofFn fun i : Fin 32 => + if i.1 = 31 then UInt8.ofNat 99 else UInt8.ofNat 0 } + ∅ + Native.projectHaltReturn state (EvmYul.UInt256.ofNat 1)) = some 99 := by + native_decide + +example : + (match Native.interpretRuntimeNative 128 [ + .let_ "v" (.call "callvalue" []), + .expr (.call "sstore" [.lit 8, .ident "v"]) + ] sampleTx zeroStorage [8] with + | .ok result => result.success && result.finalStorage 8 == sampleTx.msgValue + | .error _ => false) = true := by + native_decide + +end Compiler.Proofs.YulGeneration.Backends diff --git a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanStateBridge.lean b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanStateBridge.lean index cd2c95fde..75fd45d82 100644 --- a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanStateBridge.lean +++ b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanStateBridge.lean @@ -26,7 +26,8 @@ The bridge converts between word-level and byte-level representations. -/ -import Compiler.Proofs.YulGeneration.ReferenceOracle.Semantics +import Compiler.Proofs.YulGeneration.ReferenceOracle.Builtins +import Compiler.Proofs.YulGeneration.ReferenceOracle.State import EvmYul.Yul.State import EvmYul.SharedState import EvmYul.State.Account diff --git a/Compiler/Proofs/YulGeneration/ReferenceOracle/Semantics.lean b/Compiler/Proofs/YulGeneration/ReferenceOracle/Semantics.lean index 6582344fa..f0b338666 100644 --- a/Compiler/Proofs/YulGeneration/ReferenceOracle/Semantics.lean +++ b/Compiler/Proofs/YulGeneration/ReferenceOracle/Semantics.lean @@ -2,6 +2,7 @@ import Compiler.Yul.Ast import Compiler.Proofs.IRGeneration.IRInterpreter import Compiler.Proofs.MappingSlot import Compiler.Proofs.YulGeneration.ReferenceOracle.Builtins +import Compiler.Proofs.YulGeneration.ReferenceOracle.State namespace Compiler.Proofs.YulGeneration @@ -42,24 +43,6 @@ aligned with Solidity's keccak-derived flat storage slot layout. /-! ## Execution State -/ -structure YulState where - vars : List (String × Nat) - storage : Nat → Nat - transientStorage : Nat → Nat := fun _ => 0 - memory : Nat → Nat - calldata : List Nat - selector : Nat - returnValue : Option Nat - sender : Nat - msgValue : Nat := 0 - thisAddress : Nat := 0 - blockTimestamp : Nat := 0 - blockNumber : Nat := 0 - chainId : Nat := 0 - blobBaseFee : Nat := 0 - events : List (List Nat) := [] - deriving Nonempty - structure YulTransaction where sender : Nat msgValue : Nat := 0 diff --git a/Compiler/Proofs/YulGeneration/ReferenceOracle/State.lean b/Compiler/Proofs/YulGeneration/ReferenceOracle/State.lean new file mode 100644 index 000000000..ef7c693d5 --- /dev/null +++ b/Compiler/Proofs/YulGeneration/ReferenceOracle/State.lean @@ -0,0 +1,23 @@ +namespace Compiler.Proofs.YulGeneration + +/-! Shared state structures for the reference-oracle Yul runtime. -/ + +structure YulState where + vars : List (String × Nat) + storage : Nat → Nat + transientStorage : Nat → Nat := fun _ => 0 + memory : Nat → Nat + calldata : List Nat + selector : Nat + returnValue : Option Nat + sender : Nat + msgValue : Nat := 0 + thisAddress : Nat := 0 + blockTimestamp : Nat := 0 + blockNumber : Nat := 0 + chainId : Nat := 0 + blobBaseFee : Nat := 0 + events : List (List Nat) := [] + deriving Nonempty + +end Compiler.Proofs.YulGeneration diff --git a/Compiler/TypedIRCompiler.lean b/Compiler/TypedIRCompiler.lean index 28160705a..59afa867e 100644 --- a/Compiler/TypedIRCompiler.lean +++ b/Compiler/TypedIRCompiler.lean @@ -59,10 +59,13 @@ private def paramTypeToTy : ParamType → Except String Ty | .array _ => Except.ok Ty.uint256 | .fixedArray _ _ => Except.ok Ty.uint256 | .bytes => Except.ok Ty.uint256 + | .adt _ _ => Except.ok Ty.uint256 -- ADTs represented as storage offsets + | .newtypeOf _ baseType => paramTypeToTy baseType -- Erased to base type private def fieldTypeToTy : FieldType → Except String Ty | .uint256 => Except.ok Ty.uint256 | .address => Except.ok Ty.address + | .adt _ _ => Except.ok Ty.uint256 | .dynamicArray _ => Except.ok Ty.uint256 | .mappingTyped _ => Except.ok Ty.uint256 | .mappingStruct _ _ => Except.ok Ty.uint256 diff --git a/Contracts/Common.lean b/Contracts/Common.lean index 21024f65d..d8541a578 100644 --- a/Contracts/Common.lean +++ b/Contracts/Common.lean @@ -22,6 +22,8 @@ macro_rules let _ := $_module let _ := $_args pure ()) + | `(doElem| unsafe $_reason:str do $body:doSeq) => + `(doElem| do $body) | `(doElem| tryCatch $attempt:term (fun $name:ident => do $[$elems:doElem]*)) => do let tryCatchFn := Lean.mkIdentFrom attempt `_root_.Contracts.tryCatchWord `(doElem| $tryCatchFn:ident $attempt (fun $name => do $[$elems:doElem]*)) @@ -252,6 +254,8 @@ private def externalCallStubWord (name : String) (args : List Uint256) : Uint256 | _, _ => args.foldl add name.length def externalCallWords {α : Type} [ExternalResult α] (name : String) (args : List Uint256) : α := ExternalResult.fromWord (externalCallStubWord name args) +def tryExternalCallWords {α : Type} [Inhabited α] (_name : String) (_args : List Uint256) : Contract (Bool × α) := + pure (false, default) private def erc20ReadStubWord (name : String) (args : List Uint256) : Uint256 := externalCallStubWord name args macro_rules @@ -259,6 +263,10 @@ macro_rules `(externalCallWords $(Lean.quote (toString name.getId)) [ $[ExternalArg.toWord $args],* ]) | `(term| externalCall $name:str [ $[$args:term],* ]) => `(externalCallWords $name [ $[ExternalArg.toWord $args],* ]) + | `(term| tryExternalCall $name:str [ $[$args:term],* ]) => + `(tryExternalCallWords $name [ $[ExternalArg.toWord $args],* ]) + | `(term| tryExternalCall $name:ident [ $[$args:term],* ]) => + `(tryExternalCallWords $(Lean.quote (toString name.getId)) [ $[ExternalArg.toWord $args],* ]) def getMappingWord (_slot : StorageSlot (Uint256 → Uint256)) (_key _wordOffset : Uint256) : Contract Uint256 := pure 0 def setMappingWord (_slot : StorageSlot (Uint256 → Uint256)) (_key _wordOffset _value : Uint256) : diff --git a/Contracts/MacroTranslateInvariantTest.lean b/Contracts/MacroTranslateInvariantTest.lean index c5ead13c7..c9b5e18a7 100644 --- a/Contracts/MacroTranslateInvariantTest.lean +++ b/Contracts/MacroTranslateInvariantTest.lean @@ -316,12 +316,44 @@ private def macroSpecs : List CompilationModel := , Contracts.Smoke.ZeroAddressShadowSmoke.spec , Contracts.Smoke.StructMappingSmoke.spec , Contracts.Smoke.ExternalCallSmoke.spec + , Contracts.Smoke.TryExternalCallSmoke.spec + , Contracts.Smoke.ExternalCallMultiReturn.spec , Contracts.Smoke.ERC20HelperSmoke.spec , Contracts.Smoke.GenericECMReadSmoke.spec , Contracts.Smoke.GenericECMWriteSmoke.spec , Contracts.Smoke.LowLevelTryCatchSmoke.spec , Contracts.Smoke.LocalObligationRequiredForUnsafeFunctionBoundary.spec , Contracts.Smoke.LocalObligationRequiredForUnsafeConstructorBoundary.spec + , Contracts.Smoke.ModifiesSmoke.spec + , Contracts.Smoke.NoExternalCallsSmoke.spec + , Contracts.Smoke.EffectCompositionSmoke.spec + , Contracts.Smoke.CEISmoke.spec + , Contracts.Smoke.CEILadderSmoke.spec + , Contracts.Smoke.RolesSmoke.spec + , Contracts.Smoke.NewtypeSmoke.spec + , Contracts.Smoke.NewtypeStorageSmoke.spec + , Contracts.Smoke.NamespacedStorageSmoke.spec + , Contracts.Smoke.CustomNamespacedSmoke.spec + , Contracts.Smoke.CEIViolationRejected.spec + , Contracts.Smoke.UnsafeBlockSmoke.spec + , Contracts.Smoke.UnsafeGatingAccepted.spec + , Contracts.Smoke.UnsafeGatingRejected.spec + , Contracts.Smoke.AdtSmoke.spec + , Contracts.Smoke.CEIWriteInBranchAfterCall.spec + , Contracts.Smoke.CEICallBothBranchesWrite.spec + , Contracts.Smoke.ModifiesRolesSmoke.spec + , Contracts.Smoke.ModifiesNamespaceSmoke.spec + , Contracts.Smoke.AdtSingleVariant.spec + , Contracts.Smoke.AdtMixedFieldCounts.spec + , Contracts.Smoke.NewtypeModifiesSmoke.spec + , Contracts.Smoke.NewtypeNamespaceSmoke.spec + , Contracts.Smoke.UnsafeCEIViolation.spec + , Contracts.Smoke.UnsafeCEICompliant.spec + , Contracts.Smoke.CEIInternalCallAfterExternalRejected.spec + , Contracts.Smoke.RolesCEISmoke.spec + , Contracts.Smoke.NonreentrantModifiesSmoke.spec + , Contracts.Smoke.AdtNewtypeCombo.spec + , Contracts.Smoke.FullComboSmoke.spec ] private def functionSignature (fn : FunctionSpec) : String := @@ -393,6 +425,8 @@ private def expectedExternalSignatures : List (String × List String) := "delegateOf(address)", "setApproval(address,address,uint256,uint256)", "approvalOf(address,address)", "approvalNonce(address,address)"]) , ("ExternalCallSmoke", ["storeEcho(uint256)", "getEchoedValue()"]) + , ("TryExternalCallSmoke", ["tryEcho(uint256)"]) + , ("ExternalCallMultiReturn", ["callFanout(uint256)", "noop()"]) , ("ERC20HelperSmoke", ["pushTokens(address,address,uint256)", "pullTokens(address,address,address,uint256)", "approveTokens(address,address,uint256)", "snapshotBalance(address,address)", "snapshotAllowance(address,address,address)", "snapshotSupply(address)"]) @@ -401,6 +435,36 @@ private def expectedExternalSignatures : List (String × List String) := , ("LowLevelTryCatchSmoke", ["catchFailure()", "skipCatchOnSuccess()", "catchFailureWithShadowedParam(uint256)"]) , ("LocalObligationRequiredForUnsafeFunctionBoundary", ["preview()"]) , ("LocalObligationRequiredForUnsafeConstructorBoundary", ["noop()"]) + , ("ModifiesSmoke", ["increment()", "transferOwnership(address)", "deposit(uint256)", "getCounter()"]) + , ("NoExternalCallsSmoke", ["increment()", "getCounter()", "setOwner(address)"]) + , ("EffectCompositionSmoke", ["getCounter()", "increment()", "setOwner(address)", "deposit(uint256)"]) + , ("CEISmoke", ["increment()", "getCounter()", "updateThenCall(uint256)", "callThenUpdate(uint256)"]) + , ("CEILadderSmoke", ["callThenStoreGuarded(uint256)", "callThenStoreProved(uint256)", "storeThenCall(uint256)", "increment()"]) + , ("RolesSmoke", ["setCounter(uint256)", "getCounter()"]) + , ("NewtypeSmoke", ["mint(uint256,uint256)", "setMinter(address)", "getNextTokenId()"]) + , ("NewtypeStorageSmoke", ["setTokenId(uint256)", "getTokenId()", "setAdmin(address)", "getAdmin()"]) + , ("NamespacedStorageSmoke", ["deposit(uint256)", "getOwner()"]) + , ("CustomNamespacedSmoke", ["deposit(uint256)", "getOwner()"]) + , ("CEIViolationRejected", ["callThenStore(uint256)"]) + , ("UnsafeBlockSmoke", ["incrementUnsafe()", "getCounter()"]) + , ("UnsafeGatingAccepted", ["writeMem()"]) + , ("UnsafeGatingRejected", ["noop()"]) + , ("AdtSmoke", ["increment()"]) + , ("CEIWriteInBranchAfterCall", ["callThenConditionalWrite(uint256)"]) + , ("CEICallBothBranchesWrite", ["callThenBranchWrite(uint256)"]) + , ("ModifiesRolesSmoke", ["setCounter(uint256)", "setCounterAndFlag(uint256,uint256)", "getCounter()"]) + , ("ModifiesNamespaceSmoke", ["increment()", "transferOwnership(address)", "getCounter()"]) + , ("AdtSingleVariant", ["store()"]) + , ("AdtMixedFieldCounts", ["clear()", "set(uint256)"]) + , ("NewtypeModifiesSmoke", ["mint(uint256,uint256)", "getNextId()"]) + , ("NewtypeNamespaceSmoke", ["setId(uint256)", "getId()"]) + , ("UnsafeCEIViolation", ["unsafeCallThenWrite(uint256)"]) + , ("UnsafeCEICompliant", ["writeBeforeUnsafeCall(uint256)"]) + , ("CEIInternalCallAfterExternalRejected", ["increment(uint256)", "callThenHelper(uint256)"]) + , ("RolesCEISmoke", ["setAndCall(uint256)", "getCounter()"]) + , ("NonreentrantModifiesSmoke", ["deposit(uint256)", "getBalance()"]) + , ("AdtNewtypeCombo", ["pause()", "unpause()", "setLastId(uint256)"]) + , ("FullComboSmoke", ["deposit(uint256)", "freeze()", "getBalance()"]) ] private def expectedExternalSelectors : List (String × List String) := @@ -456,6 +520,8 @@ private def expectedExternalSelectors : List (String × List String) := , ("StructMappingSmoke", ["0x468c900e", "0xe7933b6a", "0x8d22ea2a", "0xf4536007", "0xcb01943e", "0x6c241120"]) , ("ExternalCallSmoke", ["0x32fdff86", "0x21209dbd"]) + , ("TryExternalCallSmoke", ["0xaf842e53"]) + , ("ExternalCallMultiReturn", ["0x70fce9a3", "0x5dfc2e4a"]) , ("ERC20HelperSmoke", ["0xa6c29ca3", "0x6aa209a6", "0x912d6e28", "0x48476c71", "0xdac24aaf", "0x7247c4a5"]) , ("GenericECMReadSmoke", ["0x78f2e50f"]) @@ -463,6 +529,36 @@ private def expectedExternalSelectors : List (String × List String) := , ("LowLevelTryCatchSmoke", ["0x42d9c6d1", "0xdaf546c4", "0xa4660933"]) , ("LocalObligationRequiredForUnsafeFunctionBoundary", ["0xefae2305"]) , ("LocalObligationRequiredForUnsafeConstructorBoundary", ["0x5dfc2e4a"]) + , ("ModifiesSmoke", ["0xd09de08a", "0xf2fde38b", "0xb6b55f25", "0x8ada066e"]) + , ("NoExternalCallsSmoke", ["0xd09de08a", "0x8ada066e", "0x13af4035"]) + , ("EffectCompositionSmoke", ["0x8ada066e", "0xd09de08a", "0x13af4035", "0xb6b55f25"]) + , ("CEISmoke", ["0xd09de08a", "0x8ada066e", "0x8c468aed", "0x4955cfdb"]) + , ("CEILadderSmoke", ["0xaf0ac94c", "0xe9ab4836", "0xb6fbe456", "0xd09de08a"]) + , ("RolesSmoke", ["0x8bb5d9c3", "0x8ada066e"]) + , ("NewtypeSmoke", ["0x1b2ef1ca", "0xfca3b5aa", "0xcaa0f92a"]) + , ("NewtypeStorageSmoke", ["0xc929ccf3", "0x010a38f5", "0x704b6c02", "0x6e9960c3"]) + , ("NamespacedStorageSmoke", ["0xb6b55f25", "0x893d20e8"]) + , ("CustomNamespacedSmoke", ["0xb6b55f25", "0x893d20e8"]) + , ("CEIViolationRejected", ["0xe4fccc26"]) + , ("UnsafeBlockSmoke", ["0x87a993fd", "0x8ada066e"]) + , ("UnsafeGatingAccepted", ["0x68236256"]) + , ("UnsafeGatingRejected", ["0x5dfc2e4a"]) + , ("AdtSmoke", ["0xd09de08a"]) + , ("CEIWriteInBranchAfterCall", ["0x15e5e135"]) + , ("CEICallBothBranchesWrite", ["0xfc58b77d"]) + , ("ModifiesRolesSmoke", ["0x8bb5d9c3", "0xeb00a438", "0x8ada066e"]) + , ("ModifiesNamespaceSmoke", ["0xd09de08a", "0xf2fde38b", "0x8ada066e"]) + , ("AdtSingleVariant", ["0x975057e7"]) + , ("AdtMixedFieldCounts", ["0x52efea6e", "0x60fe47b1"]) + , ("NewtypeModifiesSmoke", ["0x1b2ef1ca", "0xbc968326"]) + , ("NewtypeNamespaceSmoke", ["0xd0e0ba95", "0x5d1ca631"]) + , ("UnsafeCEIViolation", ["0x20c925f7"]) + , ("UnsafeCEICompliant", ["0x9a92630e"]) + , ("CEIInternalCallAfterExternalRejected", ["0x7cf5dab0", "0xa73250c1"]) + , ("RolesCEISmoke", ["0xdc957d7d", "0x8ada066e"]) + , ("NonreentrantModifiesSmoke", ["0xb6b55f25", "0x12065fe0"]) + , ("AdtNewtypeCombo", ["0x8456cb59", "0x3f4ba83a", "0x1a27e85f"]) + , ("FullComboSmoke", ["0xb6b55f25", "0x62a5af3b", "0x12065fe0"]) ] private def expectedFor @@ -472,9 +568,21 @@ private def expectedFor private def expectedCompileCheckedError? (contractName : String) : Option String := match contractName with | "LocalObligationRequiredForUnsafeFunctionBoundary" => - some "uses low-level/assembly mechanic(s) calldataload without any local_obligations entry" + some "uses low-level/assembly mechanic(s) calldataload outside an unsafe block without any local_obligations entry" | "LocalObligationRequiredForUnsafeConstructorBoundary" => - some "constructor uses low-level/assembly mechanic(s) mstore without any local_obligations entry" + some "constructor uses low-level/assembly mechanic(s) mstore outside an unsafe block without any local_obligations entry" + | "CEIViolationRejected" => + some "violates CEI (Checks-Effects-Interactions) ordering" + | "UnsafeGatingRejected" => + some "constructor uses low-level/assembly mechanic(s) mstore outside an unsafe block without any local_obligations entry" + | "CEIWriteInBranchAfterCall" => + some "violates CEI (Checks-Effects-Interactions) ordering" + | "CEICallBothBranchesWrite" => + some "violates CEI (Checks-Effects-Interactions) ordering" + | "UnsafeCEIViolation" => + some "violates CEI (Checks-Effects-Interactions) ordering" + | "CEIInternalCallAfterExternalRejected" => + some "violates CEI (Checks-Effects-Interactions) ordering" | _ => none -- Regression: `verity_contract` elaboration emits field-level findIdx simp lemmas. @@ -497,8 +605,124 @@ private def checkMutabilitySmoke : IO Unit := do expectTrue "MutabilitySmoke: view flag is preserved" owner.isView expectTrue "MutabilitySmoke: deposit body reads msgValue" (contains (reprStr deposit.body) "Expr.msgValue") - -- This compile-time reference fails if the auto-generated theorem is missing. + -- Verify auto-generated _is_view theorem exists (#1729, Axis 3 Step 1a). + -- This line would fail to compile if the theorem were missing. let _ := @Contracts.Smoke.MutabilitySmoke.currentOwner_is_view + -- Verify auto-generated modifies/frame artifacts exist (#1729, Axis 3 Step 1b). + let _ := @Contracts.Smoke.ModifiesSmoke.increment_modifies + let _ := @Contracts.Smoke.ModifiesSmoke.increment_frame + let _ := @Contracts.Smoke.ModifiesSmoke.increment_frame_rfl + let _ := @Contracts.Smoke.ModifiesSmoke.transferOwnership_modifies + let _ := @Contracts.Smoke.ModifiesSmoke.transferOwnership_frame + let _ := @Contracts.Smoke.ModifiesSmoke.transferOwnership_frame_rfl + let _ := @Contracts.Smoke.ModifiesSmoke.deposit_modifies + let _ := @Contracts.Smoke.ModifiesSmoke.deposit_frame + let _ := @Contracts.Smoke.ModifiesSmoke.deposit_frame_rfl + -- Verify auto-generated _no_calls theorem exists (#1729, Axis 3 Step 1c). + let _ := @Contracts.Smoke.NoExternalCallsSmoke.increment_no_calls + let _ := @Contracts.Smoke.NoExternalCallsSmoke.getCounter_no_calls + -- Verify auto-generated _effects conjunction theorem exists (#1729, Axis 3 Step 1d). + -- getCounter has view + no_external_calls → _effects bundles both + let _ := @Contracts.Smoke.EffectCompositionSmoke.getCounter_effects + -- increment has no_external_calls + modifies → _effects bundles both + let _ := @Contracts.Smoke.EffectCompositionSmoke.increment_effects + -- Also verify the existing NoExternalCallsSmoke.getCounter gets _effects + -- (it has view + no_external_calls) + let _ := @Contracts.Smoke.NoExternalCallsSmoke.getCounter_effects + -- Verify auto-generated _cei_compliant theorem exists (#1728, Axis 2 Step 2a). + -- All functions that don't have allow_post_interaction_writes get _cei_compliant + let _ := @Contracts.Smoke.CEISmoke.increment_cei_compliant + let _ := @Contracts.Smoke.CEISmoke.getCounter_cei_compliant + let _ := @Contracts.Smoke.CEISmoke.updateThenCall_cei_compliant + -- callThenUpdate has allow_post_interaction_writes so no _cei_compliant theorem + + -- Verify auto-generated _nonreentrant and _cei_safe theorems (#1728, Axis 2 Step 2b). + -- callThenStoreGuarded has nonreentrant(lock) → _nonreentrant theorem + let _ := @Contracts.Smoke.CEILadderSmoke.callThenStoreGuarded_nonreentrant + -- callThenStoreProved has cei_safe → _cei_safe theorem + let _ := @Contracts.Smoke.CEILadderSmoke.callThenStoreProved_cei_safe + -- storeThenCall and increment are default CEI-compliant → _cei_compliant + let _ := @Contracts.Smoke.CEILadderSmoke.storeThenCall_cei_compliant + let _ := @Contracts.Smoke.CEILadderSmoke.increment_cei_compliant + -- callThenStoreGuarded does NOT get _cei_compliant (it uses nonreentrant instead) + -- callThenStoreProved does NOT get _cei_compliant (it uses cei_safe instead) + + -- Verify auto-generated _requires_role theorem (#1728, Axis 2 Step 2c). + -- setCounter has requires(admin) → _requires_role theorem + let _ := @Contracts.Smoke.RolesSmoke.setCounter_requires_role + -- getCounter has no requires → gets normal _cei_compliant + let _ := @Contracts.Smoke.RolesSmoke.getCounter_cei_compliant + -- Verify NewtypeSmoke generates standard _cei_compliant theorems (#1727, Axis 1 Step 3a). + -- Newtypes are erased — functions compile with base types. + let _ := @Contracts.Smoke.NewtypeSmoke.mint_cei_compliant + let _ := @Contracts.Smoke.NewtypeSmoke.setMinter_cei_compliant + let _ := @Contracts.Smoke.NewtypeSmoke.getNextTokenId_cei_compliant + -- Verify NewtypeStorageSmoke: newtype-typed storage fields compile correctly. + let _ := @Contracts.Smoke.NewtypeStorageSmoke.setTokenId_cei_compliant + let _ := @Contracts.Smoke.NewtypeStorageSmoke.getTokenId_cei_compliant + let _ := @Contracts.Smoke.NewtypeStorageSmoke.setAdmin_cei_compliant + let _ := @Contracts.Smoke.NewtypeStorageSmoke.getAdmin_cei_compliant + -- Verify AdtSmoke generates standard _cei_compliant theorems (#1727, Axis 1 Step 5a). + -- ADTs are parsed; functions compile normally. + let _ := @Contracts.Smoke.AdtSmoke.increment_cei_compliant + -- Verify NamespacedStorageSmoke generates standard _cei_compliant theorems (#1730, Axis 4 Step 4b). + let _ := @Contracts.Smoke.NamespacedStorageSmoke.deposit_cei_compliant + let _ := @Contracts.Smoke.NamespacedStorageSmoke.getOwner_cei_compliant + -- Verify CustomNamespacedSmoke generates standard _cei_compliant theorems (#1730, Axis 4 Step 4c). + let _ := @Contracts.Smoke.CustomNamespacedSmoke.deposit_cei_compliant + let _ := @Contracts.Smoke.CustomNamespacedSmoke.getOwner_cei_compliant + + -- ── Stress-test contracts: theorem existence checks ── + -- ModifiesRolesSmoke: combined requires(admin) + modifies + no_external_calls + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounter_requires_role + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounter_modifies + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounter_frame + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounter_frame_rfl + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounter_no_calls + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounter_effects + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounterAndFlag_requires_role + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounterAndFlag_modifies + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounterAndFlag_frame + let _ := @Contracts.Smoke.ModifiesRolesSmoke.setCounterAndFlag_frame_rfl + let _ := @Contracts.Smoke.ModifiesRolesSmoke.getCounter_is_view + let _ := @Contracts.Smoke.ModifiesRolesSmoke.getCounter_cei_compliant + -- ModifiesNamespaceSmoke: namespaced storage + modifies + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.increment_modifies + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.increment_frame + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.increment_frame_rfl + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.transferOwnership_modifies + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.transferOwnership_frame + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.transferOwnership_frame_rfl + let _ := @Contracts.Smoke.ModifiesNamespaceSmoke.getCounter_is_view + -- NewtypeModifiesSmoke: newtypes + modifies + let _ := @Contracts.Smoke.NewtypeModifiesSmoke.mint_modifies + let _ := @Contracts.Smoke.NewtypeModifiesSmoke.mint_frame_rfl + let _ := @Contracts.Smoke.NewtypeModifiesSmoke.getNextId_is_view + -- UnsafeCEICompliant: write before call in unsafe block passes CEI + let _ := @Contracts.Smoke.UnsafeCEICompliant.writeBeforeUnsafeCall_cei_compliant + -- RolesCEISmoke: roles + CEI + let _ := @Contracts.Smoke.RolesCEISmoke.setAndCall_requires_role + let _ := @Contracts.Smoke.RolesCEISmoke.setAndCall_cei_compliant + let _ := @Contracts.Smoke.RolesCEISmoke.getCounter_is_view + -- NonreentrantModifiesSmoke: nonreentrant + modifies + let _ := @Contracts.Smoke.NonreentrantModifiesSmoke.deposit_nonreentrant + let _ := @Contracts.Smoke.NonreentrantModifiesSmoke.deposit_modifies + let _ := @Contracts.Smoke.NonreentrantModifiesSmoke.deposit_frame + let _ := @Contracts.Smoke.NonreentrantModifiesSmoke.deposit_frame_rfl + let _ := @Contracts.Smoke.NonreentrantModifiesSmoke.getBalance_is_view + -- FullComboSmoke: namespace + newtype + ADT + modifies + roles + no_external_calls + let _ := @Contracts.Smoke.FullComboSmoke.deposit_requires_role + let _ := @Contracts.Smoke.FullComboSmoke.deposit_modifies + let _ := @Contracts.Smoke.FullComboSmoke.deposit_frame + let _ := @Contracts.Smoke.FullComboSmoke.deposit_frame_rfl + let _ := @Contracts.Smoke.FullComboSmoke.deposit_no_calls + let _ := @Contracts.Smoke.FullComboSmoke.deposit_effects + let _ := @Contracts.Smoke.FullComboSmoke.freeze_requires_role + let _ := @Contracts.Smoke.FullComboSmoke.freeze_modifies + let _ := @Contracts.Smoke.FullComboSmoke.freeze_no_calls + let _ := @Contracts.Smoke.FullComboSmoke.getBalance_is_view + let _ := @Contracts.Smoke.FullComboSmoke.getBalance_no_calls + let _ := @Contracts.Smoke.FullComboSmoke.getBalance_effects private def checkSignedBuiltinSmoke : IO Unit := do let functions := Contracts.Smoke.SignedBuiltinSmoke.spec.functions diff --git a/Contracts/MacroTranslateRoundTripFuzz.lean b/Contracts/MacroTranslateRoundTripFuzz.lean index 1fd415bea..1da16cea0 100644 --- a/Contracts/MacroTranslateRoundTripFuzz.lean +++ b/Contracts/MacroTranslateRoundTripFuzz.lean @@ -73,10 +73,38 @@ private def macroSpecs : List CompilationModel := , Contracts.Smoke.ZeroAddressShadowSmoke.spec , Contracts.Smoke.StructMappingSmoke.spec , Contracts.Smoke.ExternalCallSmoke.spec + , Contracts.Smoke.TryExternalCallSmoke.spec + , Contracts.Smoke.ExternalCallMultiReturn.spec , Contracts.Smoke.ERC20HelperSmoke.spec , Contracts.Smoke.GenericECMReadSmoke.spec , Contracts.Smoke.GenericECMWriteSmoke.spec , Contracts.Smoke.LowLevelTryCatchSmoke.spec + , Contracts.Smoke.ModifiesSmoke.spec + , Contracts.Smoke.NoExternalCallsSmoke.spec + , Contracts.Smoke.EffectCompositionSmoke.spec + , Contracts.Smoke.CEISmoke.spec + , Contracts.Smoke.CEILadderSmoke.spec + , Contracts.Smoke.RolesSmoke.spec + , Contracts.Smoke.NewtypeSmoke.spec + , Contracts.Smoke.NewtypeStorageSmoke.spec + , Contracts.Smoke.NamespacedStorageSmoke.spec + , Contracts.Smoke.CustomNamespacedSmoke.spec + , Contracts.Smoke.UnsafeBlockSmoke.spec + , Contracts.Smoke.UnsafeGatingAccepted.spec + , Contracts.Smoke.AdtSmoke.spec + , Contracts.Smoke.ModifiesRolesSmoke.spec + , Contracts.Smoke.ModifiesNamespaceSmoke.spec + , Contracts.Smoke.AdtSingleVariant.spec + , Contracts.Smoke.AdtMixedFieldCounts.spec + , Contracts.Smoke.NewtypeModifiesSmoke.spec + , Contracts.Smoke.NewtypeNamespaceSmoke.spec + , Contracts.Smoke.UnsafeCEICompliant.spec + , Contracts.Smoke.RolesCEISmoke.spec + , Contracts.Smoke.NonreentrantModifiesSmoke.spec + , Contracts.Smoke.AdtNewtypeCombo.spec + , Contracts.Smoke.FullComboSmoke.spec + -- CEIWriteInBranchAfterCall, CEICallBothBranchesWrite, UnsafeCEIViolation are + -- intentionally invalid (CEI violations); tested via #guard_msgs in Smoke.lean ] private structure FuzzRng where diff --git a/Contracts/Smoke.lean b/Contracts/Smoke.lean index 0fa6f6b67..de6321149 100644 --- a/Contracts/Smoke.lean +++ b/Contracts/Smoke.lean @@ -746,7 +746,7 @@ verity_contract DirectHelperCallSmoke where let current ← getStorage total return (current, add current offset) - function runHelpers (amount : Uint256, extra : Uint256, offset : Uint256) : Uint256 := do + function allow_post_interaction_writes runHelpers (amount : Uint256, extra : Uint256, offset : Uint256) : Uint256 := do addToTotal amount let combined ← readTotalPlus extra let (left, right) ← pairWithTotal offset @@ -896,8 +896,9 @@ verity_contract ExternalCallSmoke where echoedValue : Uint256 := slot 0 linked_externals external echo(Uint256) -> (Uint256) + external echo_try(Uint256) -> (Bool, Uint256) - function storeEcho (next : Uint256) : Unit := do + function allow_post_interaction_writes storeEcho (next : Uint256) : Unit := do let echoed := externalCall "echo" [next] setStorage echoedValue echoed @@ -905,6 +906,23 @@ verity_contract ExternalCallSmoke where let current ← getStorage echoedValue return current +-- tryExternalCall smoke: single-return external with success flag (#1727, Axis 1 Step 5f) +verity_contract TryExternalCallSmoke where + storage + lastResult : Uint256 := slot 0 + callSucceeded : Uint256 := slot 1 + linked_externals + external echo(Uint256) -> (Uint256) + external echo_try(Uint256) -> (Bool, Uint256) + + function allow_post_interaction_writes tryEcho (x : Uint256) : Unit := do + let (success, result) ← tryExternalCall "echo" [x] + if success then + setStorage lastResult result + setStorage callSucceeded 1 + else + setStorage callSucceeded 0 + verity_contract ERC20HelperSmoke where storage lastBalance : Uint256 := slot 0 @@ -920,17 +938,17 @@ verity_contract ERC20HelperSmoke where function approveTokens (token : Address, spender : Address, amount : Uint256) : Unit := do safeApprove token spender amount - function snapshotBalance (token : Address, owner : Address) : Uint256 := do + function allow_post_interaction_writes snapshotBalance (token : Address, owner : Address) : Uint256 := do let balance ← balanceOf token owner setStorage lastBalance balance return balance - function snapshotAllowance (token : Address, owner : Address, spender : Address) : Uint256 := do + function allow_post_interaction_writes snapshotAllowance (token : Address, owner : Address, spender : Address) : Uint256 := do let current ← allowance token owner spender setStorage lastAllowance current return current - function snapshotSupply (token : Address) : Uint256 := do + function allow_post_interaction_writes snapshotSupply (token : Address) : Uint256 := do let supply ← totalSupply token setStorage lastSupply supply return supply @@ -939,7 +957,7 @@ verity_contract GenericECMReadSmoke where storage lastQuote : Uint256 := slot 0 - function snapshotQuote (oracle : Address, asset : Address) : Uint256 := do + function allow_post_interaction_writes snapshotQuote (oracle : Address, asset : Address) : Uint256 := do let quote ← ecmCall (fun resultVar => Compiler.Modules.Oracle.oracleReadUint256Module resultVar 0x12345678 1) [oracle, asset] @@ -957,7 +975,7 @@ verity_contract LowLevelTryCatchSmoke where storage lastOutcome : Uint256 := slot 0 - function catchFailure () + function allow_post_interaction_writes catchFailure () local_obligations [manual_low_level_refinement := assumed "Low-level call success/failure boundary still requires a manual refinement argument."] : Uint256 := do @@ -966,7 +984,7 @@ verity_contract LowLevelTryCatchSmoke where let current ← getStorage lastOutcome return current - function skipCatchOnSuccess () + function allow_post_interaction_writes skipCatchOnSuccess () local_obligations [manual_low_level_refinement := assumed "Low-level call success/failure boundary still requires a manual refinement argument."] : Uint256 := do @@ -975,7 +993,7 @@ verity_contract LowLevelTryCatchSmoke where let current ← getStorage lastOutcome return current - function catchFailureWithShadowedParam (verity_try_success : Uint256) + function allow_post_interaction_writes catchFailureWithShadowedParam (verity_try_success : Uint256) local_obligations [manual_low_level_refinement := assumed "Low-level call success/failure boundary still requires a manual refinement argument."] : Uint256 := do @@ -1038,14 +1056,22 @@ verity_contract ExternalCallUnsupportedType where function noop () : Unit := do pure () -/-- -error: linked external 'fanout' currently supports at most one return value; statement-style external bindings are not exposed from verity_contract yet --/ -#guard_msgs in -verity_contract ExternalCallUnsupportedMultiReturn where +-- Multi-return externals are now allowed (Step 5f); use tryExternalCall +-- to call them with a success flag. +verity_contract ExternalCallMultiReturn where storage + lastValue : Uint256 := slot 0 linked_externals external fanout(Uint256) -> (Uint256, Address) + external fanout_try(Uint256) -> (Bool, Uint256, Address) + + function allow_post_interaction_writes callFanout (x : Uint256) : Unit := do + let (success, value, addr) ← tryExternalCall "fanout" [x] + if success then + setStorage lastValue value + setStorage lastValue addr + else + pure () function noop () : Unit := do pure () @@ -1113,7 +1139,7 @@ verity_contract LocalObligationRequiredForUnsafeFunctionBoundary where return head /-- -error: #check_contract failed for 'Contracts.Smoke.LocalObligationRequiredForUnsafeFunctionBoundary': Compilation error: function 'preview' uses low-level/assembly mechanic(s) calldataload without any local_obligations entry (Issue #1424 (controlled unsafe/assembly escape hatches)). Add local_obligations [...] to make the trust boundary explicit. +error: #check_contract failed for 'Contracts.Smoke.LocalObligationRequiredForUnsafeFunctionBoundary': Compilation error: function 'preview' uses low-level/assembly mechanic(s) calldataload outside an unsafe block without any local_obligations entry (Issue #1424 (controlled unsafe/assembly escape hatches)). Wrap the low-level code in `unsafe "reason" do` or add local_obligations [...] to make the trust boundary explicit. -/ #guard_msgs in #check_contract LocalObligationRequiredForUnsafeFunctionBoundary @@ -1129,7 +1155,7 @@ verity_contract LocalObligationRequiredForUnsafeConstructorBoundary where pure () /-- -error: #check_contract failed for 'Contracts.Smoke.LocalObligationRequiredForUnsafeConstructorBoundary': Compilation error: constructor uses low-level/assembly mechanic(s) mstore without any local_obligations entry (Issue #1424 (controlled unsafe/assembly escape hatches)). Add local_obligations [...] to make the trust boundary explicit. +error: #check_contract failed for 'Contracts.Smoke.LocalObligationRequiredForUnsafeConstructorBoundary': Compilation error: constructor uses low-level/assembly mechanic(s) mstore outside an unsafe block without any local_obligations entry (Issue #1424 (controlled unsafe/assembly escape hatches)). Wrap the low-level code in `unsafe "reason" do` or add local_obligations [...] to make the trust boundary explicit. -/ #guard_msgs in #check_contract LocalObligationRequiredForUnsafeConstructorBoundary @@ -1274,6 +1300,8 @@ end SpecGenSmoke #check_contract ZeroAddressShadowSmoke #check_contract StructMappingSmoke #check_contract ExternalCallSmoke +#check_contract TryExternalCallSmoke +#check_contract ExternalCallMultiReturn #check_contract Contracts.Vault example : TupleSmoke.setFromPair = (TupleSmoke.setFromPair : (Uint256 × Uint256) → Verity.Contract Unit) := rfl @@ -1628,4 +1656,730 @@ example : simpa using Contracts.ERC721.getApproved_semantic_preservation +-- #1729, Axis 3 Step 1b: smoke test for modifies(...) annotation +verity_contract ModifiesSmoke where + storage + counter : Uint256 := slot 0 + owner : Address := slot 1 + balances : Address → Uint256 := slot 2 + + constructor (initialOwner : Address) := do + setStorageAddr owner initialOwner + + -- Only modifies `counter`; owner and balances are untouched + function increment () modifies(counter) : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + + -- Modifies `owner` only + function transferOwnership (newOwner : Address) modifies(owner) : Unit := do + setStorageAddr owner newOwner + + -- Modifies both counter and balances + function deposit (amount : Uint256) modifies(counter, balances) : Unit := do + let sender ← msgSender + let current ← getStorage counter + setStorage counter (add current 1) + let balance ← getMapping balances sender + setMapping balances sender (add balance amount) + + -- View function (no modifies needed) + function view getCounter () : Uint256 := do + let current ← getStorage counter + return current + +-- #1729, Axis 3 Step 1c: smoke test for no_external_calls annotation +verity_contract NoExternalCallsSmoke where + storage + counter : Uint256 := slot 0 + owner : Address := slot 1 + + -- Pure arithmetic, no external calls + function no_external_calls increment () : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + + -- Read-only with no_external_calls + function view no_external_calls getCounter () : Uint256 := do + let current ← getStorage counter + return current + + -- Regular function without the annotation (for comparison) + function setOwner (newOwner : Address) : Unit := do + setStorageAddr owner newOwner + +-- #1729, Axis 3 Step 1d: smoke test for annotation composition (_effects theorem) +verity_contract EffectCompositionSmoke where + storage + counter : Uint256 := slot 0 + owner : Address := slot 1 + balances : Address → Uint256 := slot 2 + + -- view + no_external_calls: _effects bundles _is_view ∧ _no_calls + function view no_external_calls getCounter () : Uint256 := do + let current ← getStorage counter + return current + + -- no_external_calls + modifies: _effects bundles _no_calls ∧ _modifies + function no_external_calls increment () modifies(counter) : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + + -- Single annotation only (no _effects expected) + function no_external_calls setOwner (newOwner : Address) : Unit := do + setStorageAddr owner newOwner + + -- No annotations at all + function deposit (amount : Uint256) : Unit := do + let sender ← msgSender + let balance ← getMapping balances sender + setMapping balances sender (add balance amount) + +-- #1728, Axis 2 Step 2a: smoke test for CEI enforcement +verity_contract CEISmoke where + storage + counter : Uint256 := slot 0 + balances : Address → Uint256 := slot 1 + linked_externals + external echo(Uint256) -> (Uint256) + + -- CEI-compliant: effects before interactions (no external calls here) + function increment () : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + + -- CEI-compliant: no state writes at all (view-like) + function getCounter () : Uint256 := do + let current ← getStorage counter + return current + + -- CEI-compliant: effects before interaction + function updateThenCall (next : Uint256) : Uint256 := do + setStorage counter next + let echoed := externalCall "echo" [next] + return echoed + + -- Opted out with allow_post_interaction_writes: writes after call + function allow_post_interaction_writes callThenUpdate (next : Uint256) : Unit := do + let echoed := externalCall "echo" [next] + setStorage counter echoed + +-- #1728, Axis 2 Step 2b: smoke test for CEI escalation ladder (nonreentrant, cei_safe) +verity_contract CEILadderSmoke where + storage + counter : Uint256 := slot 0 + lock : Uint256 := slot 1 + balances : Address → Uint256 := slot 2 + linked_externals + external echo(Uint256) -> (Uint256) + + -- nonreentrant is recorded as metadata; it does not bypass CEI by itself. + function nonreentrant(lock) callThenStoreGuarded (x : Uint256) : Uint256 := do + setStorage counter x + let echoed := externalCall "echo" [x] + return echoed + + -- cei_safe is recorded as metadata; it does not bypass CEI by itself. + function cei_safe callThenStoreProved (x : Uint256) : Uint256 := do + setStorage counter x + let echoed := externalCall "echo" [x] + return echoed + + -- Normal function: CEI-compliant (effects before interactions), gets _cei_compliant + function storeThenCall (x : Uint256) : Uint256 := do + setStorage counter x + let echoed := externalCall "echo" [x] + return echoed + + -- Normal function: no external calls at all, gets _cei_compliant + function increment () : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + +-- Roles / requires(field) smoke test (#1728, Axis 2 Step 2c) +verity_contract RolesSmoke where + storage + admin : Address := slot 0 + counter : Uint256 := slot 1 + + constructor (initialAdmin : Address) := do + setStorageAddr admin initialAdmin + + -- requires(admin) auto-injects: require(caller == admin) "Access denied: caller is not admin" + function setCounter (value : Uint256) requires(admin) : Unit := do + setStorage counter value + + -- Normal function without access control + function getCounter () : Uint256 := do + let current ← getStorage counter + return current + +-- Newtype smoke test (#1727, Axis 1 Step 3a) +-- Declares semantic newtypes that are erased to base types at EVM level +verity_contract NewtypeSmoke where + types + TokenId : Uint256 + Amount : Uint256 + Owner : Address + storage + nextTokenId : Uint256 := slot 0 + totalSupply : Uint256 := slot 1 + minter : Address := slot 2 + + constructor (initialMinter : Address) := do + setStorageAddr minter initialMinter + + -- Newtypes are erased to their base types: TokenId → Uint256, Amount → Uint256 + function mint (id : TokenId, amount : Amount) : Unit := do + setStorage nextTokenId id + let current ← getStorage totalSupply + setStorage totalSupply (add current amount) + + -- Owner erases to Address + function setMinter (newMinter : Owner) : Unit := do + setStorageAddr minter newMinter + + function getNextTokenId () : Uint256 := do + let current ← getStorage nextTokenId + return current + +#check_contract RolesSmoke +#check_contract NewtypeSmoke + +-- Smoke test for newtype-TYPED storage fields (not just newtype params). +-- Verifies that setStorage/setStorageAddr/getStorage/getStorageAddr work +-- when the storage field itself is declared with a newtype type. +verity_contract NewtypeStorageSmoke where + types + TokenId : Uint256 + Owner : Address + storage + currentTokenId : TokenId := slot 0 + admin : Owner := slot 1 + + constructor (initialAdmin : Owner) := do + setStorageAddr admin initialAdmin + + function setTokenId (id : TokenId) : Unit := do + setStorage currentTokenId id + + function getTokenId () : Uint256 := do + let tid ← getStorage currentTokenId + return tid + + function setAdmin (newAdmin : Owner) : Unit := do + setStorageAddr admin newAdmin + + function getAdmin () : Address := do + let a ← getStorageAddr admin + return a + +#check_contract NewtypeStorageSmoke + +-- Every contract emits a storageNamespace : Nat definition (#1730, Axis 4 Step 4a). +-- Verify a few representative contracts have it and it is a Nat. +example : Contracts.Counter.storageNamespace = Contracts.Counter.storageNamespace := rfl +example : NewtypeSmoke.storageNamespace = NewtypeSmoke.storageNamespace := rfl + +-- Namespaced storage smoke test (#1730, Axis 4 Step 4b). +-- When `storage_namespace` is present, user-declared slot numbers are offset +-- by keccak256("{ContractName}.storage.v0") so different contracts never collide. +verity_contract NamespacedStorageSmoke where + storage_namespace + storage + balance : Uint256 := slot 0 + owner : Address := slot 1 + + constructor (initialOwner : Address) := do + setStorageAddr owner initialOwner + + function deposit (amount : Uint256) : Unit := do + let current ← getStorage balance + setStorage balance (add current amount) + + function getOwner () : Address := do + let addr ← getStorageAddr owner + return addr + +#check_contract NamespacedStorageSmoke + +-- Verify that NamespacedStorageSmoke's storage slots differ from +-- non-namespaced contracts: slot 0 is offset by the namespace base. +-- The slot values embed the keccak-based namespace offset. +example : NamespacedStorageSmoke.balance.slot ≠ 0 := by decide +example : NamespacedStorageSmoke.owner.slot ≠ 1 := by decide + +-- Verify storageNamespace flows into the CompilationModel spec (#1730, Axis 4 Step 4d). +-- Namespaced contracts carry `some ns`; non-namespaced carry `none`. +example : NamespacedStorageSmoke.spec.storageNamespace.isSome = true := rfl +example : Contracts.Counter.spec.storageNamespace.isNone = true := rfl + +-- Custom namespace override (#1730, Axis 4 Step 4c) +-- Uses `storage_namespace "custom.v0"` instead of the default contract-name-based key. +-- The keccak256 is computed on the literal string "custom.v0". +verity_contract CustomNamespacedSmoke where + storage_namespace "custom.v0" + storage + balance : Uint256 := slot 0 + owner : Address := slot 1 + + function deposit (amount : Uint256) : Unit := do + let current ← getStorage balance + setStorage balance (add current amount) + + function getOwner () : Address := do + let addr ← getStorageAddr owner + return addr + +#check_contract CustomNamespacedSmoke + +-- Verify custom namespace: slots are offset (nonzero) and differ from the +-- default-namespaced contract (which uses keccak256("NamespacedStorageSmoke.storage.v0")). +example : CustomNamespacedSmoke.balance.slot ≠ 0 := by decide +example : CustomNamespacedSmoke.owner.slot ≠ 1 := by decide +example : CustomNamespacedSmoke.balance.slot ≠ NamespacedStorageSmoke.balance.slot := by decide +example : CustomNamespacedSmoke.spec.storageNamespace.isSome = true := rfl +-- Verify the exported storageNamespace constant matches the spec value (not the default contract name hash). +example : CustomNamespacedSmoke.storageNamespace = CustomNamespacedSmoke.spec.storageNamespace.get! := rfl +example : CustomNamespacedSmoke.storageNamespace = 105542539407630759878214364786123406227647255732885741380220581264062975076298 := rfl +example : CustomNamespacedSmoke.storageNamespace ≠ 67387409610395734986217237394999073412260967828994783805404864304835768435504 := by decide + +-- ADT (inductive) section smoke test (#1727, Axis 1 Steps 5a/5b) +-- Declares algebraic data types with typed variant fields. +-- ADT type definitions flow through to ContractSpec.adtTypes. +-- ADT-typed storage fields are represented as Uint256 (tag value) at the EVM level. +verity_contract AdtSmoke where + types + TokenId : Uint256 + inductive + OptionalUint := | Some(value : Uint256) | None + Result := | Ok(amount : Uint256, recipient : Address) | Err(code : Uint256) + storage + counter : Uint256 := slot 0 + result : OptionalUint := slot 1 + + function increment () : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + +#check_contract AdtSmoke + +-- Verify ADT type definitions flow through to spec (#1727, Step 5b plumbing) +example : AdtSmoke.spec.adtTypes.length = 2 := rfl +example : AdtSmoke.spec.adtTypes.map (·.name) = ["OptionalUint", "Result"] := rfl +example : (AdtSmoke.spec.adtTypes.map (·.variants.length)) = [2, 2] := rfl + +-- Unsafe block smoke test (#1424, Phase 6 Step 6a). +-- `unsafe "reason" do` wraps a block of statements; Step 6a is the transparent +-- wrapper (validation/compilation recurse into the body unchanged). +verity_contract UnsafeBlockSmoke where + storage + counter : Uint256 := slot 0 + + function incrementUnsafe () : Unit := do + unsafe "demo: testing unsafe block syntax" do + let current ← getStorage counter + setStorage counter (add current 1) + + function getCounter () : Uint256 := do + let current ← getStorage counter + return current + +#check_contract UnsafeBlockSmoke + +-- Unsafe gating positive test (#1728, Phase 6 Step 6b). +-- Low-level mstore inside `unsafe` block passes #check_contract +-- WITHOUT requiring local_obligations. +verity_contract UnsafeGatingAccepted where + storage + counter : Uint256 := slot 0 + + function writeMem () : Unit := do + unsafe "manual memory write for packed encoding" do + mstore 0 1 + pure () + +#check_contract UnsafeGatingAccepted + +-- Unsafe gating negative test (#1728, Phase 6 Step 6b). +-- Low-level mstore OUTSIDE unsafe block (and no local_obligations) is rejected. +verity_contract UnsafeGatingRejected where + storage + + constructor () := do + mstore 0 1 + pure () + + function noop () : Unit := do + pure () + +/-- +error: #check_contract failed for 'Contracts.Smoke.UnsafeGatingRejected': Compilation error: constructor uses low-level/assembly mechanic(s) mstore outside an unsafe block without any local_obligations entry (Issue #1424 (controlled unsafe/assembly escape hatches)). Wrap the low-level code in `unsafe "reason" do` or add local_obligations [...] to make the trust boundary explicit. +-/ +#guard_msgs in +#check_contract UnsafeGatingRejected + +-- CEI violation test: this contract compiles but #check_contract rejects it +verity_contract CEIViolationRejected where + storage + result : Uint256 := slot 0 + linked_externals + external echo(Uint256) -> (Uint256) + + function callThenStore (x : Uint256) : Unit := do + let echoed := externalCall "echo" [x] + setStorage result echoed + +/-- +error: #check_contract failed for 'Contracts.Smoke.CEIViolationRejected': Compilation error: function 'callThenStore' violates CEI (Checks-Effects-Interactions) ordering: state write after external call. Reorder state writes before external calls, or annotate with allow_post_interaction_writes to opt out (Issue #1728 (CEI enforcement — Checks-Effects-Interactions ordering)) +-/ +#guard_msgs in +#check_contract CEIViolationRejected + +/-- +error: function 'guarded': nonreentrant lock field 'lock' must be a scalar Uint256 storage field +-/ +#guard_msgs in +verity_contract NonreentrantAddressLockRejected where + storage + lock : Address := slot 0 + + function nonreentrant(lock) guarded () : Unit := do + pure () + +-- ════════════════════════════════════════════════════════════════════════════ +-- Stress-test contracts: edge-case coverage for Language Design Axes (#1731) +-- ════════════════════════════════════════════════════════════════════════════ + +-- CEI edge case 1: write after external call, inside an if-branch — should detect +verity_contract CEIWriteInBranchAfterCall where + storage + counter : Uint256 := slot 0 + linked_externals + external echo(Uint256) -> (Uint256) + + -- Call then conditional write: CEI violation + function callThenConditionalWrite (x : Uint256) : Unit := do + let echoed := externalCall "echo" [x] + if echoed != 0 then + setStorage counter echoed + else + pure () + +/-- +error: #check_contract failed for 'Contracts.Smoke.CEIWriteInBranchAfterCall': Compilation error: function 'callThenConditionalWrite' violates CEI (Checks-Effects-Interactions) ordering: in if-then branch: state write after external call. Reorder state writes before external calls, or annotate with allow_post_interaction_writes to opt out (Issue #1728 (CEI enforcement — Checks-Effects-Interactions ordering)) +-/ +#guard_msgs in +#check_contract CEIWriteInBranchAfterCall + +-- CEI edge case 2: call at top level, write after — same as CEIViolationRejected but +-- with the write in both branches of an if (to test compound-statement propagation) +verity_contract CEICallBothBranchesWrite where + storage + counter : Uint256 := slot 0 + linked_externals + external echo(Uint256) -> (Uint256) + + function callThenBranchWrite (x : Uint256) : Unit := do + let echoed := externalCall "echo" [x] + if echoed != 0 then + setStorage counter echoed + else + setStorage counter 0 + +/-- +error: #check_contract failed for 'Contracts.Smoke.CEICallBothBranchesWrite': Compilation error: function 'callThenBranchWrite' violates CEI (Checks-Effects-Interactions) ordering: in if-then branch: state write after external call. Reorder state writes before external calls, or annotate with allow_post_interaction_writes to opt out (Issue #1728 (CEI enforcement — Checks-Effects-Interactions ordering)) +-/ +#guard_msgs in +#check_contract CEICallBothBranchesWrite + +-- Modifies + roles: combined annotation +verity_contract ModifiesRolesSmoke where + storage + admin : Address := slot 0 + counter : Uint256 := slot 1 + flag : Uint256 := slot 2 + + constructor (initialAdmin : Address) := do + setStorageAddr admin initialAdmin + + -- Combines requires(admin), modifies(counter), and no_external_calls + function no_external_calls setCounter (value : Uint256) requires(admin) modifies(counter) : Unit := do + setStorage counter value + + -- Combines requires(admin) and modifies(counter, flag) + function setCounterAndFlag (value : Uint256, flagValue : Uint256) requires(admin) modifies(counter, flag) : Unit := do + setStorage counter value + setStorage flag flagValue + + function view getCounter () : Uint256 := do + let current ← getStorage counter + return current + +#check_contract ModifiesRolesSmoke + +-- Modifies + namespace: namespaced storage with modifies annotations +verity_contract ModifiesNamespaceSmoke where + storage_namespace + storage + counter : Uint256 := slot 0 + owner : Address := slot 1 + + constructor (initialOwner : Address) := do + setStorageAddr owner initialOwner + + function increment () modifies(counter) : Unit := do + let current ← getStorage counter + setStorage counter (add current 1) + + function transferOwnership (newOwner : Address) modifies(owner) : Unit := do + setStorageAddr owner newOwner + + function view getCounter () : Uint256 := do + let current ← getStorage counter + return current + +#check_contract ModifiesNamespaceSmoke + +-- Verify namespaced modifies slots are actually offset +example : ModifiesNamespaceSmoke.counter.slot ≠ 0 := by decide +example : ModifiesNamespaceSmoke.owner.slot ≠ 1 := by decide + +-- ADT edge case: single variant (no branching needed) +verity_contract AdtSingleVariant where + inductive + Sentinel := | Active + storage + tag : Sentinel := slot 0 + + function store () : Unit := do + setStorage tag (adt "Active") + +#check_contract AdtSingleVariant + +-- ADT edge case: variant with zero fields vs variant with multiple fields +verity_contract AdtMixedFieldCounts where + inductive + Maybe := | Nothing | Just(value : Uint256) + Pair := | MkPair(fst : Uint256, snd : Uint256) + storage + result : Maybe := slot 0 + + function clear () : Unit := do + setStorage result (adt "Nothing") + + function set (_value : Uint256) : Unit := do + setStorage result (adt "Just" [_value]) + +#check_contract AdtMixedFieldCounts + +-- Verify ADT spec plumbing for mixed field counts +example : AdtMixedFieldCounts.spec.adtTypes.length = 2 := rfl +example : AdtMixedFieldCounts.spec.adtTypes.map (·.name) = ["Maybe", "Pair"] := rfl + +-- Newtype + modifies: newtypes used in function params with modifies annotation +verity_contract NewtypeModifiesSmoke where + types + TokenId : Uint256 + Amount : Uint256 + storage + nextTokenId : Uint256 := slot 0 + totalMinted : Uint256 := slot 1 + + function mint (id : TokenId, amount : Amount) modifies(nextTokenId, totalMinted) : Unit := do + setStorage nextTokenId id + let current ← getStorage totalMinted + setStorage totalMinted (add current amount) + + function view getNextId () : Uint256 := do + let current ← getStorage nextTokenId + return current + +#check_contract NewtypeModifiesSmoke + +-- Newtype + namespace combo +verity_contract NewtypeNamespaceSmoke where + types + TokenId : Uint256 + storage_namespace "newtype.ns.v0" + storage + nextId : Uint256 := slot 0 + + function setId (id : TokenId) : Unit := do + setStorage nextId id + + function view getId () : Uint256 := do + let current ← getStorage nextId + return current + +#check_contract NewtypeNamespaceSmoke + +-- Verify namespace offset applies +example : NewtypeNamespaceSmoke.nextId.slot ≠ 0 := by decide + +-- Unsafe block + CEI: write after unsafe block that has a call — should detect +verity_contract UnsafeCEIViolation where + storage + counter : Uint256 := slot 0 + linked_externals + external echo(Uint256) -> (Uint256) + + function unsafeCallThenWrite (x : Uint256) : Unit := do + let echoed := externalCall "echo" [x] + unsafe "test: write inside unsafe after call" do + setStorage counter echoed + +/-- +error: #check_contract failed for 'Contracts.Smoke.UnsafeCEIViolation': Compilation error: function 'unsafeCallThenWrite' violates CEI (Checks-Effects-Interactions) ordering: in unsafe block: state write after external call. Reorder state writes before external calls, or annotate with allow_post_interaction_writes to opt out (Issue #1728 (CEI enforcement — Checks-Effects-Interactions ordering)) +-/ +#guard_msgs in +#check_contract UnsafeCEIViolation + +-- Unsafe block + CEI: write inside unsafe before call — should pass +verity_contract UnsafeCEICompliant where + storage + counter : Uint256 := slot 0 + linked_externals + external echo(Uint256) -> (Uint256) + + function writeBeforeUnsafeCall (x : Uint256) : Uint256 := do + setStorage counter x + unsafe "test: call inside unsafe after write" do + let echoed := externalCall "echo" [x] + return echoed + +#check_contract UnsafeCEICompliant + +-- CEI: internal call after external call — the internal call may write storage, +-- so this should be flagged as a CEI violation. +verity_contract CEIInternalCallAfterExternalRejected where + storage + counter : Uint256 := slot 0 + linked_externals + external echo(Uint256) -> (Uint256) + + function increment (amount : Uint256) : Unit := do + let current ← getStorage counter + setStorage counter (add current amount) + + function callThenHelper (x : Uint256) : Unit := do + let echoed := externalCall "echo" [x] + increment echoed + +/-- +error: #check_contract failed for 'Contracts.Smoke.CEIInternalCallAfterExternalRejected': Compilation error: function 'callThenHelper' violates CEI (Checks-Effects-Interactions) ordering: state write after external call. Reorder state writes before external calls, or annotate with allow_post_interaction_writes to opt out (Issue #1728 (CEI enforcement — Checks-Effects-Interactions ordering)) +-/ +#guard_msgs in +#check_contract CEIInternalCallAfterExternalRejected + +-- Roles + CEI combo: role guard with CEI-compliant external call +verity_contract RolesCEISmoke where + storage + admin : Address := slot 0 + counter : Uint256 := slot 1 + linked_externals + external echo(Uint256) -> (Uint256) + + constructor (initialAdmin : Address) := do + setStorageAddr admin initialAdmin + + -- CEI compliant: write, then call + function setAndCall (value : Uint256) requires(admin) : Uint256 := do + setStorage counter value + let echoed := externalCall "echo" [value] + return echoed + + function view getCounter () : Uint256 := do + let current ← getStorage counter + return current + +#check_contract RolesCEISmoke + +-- Nonreentrant + modifies: combined annotations +verity_contract NonreentrantModifiesSmoke where + storage + lock : Uint256 := slot 0 + counter : Uint256 := slot 1 + balance : Uint256 := slot 2 + linked_externals + external echo(Uint256) -> (Uint256) + + -- nonreentrant metadata composes with modifies, while the body remains CEI-ordered. + function nonreentrant(lock) deposit (amount : Uint256) modifies(counter, balance) : Uint256 := do + let current ← getStorage counter + setStorage counter (add current 1) + let bal ← getStorage balance + setStorage balance (add bal amount) + let echoed := externalCall "echo" [amount] + return echoed + + function view getBalance () : Uint256 := do + let bal ← getStorage balance + return bal + +#check_contract NonreentrantModifiesSmoke + +-- Multiple ADTs + newtype in same contract +verity_contract AdtNewtypeCombo where + types + TokenId : Uint256 + Owner : Address + inductive + Status := | Active | Paused | Deprecated + OptionalId := | SomeId(id : Uint256) | NoId + storage + contractStatus : Status := slot 0 + lastTokenId : OptionalId := slot 1 + + function pause () : Unit := do + setStorage contractStatus (adt "Paused") + + function unpause () : Unit := do + setStorage contractStatus (adt "Active") + + function setLastId (_id : TokenId) : Unit := do + setStorage lastTokenId (adt "SomeId" [_id]) + +#check_contract AdtNewtypeCombo + +-- Verify ADT spec for 3-variant enum +example : AdtNewtypeCombo.spec.adtTypes.length = 2 := rfl +example : AdtNewtypeCombo.spec.adtTypes.map (·.name) = ["Status", "OptionalId"] := rfl +-- Status has 3 variants: Active(0), Paused(1), Deprecated(2); OptionalId has 2 +example : AdtNewtypeCombo.spec.adtTypes.map (·.variants.length) = [3, 2] := rfl + +-- Full combo: namespace + newtype + modifies + roles + CEI +verity_contract FullComboSmoke where + types + Amount : Uint256 + inductive + TokenStatus := | Active | Frozen + storage_namespace "fullcombo.v0" + storage + admin : Address := slot 0 + balance : Uint256 := slot 1 + status : TokenStatus := slot 2 + + constructor (initialAdmin : Address) := do + setStorageAddr admin initialAdmin + + function no_external_calls deposit (amount : Amount) requires(admin) modifies(balance) : Unit := do + let current ← getStorage balance + setStorage balance (add current amount) + + function no_external_calls freeze () requires(admin) modifies(status) : Unit := do + setStorage status (adt "Frozen") + + function view no_external_calls getBalance () : Uint256 := do + let current ← getStorage balance + return current + +#check_contract FullComboSmoke + +-- Verify combo properties +example : FullComboSmoke.balance.slot ≠ 1 := by decide -- namespaced +example : FullComboSmoke.spec.storageNamespace.isSome = true := rfl +example : FullComboSmoke.spec.adtTypes.length = 1 := rfl +example : FullComboSmoke.spec.adtTypes.map (·.name) = ["TokenStatus"] := rfl + end Contracts.Smoke diff --git a/PrintAxioms.lean b/PrintAxioms.lean index ede9f51dd..998976660 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -1373,6 +1373,10 @@ import Compiler.Proofs.YulGeneration.ReferenceOracle.Semantics -- #print axioms Compiler.Proofs.IRGeneration.findDynamicArrayElementAtSlot_go_eq_copy -- private -- #print axioms Compiler.Proofs.IRGeneration.findDynamicArrayElementAtSlotCopy_eq -- private -- #print axioms Compiler.Proofs.IRGeneration.encodeStorageAt_eq_copy -- private +-- #print axioms Compiler.Proofs.IRGeneration.fieldWriteEntriesAt_base_mem -- private +-- #print axioms Compiler.Proofs.IRGeneration.exists_mem_zipIdx_of_mem -- private +-- #print axioms Compiler.Proofs.IRGeneration.fieldWriteEntriesAt_alias_mem -- private +-- #print axioms Compiler.Proofs.IRGeneration.fieldWriteEntriesAt_packed_none_of_unpacked -- private -- #print axioms Compiler.Proofs.IRGeneration.list_findSlotPackedNone_ne_none -- private -- #print axioms Compiler.Proofs.IRGeneration.firstInFieldConflictCopy_ne_none_of_seen_slot_unpacked -- private -- #print axioms Compiler.Proofs.IRGeneration.firstFieldWriteSlotConflictCopyFrom_some_of_seen_slot_member -- private @@ -2005,7 +2009,9 @@ import Compiler.Proofs.YulGeneration.ReferenceOracle.Semantics -- #print axioms Compiler.Proofs.IRGeneration.List.mem_of_mem_eraseDups -- private #print axioms Compiler.Proofs.IRGeneration.helperCallNames_nodup #print axioms Compiler.Proofs.IRGeneration.exprHelperCallNames_nodup +-- #print axioms Compiler.Proofs.IRGeneration.matchAdtBranchesExprSubsetInternal_aux -- private -- #print axioms Compiler.Proofs.IRGeneration.stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNames -- private +-- #print axioms Compiler.Proofs.IRGeneration.matchAdtBranchesExprHelperCallNames_subset_internalHelperCallNames -- private #print axioms Compiler.Proofs.IRGeneration.stmtExprHelperCallNames_subset_stmtInternalHelperCallNames #print axioms Compiler.Proofs.IRGeneration.exprHelperCallNames_subset_helperCallNames #print axioms Compiler.Proofs.IRGeneration.SupportedConstructor.paramNamesNodup @@ -2139,6 +2145,8 @@ import Compiler.Proofs.YulGeneration.ReferenceOracle.Semantics #print axioms Compiler.Proofs.IRGeneration.SupportedSpecExceptMappingWrites.noErrors #print axioms Compiler.Proofs.IRGeneration.SupportedSpec.noExternals #print axioms Compiler.Proofs.IRGeneration.SupportedSpecExceptMappingWrites.noExternals +#print axioms Compiler.Proofs.IRGeneration.SupportedSpec.noAdtTypes +#print axioms Compiler.Proofs.IRGeneration.SupportedSpecExceptMappingWrites.noAdtTypes #print axioms Compiler.Proofs.IRGeneration.SupportedSpec.noFallback #print axioms Compiler.Proofs.IRGeneration.SupportedSpecExceptMappingWrites.noFallback #print axioms Compiler.Proofs.IRGeneration.SupportedSpec.noReceive @@ -2990,4 +2998,4 @@ import Compiler.Proofs.YulGeneration.ReferenceOracle.Semantics -- Compiler/Proofs/YulGeneration/ReferenceOracle/Semantics.lean #print axioms Compiler.Proofs.YulGeneration.YulTransaction.ofIR_sender #print axioms Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args --- Total: 2823 theorems/lemmas (1946 public, 877 private, 0 sorry'd) +-- Total: 2831 theorems/lemmas (1948 public, 883 private, 0 sorry'd) diff --git a/Verity/Macro/Bridge.lean b/Verity/Macro/Bridge.lean index ca92ec1f1..780b38640 100644 --- a/Verity/Macro/Bridge.lean +++ b/Verity/Macro/Bridge.lean @@ -55,4 +55,185 @@ def mkViewTheoremCommand (fnDecl : FunctionDecl) : CommandElabM Cmd := do (Compiler.CompilationModel.FunctionSpec.isView ($modelName : Compiler.CompilationModel.FunctionSpec)) = true := rfl) +/-- Auto-generated `_no_calls` theorem for functions with a `no_external_calls` annotation + (#1729, Axis 3 Step 1c). Emits a `@[simp]` lemma stating the model's + `noExternalCalls` flag is `true`. Only called when `fnDecl.noExternalCalls`. -/ +def mkNoCallsTheoremCommand (fnDecl : FunctionDecl) : CommandElabM Cmd := do + let noCallsName ← mkSuffixedIdent fnDecl.ident "_no_calls" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + `(command| + @[simp] theorem $noCallsName : + (Compiler.CompilationModel.FunctionSpec.noExternalCalls + ($modelName : Compiler.CompilationModel.FunctionSpec)) = true := rfl) + +/-- Auto-generated `_cei_compliant` theorem for functions that pass CEI + (Checks-Effects-Interactions) validation without opting out. + Emits a `@[simp]` lemma stating the model's `allowPostInteractionWrites` + flag is `false`, certifying that the function was compiler-verified for + CEI compliance. Only called when `!fnDecl.allowPostInteractionWrites` and + the function body passed CEI analysis. (#1728, Axis 2 Step 2a) -/ +def mkCEICompliantTheoremCommand (fnDecl : FunctionDecl) : CommandElabM Cmd := do + let ceiName ← mkSuffixedIdent fnDecl.ident "_cei_compliant" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + `(command| + @[simp] theorem $ceiName : + (Compiler.CompilationModel.FunctionSpec.allowPostInteractionWrites + ($modelName : Compiler.CompilationModel.FunctionSpec)) = false := rfl) + +/-- Auto-generated `_nonreentrant` theorem for functions with a `nonreentrant(field)` annotation + (#1728, Axis 2 Step 2b). Records the reentrancy lock field as a `@[simp]` fact, certifying + that the function uses a known-safe guard for CEI compliance. -/ +def mkNonReentrantTheoremCommand (fnDecl : FunctionDecl) (lockFieldName : String) : CommandElabM Cmd := do + let nonReentrantName ← mkSuffixedIdent fnDecl.ident "_nonreentrant" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + `(command| + @[simp] theorem $nonReentrantName : + (Compiler.CompilationModel.FunctionSpec.nonReentrantLock + ($modelName : Compiler.CompilationModel.FunctionSpec)) = some $(strTermPublic lockFieldName) := rfl) + +/-- Auto-generated `_cei_safe` theorem for functions with a `cei_safe` annotation + (#1728, Axis 2 Step 2b). Records the `ceiSafe` flag as a `@[simp]` fact, certifying + that the user has asserted CEI safety via machine-checked proof obligation. -/ +def mkCEISafeTheoremCommand (fnDecl : FunctionDecl) : CommandElabM Cmd := do + let ceiSafeName ← mkSuffixedIdent fnDecl.ident "_cei_safe" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + `(command| + @[simp] theorem $ceiSafeName : + (Compiler.CompilationModel.FunctionSpec.ceiSafe + ($modelName : Compiler.CompilationModel.FunctionSpec)) = true := rfl) + +/-- Auto-generated `_requires_role` theorem for functions with a `requires(field)` annotation + (#1728, Axis 2 Step 2c). Records the role field name as a `@[simp]` fact, certifying + that the function has an access-control guard for the named role. -/ +def mkRequiresRoleTheoremCommand (fnDecl : FunctionDecl) (roleFieldName : String) : CommandElabM Cmd := do + let requiresRoleName ← mkSuffixedIdent fnDecl.ident "_requires_role" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + `(command| + @[simp] theorem $requiresRoleName : + (Compiler.CompilationModel.FunctionSpec.requiresRole + ($modelName : Compiler.CompilationModel.FunctionSpec)) = some $(strTermPublic roleFieldName) := rfl) + +/-- Auto-generated `_modifies` theorem for functions with a `modifies(...)` annotation + (#1729, Axis 3 Step 1b). Records the declared modifies set as a `@[simp]` fact. -/ +def mkModifiesTheoremCommand (fnDecl : FunctionDecl) : CommandElabM Cmd := do + let modifiesName ← mkSuffixedIdent fnDecl.ident "_modifies" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + let fieldTerms : Array Term := fnDecl.modifies.map fun ident => strTermPublic (toString ident.getId) + `(command| + @[simp] theorem $modifiesName : + (Compiler.CompilationModel.FunctionSpec.modifies + ($modelName : Compiler.CompilationModel.FunctionSpec)) = [ $[$fieldTerms],* ] := rfl) + +/-- Build a single frame-condition conjunct for a storage field that is NOT + in the modifies set. The conjunct depends on the field's storage type. -/ +private def mkFieldFrameConjunct (field : StorageFieldDecl) : CommandElabM Term := do + let slotLit : Term := ⟨Syntax.mkNumLit (toString field.slotNum)⟩ + match field.ty with + | .scalar _ => + -- For scalar uint256 (or any scalar): s'.storage slot = s.storage slot + -- For scalar address (or address newtype): s'.storageAddr slot = s.storageAddr slot + -- We use the right accessor based on the scalar type + match field.ty with + | .scalar .address | .scalar (.newtype _ .address) => + `(Verity.Specs.sameStorageAddrSlot $slotLit s s') + | .scalar (.adt _ maxFields) => + let mut body : Term ← `(Verity.Specs.sameStorageSlot $slotLit s s') + for idx in List.range maxFields do + let payloadSlot : Term := ⟨Syntax.mkNumLit (toString (field.slotNum + idx + 1))⟩ + body ← `($body ∧ Verity.Specs.sameStorageSlot $payloadSlot s s') + pure body + | _ => + `(Verity.Specs.sameStorageSlot $slotLit s s') + | .dynamicArray _ => + -- storageArray slot is unchanged + `(s'.storageArray $slotLit = s.storageArray $slotLit) + | .mappingAddressToUint256 | .mappingStruct .address _ => + -- ∀ k, s'.storageMap slot k = s.storageMap slot k + `(∀ k, s'.storageMap $slotLit k = s.storageMap $slotLit k) + | .mappingUintToUint256 | .mappingStruct .uint256 _ => + `(∀ k, s'.storageMapUint $slotLit k = s.storageMapUint $slotLit k) + | .mappingChain _ | .mapping2AddressToAddressToUint256 | .mappingStruct2 _ _ _ => + -- These shapes compile to hashed `storage` slots rather than the legacy + -- storageMap mirrors, so the conservative frame predicate must constrain + -- the hashed storage surface. + `(Verity.Specs.sameStorage s s') + +/-- Auto-generate a `_frame` definition and `_frame_rfl` lemma for functions + with `modifies(...)`. The frame is a conjunction of "unchanged" predicates + for every storage field NOT in the declared modifies set, plus `sameContext`. + (#1729, Axis 3 Step 1b) -/ +def mkFrameDefCommand + (fields : Array StorageFieldDecl) + (fnDecl : FunctionDecl) : CommandElabM (Array Cmd) := do + let frameName ← mkSuffixedIdent fnDecl.ident "_frame" + let frameRflName ← mkSuffixedIdent fnDecl.ident "_frame_rfl" + let modifiesNames := fnDecl.modifies.map fun ident => toString ident.getId + let unmodifiedFields := fields.filter fun f => !modifiesNames.contains f.name + + -- Build the conjunction: sameContext ∧ field1_unchanged ∧ field2_unchanged ∧ ... + let mut body : Term ← `(Verity.Specs.sameContext s s') + for field in unmodifiedFields do + let conjunct ← mkFieldFrameConjunct field + body ← `($body ∧ $conjunct) + + let frameCmd : Cmd ← `(command| + /-- Auto-generated frame condition: fields not in `modifies(...)` are unchanged. -/ + def $frameName (s s' : Verity.ContractState) : Prop := + $body) + + let frameRflCmd : Cmd ← `(command| + @[simp] theorem $frameRflName (s : Verity.ContractState) : $frameName s s := by + unfold $frameName + simp [Verity.Specs.sameContext, Verity.Specs.sameStorageSlot, + Verity.Specs.sameStorageAddrSlot, Verity.Specs.sameStorage]) + + pure #[frameCmd, frameRflCmd] + +/-- Count how many effect annotations are active on a function declaration. -/ +def effectAnnotationCount (fnDecl : FunctionDecl) : Nat := + (if fnDecl.isView then 1 else 0) + + (if fnDecl.noExternalCalls then 1 else 0) + + (if !fnDecl.modifies.isEmpty then 1 else 0) + +/-- Auto-generated `_effects` conjunction theorem for functions with multiple + effect annotations (#1729, Axis 3 Step 1d). Bundles all individual effect + theorems (`_is_view`, `_no_calls`, `_modifies`) into a single `And` fact so + downstream proofs can obtain all guarantees in one `exact`. -/ +def mkEffectsTheoremCommand (fnDecl : FunctionDecl) : CommandElabM Cmd := do + let effectsName ← mkSuffixedIdent fnDecl.ident "_effects" + let modelName ← mkSuffixedIdent fnDecl.ident "_model" + -- Collect conjunct terms and proof terms for each active annotation + let mut conjuncts : Array Term := #[] + let mut proofs : Array Term := #[] + if fnDecl.isView then + let viewName ← mkSuffixedIdent fnDecl.ident "_is_view" + conjuncts := conjuncts.push (← `( + (Compiler.CompilationModel.FunctionSpec.isView + ($modelName : Compiler.CompilationModel.FunctionSpec)) = true)) + proofs := proofs.push (← `($viewName)) + if fnDecl.noExternalCalls then + let noCallsName ← mkSuffixedIdent fnDecl.ident "_no_calls" + conjuncts := conjuncts.push (← `( + (Compiler.CompilationModel.FunctionSpec.noExternalCalls + ($modelName : Compiler.CompilationModel.FunctionSpec)) = true)) + proofs := proofs.push (← `($noCallsName)) + if !fnDecl.modifies.isEmpty then + let modifiesName ← mkSuffixedIdent fnDecl.ident "_modifies" + let fieldTerms : Array Term := fnDecl.modifies.map fun ident => strTermPublic (toString ident.getId) + conjuncts := conjuncts.push (← `( + (Compiler.CompilationModel.FunctionSpec.modifies + ($modelName : Compiler.CompilationModel.FunctionSpec)) = [ $[$fieldTerms],* ])) + proofs := proofs.push (← `($modifiesName)) + -- Build right-nested And: P₁ ∧ P₂ ∧ ... ∧ Pn + -- For n=2: And.intro p₁ p₂ + -- For n=3: And.intro p₁ (And.intro p₂ p₃) + let mut propTerm := conjuncts[conjuncts.size - 1]! + for i in List.range (conjuncts.size - 1) |>.reverse do + propTerm ← `($(conjuncts[i]!) ∧ $propTerm) + let mut proofTerm := proofs[proofs.size - 1]! + for i in List.range (proofs.size - 1) |>.reverse do + proofTerm ← `(And.intro $(proofs[i]!) $proofTerm) + `(command| + @[simp] theorem $effectsName : + $propTerm := $proofTerm) end Verity.Macro diff --git a/Verity/Macro/Elaborate.lean b/Verity/Macro/Elaborate.lean index 59f0bb225..e49f70064 100644 --- a/Verity/Macro/Elaborate.lean +++ b/Verity/Macro/Elaborate.lean @@ -13,7 +13,7 @@ set_option hygiene false @[command_elab verityContractCmd] def elabVerityContract : CommandElab := fun stx => do - let (contractName, fields, errorDecls, constDecls, immutableDecls, externalDecls, ctor, functions) ← parseContractSyntax stx + let (contractName, _newtypeDecls, adtDecls, fields, errorDecls, constDecls, immutableDecls, externalDecls, ctor, functions, storageNamespace) ← parseContractSyntax stx validateGeneratedDefNamesPublic fields constDecls functions validateConstantDeclsPublic constDecls @@ -32,13 +32,17 @@ def elabVerityContract : CommandElab := fun stx => do for imm in immutableDecls.zipIdx do elabCommand (← mkStorageDefCommandPublic (immutableStorageFieldDecl fields imm.1 imm.2)) + -- Emit storageNamespace : Nat for the contract (#1730, Axis 4 Step 4a). + -- Use the resolved namespace from parseContractSyntax to respect custom keys. + elabCommand (← mkStorageNamespaceCommand (toString contractName.getId) storageNamespace) + for fn in functions do - let fnCmds ← mkFunctionCommandsPublic fields constDecls immutableDecls functions fn + let fnCmds ← mkFunctionCommandsPublic fields constDecls immutableDecls externalDecls functions fn for cmd in fnCmds do elabCommand cmd elabCommand (← mkBridgeCommand fn.ident) - elabCommand (← mkSpecCommandPublic (toString contractName.getId) fields errorDecls constDecls immutableDecls externalDecls ctor functions) + elabCommand (← mkSpecCommandPublic (toString contractName.getId) fields errorDecls constDecls immutableDecls externalDecls ctor functions adtDecls storageNamespace) let findIdxSimpCmds ← mkFindIdxFieldSimpCommandsPublic contractName fields for cmd in findIdxSimpCmds do @@ -57,6 +61,55 @@ def elabVerityContract : CommandElab := fun stx => do if fn.isView then elabCommand (← mkViewTheoremCommand fn) + -- Emit per-function _no_calls theorems for no_external_calls functions (#1729, Axis 3 Step 1c). + for fn in functions do + if fn.noExternalCalls then + elabCommand (← mkNoCallsTheoremCommand fn) + + -- Emit per-function _modifies theorem and _frame definition for + -- functions with modifies(...) annotation (#1729, Axis 3 Step 1b). + for fn in functions do + if !fn.modifies.isEmpty then + elabCommand (← mkModifiesTheoremCommand fn) + let frameCmds ← mkFrameDefCommand fields fn + for cmd in frameCmds do + elabCommand cmd + + -- Emit per-function _effects conjunction theorem when multiple effect + -- annotations are active on the same function (#1729, Axis 3 Step 1d). + for fn in functions do + if effectAnnotationCount fn ≥ 2 then + elabCommand (← mkEffectsTheoremCommand fn) + + -- Emit per-function _cei_compliant theorem for functions that use default + -- CEI enforcement (rung 1) — i.e. not opted out via any escalation rung. + -- (#1728, Axis 2 Step 2a) + for fn in functions do + if !fn.allowPostInteractionWrites && fn.nonReentrantLock.isNone && !fn.ceiSafe then + elabCommand (← mkCEICompliantTheoremCommand fn) + + -- Emit per-function _nonreentrant theorem for functions with nonreentrant(field). + -- (#1728, Axis 2 Step 2b — known-safe guard rung) + for fn in functions do + match fn.nonReentrantLock with + | some lockIdent => + elabCommand (← mkNonReentrantTheoremCommand fn (toString lockIdent.getId)) + | none => pure () + + -- Emit per-function _cei_safe theorem for functions with cei_safe annotation. + -- (#1728, Axis 2 Step 2b — Lean proof rung) + for fn in functions do + if fn.ceiSafe then + elabCommand (← mkCEISafeTheoremCommand fn) + + -- Emit per-function _requires_role theorem for functions with requires(field). + -- (#1728, Axis 2 Step 2c — access control) + for fn in functions do + match fn.requiresRole with + | some roleIdent => + elabCommand (← mkRequiresRoleTheoremCommand fn (toString roleIdent.getId)) + | none => pure () + elabCommand (← `(end $contractName)) catch err => elabCommand (← `(end $contractName)) diff --git a/Verity/Macro/Syntax.lean b/Verity/Macro/Syntax.lean index 5ec4516c1..a503320b4 100644 --- a/Verity/Macro/Syntax.lean +++ b/Verity/Macro/Syntax.lean @@ -16,6 +16,12 @@ declare_syntax_cat verityLocalObligations declare_syntax_cat verityConstructor declare_syntax_cat verityMutability declare_syntax_cat verityInitGuard +declare_syntax_cat verityModifies +declare_syntax_cat verityRequiresRole +declare_syntax_cat verityNewtype +declare_syntax_cat verityAdtVariant +declare_syntax_cat verityAdtDecl +declare_syntax_cat verityNamespaceSpec declare_syntax_cat veritySpecialEntrypoint declare_syntax_cat verityFunction @@ -33,22 +39,44 @@ syntax ident " := " ident ppSpace str : verityLocalObligation syntax "local_obligations " "[" sepBy(verityLocalObligation, ",") "]" : verityLocalObligations syntax "payable" : verityMutability syntax "view" : verityMutability +syntax "no_external_calls" : verityMutability +syntax "allow_post_interaction_writes" : verityMutability +syntax "nonreentrant(" ident ")" : verityMutability +syntax "cei_safe" : verityMutability +syntax "modifies(" sepBy1(ident, ",") ")" : verityModifies +syntax "requires(" ident ")" : verityRequiresRole +syntax ident " : " term:max : verityNewtype +syntax "| " ident "(" sepBy(verityParam, ",") ")" : verityAdtVariant +syntax "| " ident : verityAdtVariant +syntax ident " := " verityAdtVariant+ : verityAdtDecl +syntax "storage_namespace " : verityNamespaceSpec +syntax "storage_namespace " str : verityNamespaceSpec syntax "initializer(" ident ")" : verityInitGuard syntax "reinitializer(" ident ", " num ")" : verityInitGuard syntax "ecmCall " term:max ppSpace term:max : term syntax "ecmDo " term:max ppSpace term:max : term +syntax "adt " str : term +syntax "adt " str " [" sepBy(term, ",") "]" : term syntax "tryCatch " term:max ppSpace term:max : doElem + +macro_rules + | `(adt $_variant:str) => `(0) + | `(adt $_variant:str [ $[$_args:term],* ]) => `(0) syntax "revert " ident "(" sepBy(term, ",") ")" : doElem syntax "revertError " ident "(" sepBy(term, ",") ")" : doElem syntax "requireError " term:max ppSpace ident "(" sepBy(term, ",") ")" : doElem +syntax (priority := high) "unsafe " str " do " doSeq : doElem syntax "constructor " "(" sepBy(verityParam, ",") ")" (ppSpace verityLocalObligations)? " := " term : verityConstructor syntax "constructor " "(" sepBy(verityParam, ",") ")" " payable" (ppSpace verityLocalObligations)? " := " term : verityConstructor syntax "receive" (ppSpace verityLocalObligations)? " := " term : veritySpecialEntrypoint syntax "fallback" (ppSpace verityLocalObligations)? " := " term : veritySpecialEntrypoint -syntax "function " verityMutability* ident " (" sepBy(verityParam, ",") ")" (ppSpace verityInitGuard)? (ppSpace verityLocalObligations)? " : " term " := " term : verityFunction +syntax "function " verityMutability* ident " (" sepBy(verityParam, ",") ")" (ppSpace verityInitGuard)? (ppSpace verityRequiresRole)? (ppSpace verityModifies)? (ppSpace verityLocalObligations)? " : " term " := " term : verityFunction syntax (name := verityContractCmd) "verity_contract " ident " where " + ("types " verityNewtype+)? + ("inductive " verityAdtDecl+)? + (verityNamespaceSpec)? "storage " verityStorageField* ("errors " verityError+)? ("constants " verityConstant+)? diff --git a/Verity/Macro/Translate.lean b/Verity/Macro/Translate.lean index 7abb2b92f..cb2fdc83f 100644 --- a/Verity/Macro/Translate.lean +++ b/Verity/Macro/Translate.lean @@ -2,6 +2,7 @@ import Lean import Compiler.Modules.ERC20 import Compiler.Modules.Precompiles import Compiler.CompilationModel.InternalNaming +import Compiler.Keccak.Sponge import Verity.Macro.Syntax namespace Verity.Macro @@ -29,6 +30,8 @@ inductive ValueType where | array (elemTy : ValueType) | tuple (elemTys : List ValueType) | unit + | newtype (name : String) (baseType : ValueType) -- Semantic newtype; erased to baseType (#1727 Steps 3b/3c) + | adt (name : String) (maxFields : Nat) -- User-defined ADT (tagged union); maxFields = max variant field count (#1727 Step 5b) deriving Repr, BEq inductive MappingKeyType where @@ -58,6 +61,7 @@ structure StorageFieldDecl where name : String ty : StorageType slotNum : Nat + adtInfo? : Option (String × Nat) := none structure ParamDecl where ident : Ident @@ -87,6 +91,32 @@ structure ExternalDecl where params : Array ValueType returnTys : Array ValueType +/-- A user-defined semantic newtype declared in the `types` section. + At the language level the type is distinct from its base type; at the + EVM/Yul level it is erased to the base type (zero overhead). + (#1727, Axis 1 Step 3a) -/ +structure NewtypeDecl where + ident : Ident + name : String + baseType : ValueType + +/-- A single variant (constructor) of a user-defined algebraic data type. + E.g. `| Ok(value : Uint256)` or `| None`. + (#1727, Axis 1 Step 5a) -/ +structure AdtVariantDecl where + ident : Ident + name : String + fields : Array ParamDecl + +/-- A user-defined algebraic data type (tagged union) declared in the `inductive` section. + E.g. `Result := | Ok(value : Uint256) | Err(code : Uint256)`. + At the EVM level, ADTs use max-width tagged union encoding. + (#1727, Axis 1 Step 5a) -/ +structure AdtDecl where + ident : Ident + name : String + variants : Array AdtVariantDecl + structure LocalObligationDecl where ident : Ident name : String @@ -104,7 +134,31 @@ structure FunctionDecl where returnTy : ValueType isPayable : Bool := false isView : Bool := false + noExternalCalls : Bool := false + /-- When true, the function is annotated `allow_post_interaction_writes` and + CEI (Checks-Effects-Interactions) enforcement is bypassed. This is the + explicit trust-surface opt-out in the escalation ladder (#1728, Axis 2 Step 2a). -/ + allowPostInteractionWrites : Bool := false + /-- When `some fieldIdent`, the function is annotated `nonreentrant(field)`. + The named storage field is used as a reentrancy lock. CEI enforcement is + bypassed because the lock prevents reentrant state corruption. + (#1728, Axis 2 Step 2b — known-safe guard rung) -/ + nonReentrantLock : Option Ident := none + /-- When true, the function is annotated `cei_safe` — the user asserts CEI + safety via a machine-checked proof obligation. CEI enforcement is bypassed + and a proof obligation is generated. + (#1728, Axis 2 Step 2b — Lean proof rung) -/ + ceiSafe : Bool := false + /-- When `some fieldIdent`, the function is annotated `requires(field)`. + The named Address-typed storage field is an access-control role. + A `require(caller == roleHolder)` check is auto-injected at the start + of the function body. (#1728, Axis 2 Step 2c) -/ + requiresRole : Option Ident := none initGuard? : Option InitGuardDecl := none + /-- Storage field names declared via `modifies(field1, field2)`. + When non-empty, the compiler validates that the function body only + writes to fields in this set and auto-generates a `_frame` theorem. -/ + modifies : Array Ident := #[] localObligations : Array LocalObligationDecl := #[] body : Term @@ -152,7 +206,7 @@ private def natFromSyntax (stx : Syntax) : CommandElabM Nat := | some n => pure n | none => throwErrorAt stx "expected natural literal" -private partial def valueTypeFromSyntax (ty : Term) : CommandElabM ValueType := do +private partial def valueTypeFromSyntax (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl) (ty : Term) : CommandElabM ValueType := do match ty with | `(term| Uint256) => pure .uint256 | `(term| Int256) => pure .int256 @@ -163,20 +217,33 @@ private partial def valueTypeFromSyntax (ty : Term) : CommandElabM ValueType := | `(term| String) => pure .string | `(term| Bytes) => pure .bytes | `(term| Array $elemTy:term) => - let elem ← valueTypeFromSyntax elemTy + let elem ← valueTypeFromSyntax newtypes adtDecls elemTy match elem with | .unit => throwErrorAt ty "unsupported type '{ty}'; Array Unit is not allowed" | .array _ => throwErrorAt ty "unsupported type '{ty}'; nested arrays are not supported" | _ => pure (.array elem) | `(term| Tuple [ $[$elemTys:term],* ]) => - let elems ← elemTys.mapM valueTypeFromSyntax + let elems ← elemTys.mapM (valueTypeFromSyntax newtypes adtDecls) if elems.size < 2 then throwErrorAt ty "tuple types must have at least 2 elements" pure (.tuple elems.toList) | `(term| Unit) => pure .unit - | _ => throwErrorAt ty "unsupported type '{ty}'; expected Uint256, Int256, Uint8, Address, Bytes32, Bool, String, Bytes, Array , Tuple [...], or Unit" + | `(term| $id:ident) => + let tyName := toString id.getId + -- Try resolving as a user-defined newtype (#1727, Axis 1 Steps 3a/3b) + match newtypes.find? (fun nt => nt.name == tyName) with + | some nt => pure (.newtype nt.name nt.baseType) + | none => + -- Try resolving as a user-defined ADT (#1727, Axis 1 Step 5b) + match adtDecls.find? (fun a => a.name == tyName) with + | some decl => + let maxFields := decl.variants.foldl (fun acc v => max acc v.fields.size) 0 + pure (.adt decl.name maxFields) + | none => throwErrorAt ty "unsupported type '{ty}'; expected Uint256, Int256, Uint8, Address, Bytes32, Bool, String, Bytes, Array , Tuple [...], Unit, or a user-defined type from the `types` or `inductive` section" + | _ => + throwErrorAt ty "unsupported type '{ty}'; expected Uint256, Int256, Uint8, Address, Bytes32, Bool, String, Bytes, Array , Tuple [...], Unit, or a user-defined type from the `types` or `inductive` section" -private def storageTypeFromSyntax (ty : Term) : CommandElabM StorageType := do +private def storageTypeFromSyntax (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl := #[]) (ty : Term) : CommandElabM StorageType := do let keyTypeFromSyntax (stx : Term) : CommandElabM MappingKeyType := do match stx with | `(term| Address) => pure .address @@ -232,7 +299,7 @@ private def storageTypeFromSyntax (ty : Term) : CommandElabM StorageType := do (← keyTypeFromSyntax innerKey) ((← members.mapM structMemberFromSyntax).toList) | _ => do - let vt ← valueTypeFromSyntax ty + let vt ← valueTypeFromSyntax newtypes adtDecls ty match vt with | .array elemTy => pure (.dynamicArray (← storageArrayElemTypeFromValueType elemTy)) | .tuple _ => throwErrorAt ty "storage fields cannot be Tuple; use mapping encodings" @@ -280,6 +347,9 @@ private def modelFieldTypeTerm (ty : StorageType) : CommandElabM Term := | .scalar (.array _) => throwError "storage fields cannot be Array; use mapping encodings" | .scalar (.tuple _) => throwError "storage fields cannot be Tuple; use mapping encodings" | .scalar .unit => throwError "storage fields cannot be Unit" + | .scalar (.newtype _ baseType) => modelFieldTypeTerm (.scalar baseType) -- Erased to base type + | .scalar (.adt name maxFields) => + `(Compiler.CompilationModel.FieldType.adt $(Lean.quote name) $(Lean.quote maxFields)) | .dynamicArray .uint256 => `(Compiler.CompilationModel.FieldType.dynamicArray Compiler.CompilationModel.StorageArrayElemType.uint256) | .dynamicArray .address => `(Compiler.CompilationModel.FieldType.dynamicArray Compiler.CompilationModel.StorageArrayElemType.address) | .dynamicArray .bool => `(Compiler.CompilationModel.FieldType.dynamicArray Compiler.CompilationModel.StorageArrayElemType.bool) @@ -326,6 +396,11 @@ private partial def modelParamTypeTerm (ty : ValueType) : CommandElabM Term := let elemTerms ← elemTys.mapM modelParamTypeTerm `(Compiler.CompilationModel.ParamType.tuple [ $[$elemTerms.toArray],* ]) | .unit => throwError "function parameters cannot be Unit" + | .newtype name baseType => do + let baseTerm ← modelParamTypeTerm baseType + `(Compiler.CompilationModel.ParamType.newtypeOf $(Lean.quote name) $baseTerm) + | .adt name maxFields => do + `(Compiler.CompilationModel.ParamType.adt $(Lean.quote name) $(Lean.quote maxFields)) private def modelReturnTypeTerm (ty : ValueType) : CommandElabM Term := match ty with @@ -340,6 +415,8 @@ private def modelReturnTypeTerm (ty : ValueType) : CommandElabM Term := | .bytes => `(none) | .array _ => `(none) | .tuple _ => `(none) + | .newtype _ baseType => modelReturnTypeTerm baseType + | .adt _ _ => `(none) -- ADTs are not directly returnable as single FieldType private partial def modelReturnsTerm (ty : ValueType) : CommandElabM Term := match ty with @@ -357,6 +434,11 @@ private partial def modelReturnsTerm (ty : ValueType) : CommandElabM Term := | .tuple elemTys => do let elemTerms ← elemTys.mapM modelParamTypeTerm `([ $[$elemTerms.toArray],* ]) + | .newtype name baseType => do + let baseTerm ← modelParamTypeTerm baseType + `([Compiler.CompilationModel.ParamType.newtypeOf $(Lean.quote name) $baseTerm]) + | .adt name maxFields => do + `([Compiler.CompilationModel.ParamType.adt $(Lean.quote name) $(Lean.quote maxFields)]) mutual private partial def mkTupleContractType (elemTys : List ValueType) : CommandElabM Term := do @@ -381,75 +463,131 @@ private partial def contractValueTypeTerm (ty : ValueType) : CommandElabM Term : `(Array $(← contractValueTypeTerm elemTy)) | .tuple elemTys => mkTupleContractType elemTys | .unit => `(Unit) + | .newtype _ baseType => contractValueTypeTerm baseType -- Erased to base type at contract level + | .adt _ _ => `(Uint256) -- ADTs represented as tag value at contract level end -private def parseStorageField (stx : Syntax) : CommandElabM StorageFieldDecl := do +private def parseStorageField (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl := #[]) (stx : Syntax) : CommandElabM StorageFieldDecl := do match stx with | `(verityStorageField| $name:ident : $ty:term := slot $slotNum:num) => + let parsedTy ← storageTypeFromSyntax newtypes adtDecls ty + let adtInfo? := + match parsedTy with + | .scalar (.adt adtName maxFields) => some (adtName, maxFields) + | _ => none pure { ident := name name := toString name.getId - ty := ← storageTypeFromSyntax ty + ty := parsedTy slotNum := ← natFromSyntax slotNum + adtInfo? := adtInfo? } | _ => throwErrorAt stx "invalid storage field declaration" -private def parseParam (stx : Syntax) : CommandElabM ParamDecl := do +private def parseParam (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl) (stx : Syntax) : CommandElabM ParamDecl := do match stx with | `(verityParam| $name:ident : $ty:term) => pure { ident := name name := toString name.getId - ty := ← valueTypeFromSyntax ty + ty := ← valueTypeFromSyntax newtypes adtDecls ty } | _ => throwErrorAt stx "invalid parameter declaration" -private def parseError (stx : Syntax) : CommandElabM ErrorDecl := do +private def parseNewtype (stx : Syntax) : CommandElabM NewtypeDecl := do + match stx with + | `(verityNewtype| $name:ident : $ty:term) => + let baseType ← valueTypeFromSyntax #[] #[] ty + -- Validate: newtypes must be based on scalar types (not arrays, tuples, or unit) + match baseType with + | .array _ => throwErrorAt ty "newtype base type must be a scalar type, not an array" + | .tuple _ => throwErrorAt ty "newtype base type must be a scalar type, not a tuple" + | .unit => throwErrorAt ty "newtype base type must be a scalar type, not Unit" + | _ => pure () + pure { + ident := name + name := toString name.getId + baseType := baseType + } + | _ => throwErrorAt stx "invalid type declaration" + +/-- Parse a single ADT variant: `| Name(field1 : Type1, field2 : Type2)` or `| Name`. + (#1727, Axis 1 Step 5a) -/ +private def parseAdtVariant (newtypes : Array NewtypeDecl) (stx : Syntax) : CommandElabM AdtVariantDecl := do + match stx with + | `(verityAdtVariant| | $name:ident ($[$params:verityParam],*)) => + let parsedParams ← params.mapM (parseParam newtypes #[]) + pure { ident := name, name := toString name.getId, fields := parsedParams } + | `(verityAdtVariant| | $name:ident) => + pure { ident := name, name := toString name.getId, fields := #[] } + | _ => throwErrorAt stx "invalid ADT variant declaration" + +/-- Parse a full ADT declaration: `Name := | Variant1(...) | Variant2(...)`. + (#1727, Axis 1 Step 5a) -/ +private def parseAdtDecl (newtypes : Array NewtypeDecl) (stx : Syntax) : CommandElabM AdtDecl := do + match stx with + | `(verityAdtDecl| $name:ident := $[$variants:verityAdtVariant]*) => + let parsedVariants ← variants.mapM (parseAdtVariant newtypes) + if parsedVariants.isEmpty then + throwErrorAt name s!"ADT '{toString name.getId}' must have at least one variant" + if parsedVariants.size > 256 then + throwErrorAt name + s!"ADT '{toString name.getId}' has {parsedVariants.size} variants, but ADT tags are encoded as Uint8 and support at most 256 variants" + -- Validate: no duplicate variant names within this ADT + let mut seenVariantNames : Array String := #[] + for v in parsedVariants do + if seenVariantNames.contains v.name then + throwErrorAt v.ident s!"duplicate variant name '{v.name}' in ADT '{toString name.getId}'" + seenVariantNames := seenVariantNames.push v.name + pure { ident := name, name := toString name.getId, variants := parsedVariants } + | _ => throwErrorAt stx "invalid ADT declaration" + +private def parseError (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl) (stx : Syntax) : CommandElabM ErrorDecl := do match stx with | `(verityError| error $name:ident ($[$params:term],*)) => pure { ident := name name := toString name.getId - params := ← params.mapM valueTypeFromSyntax + params := ← params.mapM (valueTypeFromSyntax newtypes adtDecls) } | _ => throwErrorAt stx "invalid custom error declaration" -private def parseConstant (stx : Syntax) : CommandElabM ConstantDecl := do +private def parseConstant (newtypes : Array NewtypeDecl) (stx : Syntax) : CommandElabM ConstantDecl := do match stx with | `(verityConstant| $name:ident : $ty:term := $body:term) => pure { ident := name name := toString name.getId - ty := ← valueTypeFromSyntax ty + ty := ← valueTypeFromSyntax newtypes #[] ty body := body } | _ => throwErrorAt stx "invalid constant declaration" -private def parseImmutable (stx : Syntax) : CommandElabM ImmutableDecl := do +private def parseImmutable (newtypes : Array NewtypeDecl) (stx : Syntax) : CommandElabM ImmutableDecl := do match stx with | `(verityImmutable| $name:ident : $ty:term := $body:term) => pure { ident := name name := toString name.getId - ty := ← valueTypeFromSyntax ty + ty := ← valueTypeFromSyntax newtypes #[] ty body := body } | _ => throwErrorAt stx "invalid immutable declaration" -private def parseExternal (stx : Syntax) : CommandElabM ExternalDecl := do +private def parseExternal (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl) (stx : Syntax) : CommandElabM ExternalDecl := do match stx with | `(verityExternal| external $name:ident ($[$params:term],*) -> ($[$returnTys:term],*)) => pure { ident := name name := toString name.getId - params := ← params.mapM valueTypeFromSyntax - returnTys := ← returnTys.mapM valueTypeFromSyntax + params := ← params.mapM (valueTypeFromSyntax newtypes adtDecls) + returnTys := ← returnTys.mapM (valueTypeFromSyntax newtypes adtDecls) } | `(verityExternal| external $name:ident ($[$params:term],*)) => pure { ident := name name := toString name.getId - params := ← params.mapM valueTypeFromSyntax + params := ← params.mapM (valueTypeFromSyntax newtypes adtDecls) returnTys := #[] } | _ => throwErrorAt stx "invalid external declaration" @@ -476,23 +614,60 @@ private def parseLocalObligation (stx : Syntax) : CommandElabM LocalObligationDe } | _ => throwErrorAt stx "invalid local obligation declaration" +private structure ParsedMutability where + isPayable : Bool := false + isView : Bool := false + noExternalCalls : Bool := false + allowPostInteractionWrites : Bool := false + nonReentrantLock : Option Ident := none + ceiSafe : Bool := false + private def parseMutabilityModifiers (mods : Array (TSyntax `verityMutability)) - (stx : Syntax) : CommandElabM (Bool × Bool) := do - let mut isPayable := false - let mut isView := false + (stx : Syntax) : CommandElabM ParsedMutability := do + let mut result : ParsedMutability := {} for mod in mods do match mod with | `(verityMutability| payable) => - if isPayable then + if result.isPayable then throwErrorAt mod "duplicate 'payable' modifier" - isPayable := true + result := { result with isPayable := true } | `(verityMutability| view) => - if isView then + if result.isView then throwErrorAt mod "duplicate 'view' modifier" - isView := true + result := { result with isView := true } + | `(verityMutability| no_external_calls) => + if result.noExternalCalls then + throwErrorAt mod "duplicate 'no_external_calls' modifier" + result := { result with noExternalCalls := true } + | `(verityMutability| allow_post_interaction_writes) => + if result.allowPostInteractionWrites then + throwErrorAt mod "duplicate 'allow_post_interaction_writes' modifier" + result := { result with allowPostInteractionWrites := true } + | `(verityMutability| nonreentrant($field:ident)) => + if result.nonReentrantLock.isSome then + throwErrorAt mod "duplicate 'nonreentrant' modifier" + result := { result with nonReentrantLock := some field } + | `(verityMutability| cei_safe) => + if result.ceiSafe then + throwErrorAt mod "duplicate 'cei_safe' modifier" + result := { result with ceiSafe := true } | _ => throwErrorAt stx "invalid function mutability modifier" - pure (isPayable, isView) + pure result + +private def parseModifies (stx : TSyntax `verityModifies) : CommandElabM (Array Ident) := do + match stx with + | `(verityModifies| modifies($[$fields:ident],*)) => + let result := fields + -- Check for duplicates + let mut seen : Array String := #[] + for f in result do + let s := toString f.getId + if seen.contains s then + throwErrorAt f s!"duplicate field '{s}' in modifies annotation" + seen := seen.push s + pure result + | _ => throwErrorAt stx "invalid modifies annotation" private def parseInitGuard (stx : TSyntax `verityInitGuard) : CommandElabM InitGuardDecl := do match stx with @@ -546,16 +721,27 @@ private def parseSpecialEntrypoint (stx : Syntax) : CommandElabM FunctionDecl := } | _ => throwErrorAt stx "invalid special entrypoint declaration" -private def parseFunction (stx : Syntax) : CommandElabM FunctionDecl := do +private def parseFunction (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl := #[]) (stx : Syntax) : CommandElabM FunctionDecl := do match stx with - | `(verityFunction| function $[$mods:verityMutability]* $name:ident ($[$params:verityParam],*) $[$guard?:verityInitGuard]? $[$localObligations?:verityLocalObligations]? : $retTy:term := $body:term) => do - let (isPayable, isView) ← parseMutabilityModifiers mods stx - let parsedParams ← params.mapM parseParam - let parsedReturnTy ← valueTypeFromSyntax retTy + | `(verityFunction| function $[$mods:verityMutability]* $name:ident ($[$params:verityParam],*) $[$guard?:verityInitGuard]? $[$requiresRoleClause?:verityRequiresRole]? $[$modifiesClause?:verityModifies]? $[$localObligations?:verityLocalObligations]? : $retTy:term := $body:term) => do + let mut_ ← parseMutabilityModifiers mods stx + let parsedParams ← params.mapM (parseParam newtypes adtDecls) + let parsedReturnTy ← valueTypeFromSyntax newtypes adtDecls retTy let parsedGuard? ← match guard? with | some guard => pure (some (← parseInitGuard guard)) | none => pure none + let parsedRequiresRole ← + match requiresRoleClause? with + | some roleClause => + match roleClause with + | `(verityRequiresRole| requires($roleField:ident)) => pure (some roleField) + | _ => throwErrorAt roleClause "invalid requires annotation" + | none => pure none + let parsedModifies ← + match modifiesClause? with + | some modClause => parseModifies modClause + | none => pure #[] let parsedLocalObligations ← match localObligations? with | some obligations => parseLocalObligations obligations @@ -565,38 +751,44 @@ private def parseFunction (stx : Syntax) : CommandElabM FunctionDecl := do name := toString name.getId params := parsedParams returnTy := parsedReturnTy - isPayable := isPayable - isView := isView + isPayable := mut_.isPayable + isView := mut_.isView + noExternalCalls := mut_.noExternalCalls + allowPostInteractionWrites := mut_.allowPostInteractionWrites + nonReentrantLock := mut_.nonReentrantLock + ceiSafe := mut_.ceiSafe + requiresRole := parsedRequiresRole initGuard? := parsedGuard? + modifies := parsedModifies localObligations := parsedLocalObligations body := body } | _ => throwErrorAt stx "invalid function declaration" -private def parseConstructor (stx : Syntax) : CommandElabM ConstructorDecl := do +private def parseConstructor (newtypes : Array NewtypeDecl) (adtDecls : Array AdtDecl := #[]) (stx : Syntax) : CommandElabM ConstructorDecl := do match stx with | `(verityConstructor| constructor ($[$params:verityParam],*) payable local_obligations [ $[$obligations:verityLocalObligation],* ] := $body:term) => pure { - params := ← params.mapM parseParam + params := ← params.mapM (parseParam newtypes adtDecls) isPayable := true localObligations := ← obligations.mapM parseLocalObligation body := body } | `(verityConstructor| constructor ($[$params:verityParam],*) payable := $body:term) => pure { - params := ← params.mapM parseParam + params := ← params.mapM (parseParam newtypes adtDecls) isPayable := true body := body } | `(verityConstructor| constructor ($[$params:verityParam],*) local_obligations [ $[$obligations:verityLocalObligation],* ] := $body:term) => pure { - params := ← params.mapM parseParam + params := ← params.mapM (parseParam newtypes adtDecls) localObligations := ← obligations.mapM parseLocalObligation body := body } | `(verityConstructor| constructor ($[$params:verityParam],*) := $body:term) => pure { - params := ← params.mapM parseParam + params := ← params.mapM (parseParam newtypes adtDecls) body := body } | _ => throwErrorAt stx "invalid constructor declaration" @@ -604,8 +796,14 @@ private def parseConstructor (stx : Syntax) : CommandElabM ConstructorDecl := do private def immutableHiddenName (imm : ImmutableDecl) : String := s!"__immutable_{imm.name}" +private def storageFieldFootprintSize (field : StorageFieldDecl) : Nat := + match field.ty with + | .scalar (.adt _ maxFields) => maxFields + 1 + | _ => 1 + private def immutableSlotIndex (fields : Array StorageFieldDecl) (idx : Nat) : Nat := - let nextUserSlot := fields.foldl (fun maxSlot field => max maxSlot (field.slotNum + 1)) 0 + let nextUserSlot := fields.foldl (fun maxSlot field => + max maxSlot (field.slotNum + storageFieldFootprintSize field)) 0 nextUserSlot + idx private def immutableSlotIdent (imm : ImmutableDecl) : Ident := @@ -623,6 +821,7 @@ def immutableStorageFieldDecl | .address => .scalar .address | _ => .scalar imm.ty slotNum := immutableSlotIndex fields idx + adtInfo? := none } private def validateImmutableType (imm : ImmutableDecl) : CommandElabM Unit := @@ -643,8 +842,24 @@ private def validateImmutableBodyType liftTermElabM do discard <| Lean.Elab.Term.elabTerm wrappedBody none +private partial def containsAdtValueType : ValueType → Bool + | .adt _ _ => true + | .newtype _ baseType => containsAdtValueType baseType + | .array elemTy => containsAdtValueType elemTy + | .tuple elemTys => elemTys.any containsAdtValueType + | _ => false + +private def rejectExecutableBoundaryAdt + (stx : Syntax) + (context : String) + (ty : ValueType) : CommandElabM Unit := do + if containsAdtValueType ty then + throwErrorAt stx + s!"{context} uses an ADT at the executable contract boundary. ADT storage is supported, but ABI/function boundary ADT lowering is not yet implemented; pass scalar fields explicitly or keep the ADT in storage." + private def externalExecutableWordType? : ValueType → Bool | .uint256 | .int256 | .uint8 | .address | .bytes32 | .bool => true + | .newtype _ baseType => externalExecutableWordType? baseType | _ => false private def validateExternalExecutableType @@ -777,6 +992,62 @@ private def mkInitGuardedBody $[$elems:doElem]*) | _ => throwErrorAt fn.body "function body must be a do block" +/-- Resolve the storage field referenced by a `requires(role)` annotation. + The role must be an Address-typed scalar storage field. -/ +private def resolveRoleField + (fields : Array StorageFieldDecl) (roleIdent : Ident) (fnIdent : Ident) + : CommandElabM StorageFieldDecl := do + let roleName := toString roleIdent.getId + match fields.find? (fun f => f.name == roleName) with + | none => + throwErrorAt roleIdent s!"function '{toString fnIdent.getId}': requires references unknown storage field '{roleName}'; known fields: {(fields.map (·.name)).toList}" + | some field => + match field.ty with + | .scalar .address | .scalar (.newtype _ .address) => pure field + | _ => throwErrorAt roleIdent s!"function '{toString fnIdent.getId}': requires({roleName}) must reference an Address-typed storage field, but '{roleName}' has a different type" + +/-- Generate IR-level prelude statements for a `requires(role)` annotation. + Injects `Stmt.require (Expr.eq Expr.caller (Expr.storage roleField)) "Access denied: only role"`. + (#1728, Axis 2 Step 2c) -/ +private def roleGuardPreludeStmtTerms + (fields : Array StorageFieldDecl) + (fn : FunctionDecl) : CommandElabM (Array Term) := do + match fn.requiresRole with + | none => pure #[] + | some roleIdent => + let field ← resolveRoleField fields roleIdent fn.ident + let message := strTerm s!"Access denied: caller is not {field.name}" + pure #[ + ← `(Compiler.CompilationModel.Stmt.require + (Compiler.CompilationModel.Expr.eq + (Compiler.CompilationModel.Expr.caller) + (Compiler.CompilationModel.Expr.storageAddr $(strTerm field.name))) + $message) + ] + +/-- Transform the source-level do-block body to inject a role access control check + at the start. Injects `let __sender ← msgSender; let __roleHolder ← getStorageAddr field; + require (__sender == __roleHolder) "Access denied: caller is not role"`. + (#1728, Axis 2 Step 2c) -/ +private def mkRoleGuardedBody + (fields : Array StorageFieldDecl) + (fn : FunctionDecl) : CommandElabM Term := do + match fn.requiresRole with + | none => pure fn.body + | some roleIdent => + let field ← resolveRoleField fields roleIdent fn.ident + let senderVar := mkIdent (Name.mkSimple s!"__verity_role_sender_{field.name}") + let holderVar := mkIdent (Name.mkSimple s!"__verity_role_holder_{field.name}") + let message := strTerm s!"Access denied: caller is not {field.name}" + match fn.body with + | `(term| do $[$elems:doElem]*) => + `(do + let $senderVar ← msgSender + let $holderVar ← getStorageAddr $field.ident + require ($senderVar == $holderVar) $message + $[$elems:doElem]*) + | _ => throwErrorAt fn.body "function body must be a do block" + private def mkImmutableBoundBody (fields : Array StorageFieldDecl) (immutableDecls : Array ImmutableDecl) @@ -910,14 +1181,17 @@ private def typedLocalNames (locals : Array TypedLocal) : Array String := private def isSignedWordValueType : ValueType → Bool | .int256 => true + | .newtype _ baseType => isSignedWordValueType baseType | _ => false private def isWordLikeValueType : ValueType → Bool | .uint256 | .int256 | .uint8 | .address | .bytes32 => true + | .newtype _ baseType => isWordLikeValueType baseType | _ => false private def isSingleWordStaticValueType : ValueType → Bool | .bool => true + | .newtype _ baseType => isSingleWordStaticValueType baseType | ty => isWordLikeValueType ty private def classifyWordArithmeticResultType @@ -1116,6 +1390,8 @@ private def requireDeclaredValueType private partial def localBindingUsesDynamicData : ValueType → Bool | .string | .bytes | .array _ => true | .tuple elemTys => elemTys.any localBindingUsesDynamicData + | .newtype _ baseType => localBindingUsesDynamicData baseType + | .adt _ _ => false -- ADTs are stored as tag + fixed-width slots, not dynamic | .uint256 | .int256 | .uint8 | .address | .bytes32 | .bool | .unit => false private def requireSupportedLocalBindingType @@ -1128,6 +1404,7 @@ private def requireSupportedLocalBindingType private def customErrorRequiresDirectParamRef : ValueType → Bool | .uint256 | .int256 | .uint8 | .address | .bool | .bytes32 => false + | .newtype _ baseType => customErrorRequiresDirectParamRef baseType | _ => true private def directParamRefName? (stx : Term) : Option String := @@ -1386,7 +1663,8 @@ private partial def inferPureExprType | some ext => match ext.returnTys.toList with | [retTy] => pure retTy - | _ => pure .uint256 + | [] => throwErrorAt name s!"externalCall '{extName}' returns no values; use `let success ← tryExternalCall \"{extName}\" [...]` instead" + | _ => throwErrorAt name s!"externalCall '{extName}' returns {ext.returnTys.size} values; use `let (success, ...) ← tryExternalCall \"{extName}\" [...]` for multi-return" | none => pure .uint256 | `(term| structMember $field:term $key:term $member:term) => do let fieldName := ← expectStringOrIdent field @@ -1429,6 +1707,9 @@ private partial def inferBindSourceType let f ← lookupStorageField fields (toString field.getId) match f.ty with | .scalar .uint256 => pure .uint256 + | .scalar (.newtype ntName (.uint256)) => pure (.newtype ntName .uint256) + | .scalar (.adt name maxFields) => pure (.adt name maxFields) + | .scalar (.newtype _ (.address)) => throwErrorAt rhs s!"field '{f.name}' is Address-based newtype; use getStorageAddr" | .scalar .address => throwErrorAt rhs s!"field '{f.name}' is Address; use getStorageAddr" | .scalar .bool => throwErrorAt rhs s!"field '{f.name}' is Bool; encode as Uint256 and use getStorage" | .dynamicArray _ => throwErrorAt rhs s!"field '{f.name}' is a storage dynamic array; use getStorageArrayLength/getStorageArrayElement" @@ -1439,6 +1720,8 @@ private partial def inferBindSourceType let f ← lookupStorageField fields (toString field.getId) match f.ty with | .scalar .address => pure .address + | .scalar (.newtype ntName (.address)) => pure (.newtype ntName .address) + | .scalar (.newtype _ (.uint256)) => throwErrorAt rhs s!"field '{f.name}' is Uint256-based newtype; use getStorage" | .scalar .uint256 => throwErrorAt rhs s!"field '{f.name}' is Uint256; use getStorage" | .scalar .bool => throwErrorAt rhs s!"field '{f.name}' is Bool; use getStorage" | .dynamicArray _ => throwErrorAt rhs s!"field '{f.name}' is a storage dynamic array; use getStorageArrayLength/getStorageArrayElement" @@ -1563,6 +1846,22 @@ private partial def inferBindSourceType (← inferPureExprType fields constDecls immutableDecls externalDecls params locals x) | _ => throwErrorAt args "expected list literal [..]" pure .uint256 + | `(term| tryExternalCall $name:term $args:term) => do + let extName := ← expectStringOrIdent name + match stripParens args with + | `(term| [ $[$xs],* ]) => + for x in xs do + requireWordLikeType x s!"tryExternalCall '{extName}' argument" + (← inferPureExprType fields constDecls immutableDecls externalDecls params locals x) + | _ => throwErrorAt args "expected list literal [..]" + match externalDecls.find? (fun ext => ext.name == extName) with + | some ext => + if ext.returnTys.size > 0 then + throwErrorAt rhs s!"tryExternalCall '{extName}' returns {ext.returnTys.size} value(s); use tuple destructuring: `let (success, ...) ← tryExternalCall ...`" + -- Zero-return external: success flag only + pure .bool + | none => + throwErrorAt rhs s!"unknown external function '{extName}'" | `(term| requireSomeUint $optExpr:term $_msg:term) => match stripParens optExpr with | `(term| safeAdd $a:term $b:term) | `(term| safeSub $a:term $b:term) => do @@ -1625,6 +1924,23 @@ private partial def inferTupleSourceTypes? requireWordLikeType key1 "structMembers2 key" (← inferPureExprType fields constDecls immutableDecls externalDecls params locals key1) requireWordLikeType key2 "structMembers2 key" (← inferPureExprType fields constDecls immutableDecls externalDecls params locals key2) pure (some (Array.replicate memberNames.size .uint256)) + | `(term| tryExternalCall $name:term $args:term) => + let extName := ← expectStringOrIdent name + match stripParens args with + | `(term| [ $[$xs],* ]) => + for x in xs do + requireWordLikeType x s!"tryExternalCall '{extName}' argument" + (← inferPureExprType fields constDecls immutableDecls externalDecls params locals x) + | _ => throwErrorAt args "expected list literal [..]" + match externalDecls.find? (fun ext => ext.name == extName) with + | some ext => + -- tryExternalCall returns (success : Bool, result₁ : T₁, ..., resultₙ : Tₙ) + pure (some (#[.bool] ++ ext.returnTys)) + | none => + -- When called from translation path with empty externalDecls, return none + -- to let the tryExternalCallBindStmt? helper handle translation. + -- The validation path (with real externalDecls) catches actual errors. + pure none | other => match matchLocalFunctionApp? functions other with | some (fn, argTerms) => @@ -2195,6 +2511,65 @@ private def tupleInternalCallAssignStmt? | none => pure none +/-- Try to translate a tuple‐destructured `tryExternalCall "name" [args]` RHS into + a `Stmt.tryExternalCallBind` term. Returns `none` when the RHS is not a + `tryExternalCall` application. Returns the statement term and the inferred + types for each bound name (Bool for success flag, Uint256 for all result + vars — precise return types require external decl lookup which happens in + the validation pass). (#1727, Axis 1 Step 5f) -/ +private def tryExternalCallBindStmt? + (fields : Array StorageFieldDecl) + (constDecls : Array ConstantDecl) + (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) + (params : Array ParamDecl) + (locals : Array TypedLocal) + (rhs : Term) + (names : Array (Option String)) : CommandElabM (Option (Term × Array ValueType)) := do + let rhs := stripParens rhs + match rhs with + | `(term| tryExternalCall $name:term $args:term) => + let extName := ← expectStringOrIdent name + let argExprs ← match stripParens args with + | `(term| [ $[$xs],* ]) => + xs.mapM (translatePureExprWithTypes fields constDecls immutableDecls params locals) + | _ => throwErrorAt args "expected list literal [..]" + -- names[0] is the success flag, names[1..] are result vars + let initialUsedNames := (params.toList.map (fun p => p.name)) ++ (typedLocalNames locals).toList ++ (names.filterMap id).toList + let (_, targetNamesRev) := names.toList.zipIdx.foldl + (fun (acc : List String × List String) (name?, idx) => + let (usedNames, targetNamesRev) := acc + let targetName := match name? with + | some name => name + | none => freshDiscardName usedNames idx + (targetName :: usedNames, targetName :: targetNamesRev)) + (initialUsedNames, []) + let targetNames := targetNamesRev.reverse + let successVar := match targetNames.head? with + | some n => n + | none => "_try_success" + let resultVars := targetNames.drop 1 + let successVarTerm := strTerm successVar + let resultVarTerms := resultVars.toArray.map strTerm + let stmt ← `(Compiler.CompilationModel.Stmt.tryExternalCallBind + $successVarTerm + [ $[$resultVarTerms],* ] + $(strTerm extName) + [ $[$argExprs],* ]) + let resultTys ← + match externalDecls.find? (fun ext => ext.name == extName) with + | some ext => + if ext.returnTys.size != resultVars.length then + throwErrorAt rhs s!"tryExternalCall '{extName}' binds {resultVars.length} result value(s), but the external declaration returns {ext.returnTys.size}" + pure ext.returnTys + | none => + -- Validation reports the unknown external with full context; keep + -- translation moving with word-shaped placeholders. + pure (Array.replicate resultVars.length .uint256) + let tys := #[ValueType.bool] ++ resultTys + pure (some (stmt, tys)) + | _ => pure none + private def expectExprList (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) @@ -2220,9 +2595,11 @@ private def translateBindSource | `(term| getStorage $field:ident) => let f ← lookupStorageField fields (toString field.getId) match f.ty with - | .scalar .uint256 => `(Compiler.CompilationModel.Expr.storage $(strTerm f.name)) + | .scalar .uint256 | .scalar (.newtype _ .uint256) | .scalar (.adt _ _) => + `(Compiler.CompilationModel.Expr.storage $(strTerm f.name)) | .scalar .bool => throwErrorAt rhs s!"field '{f.name}' is Bool; encode as Uint256 and use getStorage" - | .scalar .address => throwErrorAt rhs s!"field '{f.name}' is Address; use getStorageAddr" + | .scalar .address | .scalar (.newtype _ .address) => + throwErrorAt rhs s!"field '{f.name}' is Address; use getStorageAddr" | .scalar .unit => throwErrorAt rhs "invalid field type" | .dynamicArray _ => throwErrorAt rhs s!"field '{f.name}' is a storage dynamic array; use getStorageArrayLength/getStorageArrayElement" | .mappingStruct _ _ | .mappingStruct2 _ _ _ => @@ -2231,8 +2608,10 @@ private def translateBindSource | `(term| getStorageAddr $field:ident) => let f ← lookupStorageField fields (toString field.getId) match f.ty with - | .scalar .address => `(Compiler.CompilationModel.Expr.storageAddr $(strTerm f.name)) - | .scalar .uint256 => throwErrorAt rhs s!"field '{f.name}' is Uint256; use getStorage" + | .scalar .address | .scalar (.newtype _ .address) => + `(Compiler.CompilationModel.Expr.storageAddr $(strTerm f.name)) + | .scalar .uint256 | .scalar (.newtype _ .uint256) => + throwErrorAt rhs s!"field '{f.name}' is Uint256; use getStorage" | .scalar .bool => throwErrorAt rhs s!"field '{f.name}' is Bool; use getStorage" | .scalar .unit => throwErrorAt rhs "invalid field type" | .dynamicArray _ => throwErrorAt rhs s!"field '{f.name}' is a storage dynamic array; use getStorageArrayLength/getStorageArrayElement" @@ -2591,6 +2970,9 @@ private partial def validateDoElemExprTypes validateTryCatchHandlerDoesNotUsePayload handler payloadName? catchElems let _ ← validateDoElemsExprTypes ownerName fields constDecls immutableDecls externalDecls errorDecls functions params locals catchElems pure locals + | `(doElem| unsafe $_reason:str do $body:doSeq) => + validateDoSeqExprTypes ownerName fields constDecls immutableDecls externalDecls errorDecls functions params locals body + pure locals | `(doElem| $stmt:term) => validateEffectStmtExprTypes fields constDecls immutableDecls externalDecls functions params locals stmt pure locals @@ -2616,7 +2998,15 @@ private partial def validateEffectStmtExprTypes | `(term| safeApprove $token:term $spender:term $amount:term) => for arg in [token, spender, amount] do requireWordLikeType arg "ERC-20 helper" (← inferPureExprType fields constDecls immutableDecls externalDecls params locals arg) - | `(term| setStorage $_field:ident $value:term) | `(term| setStorageAddr $_field:ident $value:term) + | `(term| setStorage $field:ident $value:term) => + let f ← lookupStorageField fields (toString field.getId) + match f.adtInfo?, f.ty with + | some _, _ => pure () + | none, .scalar (.adt _ _) => pure () + | _, _ => + let _ ← inferPureExprType fields constDecls immutableDecls externalDecls params locals value + pure () + | `(term| setStorageAddr $_field:ident $value:term) | `(term| require $value:term $_msg) => let _ ← inferPureExprType fields constDecls immutableDecls externalDecls params locals value pure () @@ -2682,7 +3072,8 @@ private partial def validateEffectStmtExprTypes requireSupportedReturnStorageWordsType name "returnStorageWords" ty | `(term| internalCall $_fnName:term $args:term) | `(term| internalCallAssign $_names:term $_fnName:term $args:term) - | `(term| externalCallBind $_names:term $_fnName:term $args:term) => + | `(term| externalCallBind $_names:term $_fnName:term $args:term) + | `(term| tryExternalCallBind $_successVar:term $_names:term $_fnName:term $args:term) => match stripParens args with | `(term| [ $[$xs],* ]) => for x in xs do @@ -2785,6 +3176,66 @@ private def translateERC20BindStmt? [$tokenExpr])) | _ => pure none +private def adtConstructorApp? (stx : Term) : Option (Ident × Array Term) := + let stx := stripParens stx + match stx with + | `(term| $ctor:ident) => some (ctor, #[]) + | `(term| $ctor:ident $args:term*) => some (ctor, args) + | _ => none + +private def adtConstructorSyntax? (stx : Term) : Option (String × Array Term) := + let stx := stripParens stx + match stx with + | `(term| $variant:str) => some (variant.getString, #[]) + | `(term| ($variant:str, [ $[$args:term],* ])) => some (variant.getString, args) + | `(term| adt $variant:str) => some (variant.getString, #[]) + | `(term| adt $variant:str [ $[$args:term],* ]) => some (variant.getString, args) + | _ => + match adtConstructorApp? stx with + | some (variant, args) => + if toString variant.getId == "adt" then + match args with + | #[arg] => + match stripParens arg with + | `(term| $variant:str) => some (variant.getString, #[]) + | _ => none + | #[arg, argList] => + match stripParens arg, stripParens argList with + | `(term| $variant:str), `(term| [ $[$payloadArgs:term],* ]) => + some (variant.getString, payloadArgs) + | _, _ => none + | _ => none + else + some (toString variant.getId, args) + | none => none + +private def translateAdtConstructForStorage + (fields : Array StorageFieldDecl) + (constDecls : Array ConstantDecl) + (immutableDecls : Array ImmutableDecl) + (params : Array ParamDecl) + (locals : Array TypedLocal) + (adtName : String) + (value : Term) : CommandElabM Term := do + match adtConstructorSyntax? value with + | some (variantName, args) => + let argExprs ← args.mapM (translatePureExprWithTypes fields constDecls immutableDecls params locals) + `(Compiler.CompilationModel.Expr.adtConstruct + $(strTerm adtName) + $(strTerm variantName) + [ $[$argExprs],* ]) + | none => + throwErrorAt value + s!"ADT storage assignment for '{adtName}' must use a variant constructor so payload slots are preserved" + +private def storageFieldAdtName? (field : StorageFieldDecl) : Option String := + match field.adtInfo? with + | some (adtName, _) => some adtName + | none => + match field.ty with + | .scalar (.adt adtName _) => some adtName + | _ => none + private def translateEffectStmt (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) @@ -2838,23 +3289,33 @@ private def translateEffectStmt `(Compiler.CompilationModel.Stmt.ecm $module [ $[$argExprs],* ]) - | `(term| setStorage $field:ident $value) => + | `(term| setStorage $field:ident $value:term) => let f ← lookupStorageField fields (toString field.getId) - match f.ty with - | .scalar .uint256 => - `(Compiler.CompilationModel.Stmt.setStorage $(strTerm f.name) $(← translatePureExprWithTypes fields constDecls immutableDecls params locals value)) - | .scalar .address => - throwErrorAt stx s!"field '{f.name}' is Address-valued; use setStorageAddr" - | .dynamicArray _ => - throwErrorAt stx s!"field '{f.name}' is a storage dynamic array; use pushStorageArray/popStorageArray/setStorageArrayElement" - | _ => - throwErrorAt stx s!"field '{f.name}' is not Uint256; use setStorageAddr" + match storageFieldAdtName? f with + | some adtName => + `(Compiler.CompilationModel.Stmt.setStorage + $(strTerm f.name) + $(← translateAdtConstructForStorage fields constDecls immutableDecls params locals adtName value)) + | none => + match f.ty with + | .scalar .uint256 | .scalar (.newtype _ .uint256) => + `(Compiler.CompilationModel.Stmt.setStorage $(strTerm f.name) $(← translatePureExprWithTypes fields constDecls immutableDecls params locals value)) + | .scalar (.adt adtName _) => + `(Compiler.CompilationModel.Stmt.setStorage + $(strTerm f.name) + $(← translateAdtConstructForStorage fields constDecls immutableDecls params locals adtName value)) + | .scalar .address | .scalar (.newtype _ .address) => + throwErrorAt stx s!"field '{f.name}' is Address-valued; use setStorageAddr" + | .dynamicArray _ => + throwErrorAt stx s!"field '{f.name}' is a storage dynamic array; use pushStorageArray/popStorageArray/setStorageArrayElement" + | _ => + throwErrorAt stx s!"field '{f.name}' is not Uint256; use setStorageAddr" | `(term| setStorageAddr $field:ident $value) => let f ← lookupStorageField fields (toString field.getId) match f.ty with - | .scalar .address => + | .scalar .address | .scalar (.newtype _ .address) => `(Compiler.CompilationModel.Stmt.setStorageAddr $(strTerm f.name) $(← translatePureExprWithTypes fields constDecls immutableDecls params locals value)) - | .scalar .uint256 => + | .scalar .uint256 | .scalar (.newtype _ .uint256) => throwErrorAt stx s!"field '{f.name}' is Uint256-valued; use setStorage" | .dynamicArray _ => throwErrorAt stx s!"field '{f.name}' is a storage dynamic array; use pushStorageArray/popStorageArray/setStorageArrayElement" @@ -3117,6 +3578,7 @@ private partial def translateDoElems (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) (functions : Array FunctionDecl) (params : Array ParamDecl) (locals : Array TypedLocal) @@ -3127,7 +3589,7 @@ private partial def translateDoElems let mut stmts : Array Term := #[] for elem in elems do let (newStmts, newLocals, newMutableLocals) ← - translateDoElem fields constDecls immutableDecls functions params branchLocals branchMutableLocals elem + translateDoElem fields constDecls immutableDecls externalDecls functions params branchLocals branchMutableLocals elem stmts := stmts ++ newStmts branchLocals := newLocals branchMutableLocals := newMutableLocals @@ -3137,6 +3599,7 @@ private partial def translateDoSeqToStmtTerms (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) (functions : Array FunctionDecl) (params : Array ParamDecl) (locals : Array TypedLocal) @@ -3144,13 +3607,14 @@ private partial def translateDoSeqToStmtTerms (doSeq : DoSeq) : CommandElabM (Array Term) := do match doSeq with | `(doSeq| $[$elems:doElem]*) => - pure (← (translateDoElems fields constDecls immutableDecls functions params locals mutableLocals elems)).1 + pure (← (translateDoElems fields constDecls immutableDecls externalDecls functions params locals mutableLocals elems)).1 | _ => throwErrorAt doSeq "unsupported branch body; expected do-sequence" private partial def translateDoElem (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) (functions : Array FunctionDecl) (params : Array ParamDecl) (locals : Array TypedLocal) @@ -3177,7 +3641,7 @@ private partial def translateDoElem name?.map (fun name => (name, valueExpr)) let stmts ← boundPairs.mapM fun (name, valueExpr) => `(Compiler.CompilationModel.Stmt.letVar $(strTerm name) $valueExpr) - let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls #[] functions params locals rhs + let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls externalDecls functions params locals rhs match valueTys with | some tys => let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) @@ -3186,13 +3650,18 @@ private partial def translateDoElem | none => match (← tupleInternalCallAssignStmt? fields constDecls immutableDecls functions params locals rhs names) with | some stmt => - let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls #[] functions params locals rhs + let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls externalDecls functions params locals rhs match valueTys with | some tys => let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) pure (some (#[(stmt)], locals ++ typedPairs, mutableLocals)) | none => throwErrorAt rhs "unable to infer tuple local types" - | none => throwErrorAt rhs "tuple destructuring currently requires a tuple literal, tuple-typed parameter, structMembers/structMembers2 source, or internal helper call" + | none => + match (← tryExternalCallBindStmt? fields constDecls immutableDecls externalDecls params locals rhs names) with + | some (stmt, tys) => + let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) + pure (some (#[(stmt)], locals ++ typedPairs, mutableLocals)) + | none => throwErrorAt rhs "tuple destructuring currently requires a tuple literal, tuple-typed parameter, structMembers/structMembers2 source, internal helper call, or tryExternalCall" | _ => match (← tupleLiteralOrStructValueExprs? fields constDecls immutableDecls params locals rhs) with | some valueExprs => @@ -3202,7 +3671,7 @@ private partial def translateDoElem name?.map (fun name => (name, valueExpr)) let stmts ← boundPairs.mapM fun (name, valueExpr) => `(Compiler.CompilationModel.Stmt.letVar $(strTerm name) $valueExpr) - let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls #[] functions params locals rhs + let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls externalDecls functions params locals rhs match valueTys with | some tys => let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) @@ -3211,13 +3680,18 @@ private partial def translateDoElem | none => match (← tupleInternalCallAssignStmt? fields constDecls immutableDecls functions params locals rhs names) with | some stmt => - let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls #[] functions params locals rhs + let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls externalDecls functions params locals rhs match valueTys with | some tys => let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) pure (some (#[(stmt)], locals ++ typedPairs, mutableLocals)) | none => throwErrorAt rhs "unable to infer tuple local types" | none => + match (← tryExternalCallBindStmt? fields constDecls immutableDecls externalDecls params locals rhs names) with + | some (stmt, tys) => + let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) + pure (some (#[(stmt)], locals ++ typedPairs, mutableLocals)) + | none => let valueExprs ← tupleValueExprs fields constDecls immutableDecls params locals rhs if names.size != valueExprs.size then throwErrorAt patDecl s!"tuple destructuring binds {names.size} names, but the source provides {valueExprs.size} values" @@ -3225,7 +3699,7 @@ private partial def translateDoElem name?.map (fun name => (name, valueExpr)) let stmts ← boundPairs.mapM fun (name, valueExpr) => `(Compiler.CompilationModel.Stmt.letVar $(strTerm name) $valueExpr) - let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls #[] functions params locals rhs + let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls externalDecls functions params locals rhs match valueTys with | some tys => let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) @@ -3240,13 +3714,18 @@ private partial def translateDoElem let rhs : Term := ⟨patDecl[2][0]⟩ match (← tupleInternalCallAssignStmt? fields constDecls immutableDecls functions params locals rhs names) with | some stmt => - let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls #[] functions params locals rhs + let valueTys ← inferTupleSourceTypes? fields constDecls immutableDecls externalDecls functions params locals rhs match valueTys with | some tys => let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) pure (some (#[(stmt)], locals ++ typedPairs, mutableLocals)) | none => throwErrorAt rhs "unable to infer tuple local types" - | none => throwErrorAt rhs "tuple bind sources must be internal helper calls" + | none => + match (← tryExternalCallBindStmt? fields constDecls immutableDecls externalDecls params locals rhs names) with + | some (stmt, tys) => + let typedPairs := (names.zip tys).filterMap fun (name?, ty) => name?.map (fun name => (name, ty)) + pure (some (#[(stmt)], locals ++ typedPairs, mutableLocals)) + | none => throwErrorAt rhs "tuple bind sources must be internal helper calls or tryExternalCall" | none => pure none else pure none @@ -3258,7 +3737,7 @@ private partial def translateDoElem if localNames.contains varName then throwErrorAt name s!"duplicate local variable '{varName}'" let rhsExpr ← translatePureExprWithTypes fields constDecls immutableDecls params locals rhs - let ty ← inferPureExprType fields constDecls immutableDecls #[] params locals rhs + let ty ← inferPureExprType fields constDecls immutableDecls externalDecls params locals rhs pure (#[(← `(Compiler.CompilationModel.Stmt.letVar $(strTerm varName) $rhsExpr))], locals.push (varName, ty), @@ -3268,7 +3747,7 @@ private partial def translateDoElem if localNames.contains varName then throwErrorAt name s!"duplicate local variable '{varName}'" let rhsExpr ← translatePureExprWithTypes fields constDecls immutableDecls params locals rhs - let ty ← inferPureExprType fields constDecls immutableDecls #[] params locals rhs + let ty ← inferPureExprType fields constDecls immutableDecls externalDecls params locals rhs pure (#[(← `(Compiler.CompilationModel.Stmt.letVar $(strTerm varName) $rhsExpr))], locals.push (varName, ty), @@ -3299,6 +3778,19 @@ private partial def translateDoElem [$hashExpr, $vExpr, $rExpr, $sExpr]))], locals.push (varName, .address), mutableLocals) + | `(term| tryExternalCall $extName:term $args:term) => + -- Zero-return tryExternalCall: `let success ← tryExternalCall "fn" [args]` + -- produces Stmt.tryExternalCallBind successVar [] externalName args + let targetFn := ← expectStringOrIdent extName + let argExprs ← expectExprList fields constDecls immutableDecls params locals args + pure + (#[(← `(Compiler.CompilationModel.Stmt.tryExternalCallBind + $(strTerm varName) + [] + $(strTerm targetFn) + [ $[$argExprs],* ]))], + locals.push (varName, .bool), + mutableLocals) | _ => let safeBind? ← translateSafeRequireBind fields constDecls immutableDecls params locals varName rhs match safeBind? with @@ -3309,7 +3801,7 @@ private partial def translateDoElem pure (#[(stmt)], locals.push (varName, .uint256), mutableLocals) | none => let rhsExpr ← translateBindSource fields constDecls immutableDecls functions params locals rhs - let ty ← inferBindSourceType fields constDecls immutableDecls #[] functions params locals rhs + let ty ← inferBindSourceType fields constDecls immutableDecls externalDecls functions params locals rhs pure (#[(← `(Compiler.CompilationModel.Stmt.letVar $(strTerm varName) $rhsExpr))], locals.push (varName, ty), @@ -3335,8 +3827,8 @@ private partial def translateDoElem pure (#[], locals, mutableLocals) | `(doElem| if $cond:term then $thenBranch:doSeq else $elseBranch:doSeq) => let condExpr ← translatePureExprWithTypes fields constDecls immutableDecls params locals cond - let thenStmts ← translateDoSeqToStmtTerms fields constDecls immutableDecls functions params locals mutableLocals thenBranch - let elseStmts ← translateDoSeqToStmtTerms fields constDecls immutableDecls functions params locals mutableLocals elseBranch + let thenStmts ← translateDoSeqToStmtTerms fields constDecls immutableDecls externalDecls functions params locals mutableLocals thenBranch + let elseStmts ← translateDoSeqToStmtTerms fields constDecls immutableDecls externalDecls functions params locals mutableLocals elseBranch pure (#[(← `(Compiler.CompilationModel.Stmt.ite $condExpr @@ -3351,7 +3843,7 @@ private partial def translateDoElem validateTryCatchHandlerDoesNotUsePayload handler payloadName? catchElems let attemptExpr ← translatePureExprWithTypes fields constDecls immutableDecls params locals attempt let catchTranslation ← - translateDoElems fields constDecls immutableDecls functions params locals mutableLocals catchElems + translateDoElems fields constDecls immutableDecls externalDecls functions params locals mutableLocals catchElems let catchStmts := catchTranslation.1 pure (#[ @@ -3371,7 +3863,7 @@ private partial def translateDoElem let bodyStmts ← match stripParens body with | `(term| do $[$inner:doElem]*) => - pure (← (translateDoElems fields constDecls immutableDecls functions params (locals.push (loopVar, .uint256)) mutableLocals inner)).1 + pure (← (translateDoElems fields constDecls immutableDecls externalDecls functions params (locals.push (loopVar, .uint256)) mutableLocals inner)).1 | _ => throwErrorAt body "forEach body must be a do block" pure (#[(← `(Compiler.CompilationModel.Stmt.forEach @@ -3405,6 +3897,15 @@ private partial def translateDoElem [ $[$argExprs],* ]))], locals, mutableLocals) + | `(doElem| unsafe $reason:str do $body:doSeq) => + let bodyStmts ← translateDoSeqToStmtTerms fields constDecls immutableDecls externalDecls functions params locals mutableLocals body + let reasonStr := reason.getString + pure + (#[(← `(Compiler.CompilationModel.Stmt.unsafeBlock + $(Lean.Quote.quote reasonStr) + [ $[$bodyStmts],* ]))], + locals, + mutableLocals) | `(doElem| $stmt:term) => pure (#[(← translateEffectStmt fields constDecls immutableDecls functions params locals stmt)], locals, mutableLocals) | _ => throwErrorAt elem "unsupported do element" @@ -3414,12 +3915,14 @@ private def translateBodyToStmtTerms (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) (functions : Array FunctionDecl) (fn : FunctionDecl) : CommandElabM (Array Term) := do match fn.body with | `(term| do $[$elems:doElem]*) => let guardPrelude ← initGuardPreludeStmtTerms fields fn - let stmts := guardPrelude ++ (← translateDoElems fields constDecls immutableDecls functions fn.params #[] #[] elems).1 + let rolePrelude ← roleGuardPreludeStmtTerms fields fn + let stmts := guardPrelude ++ rolePrelude ++ (← translateDoElems fields constDecls immutableDecls externalDecls functions fn.params #[] #[] elems).1 let mut stmts := stmts if fn.returnTy == .unit then stmts := stmts.push (← `(Compiler.CompilationModel.Stmt.stop)) @@ -3430,11 +3933,12 @@ private def translateConstructorBodyToStmtTerms (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) (functions : Array FunctionDecl) (ctor : ConstructorDecl) : CommandElabM (Array Term) := do match ctor.body with | `(term| do $[$elems:doElem]*) => - pure (← (translateDoElems fields constDecls immutableDecls functions ctor.params #[] #[] elems)).1 + pure (← (translateDoElems fields constDecls immutableDecls externalDecls functions ctor.params #[] #[] elems)).1 | _ => throwErrorAt ctor.body "constructor body must be a do block" private def immutableInitStmtTerms @@ -3526,6 +4030,13 @@ private def mkStorageDefCommand (field : StorageFieldDecl) : CommandElabM Cmd := | .scalar (.array _) => throwError "storage field cannot be Array; use mapping encodings" | .scalar (.tuple _) => throwError "storage field cannot be Tuple; use mapping encodings" | .scalar .unit => throwError "storage field cannot be Unit" + | .scalar (.newtype _ baseType) => + -- Newtypes erased to base type for storage (#1727 Step 3b) + match baseType with + | .uint256 => `(Uint256) + | .address => `(Address) + | _ => throwError "storage field with newtype base type not supported; use Uint256 or Address" + | .scalar (.adt _ _) => `(Uint256) -- ADTs stored as tag value in storage (#1727 Step 5b) | .dynamicArray .uint256 => `(List Uint256) | .dynamicArray .address => `(List Address) | .dynamicArray .bool => `(List Bool) @@ -3585,6 +4096,23 @@ private def mkModelLocalObligationTerm (obligation : LocalObligationDecl) : Comm $(strTerm obligation.obligation) $proofStatusTerm) +private def mkAdtVariantTerm (variant : AdtVariantDecl) (tag : Nat) : CommandElabM Term := do + let fieldTerms ← variant.fields.mapM fun p => do + let tyTerm ← modelParamTypeTerm p.ty + `(Compiler.CompilationModel.Param.mk $(strTerm p.name) $tyTerm) + `(Compiler.CompilationModel.AdtVariant.mk + $(strTerm variant.name) + $(natTerm tag) + [ $[$fieldTerms],* ]) + +private def mkAdtTypeDefTerm (adtDecl : AdtDecl) : CommandElabM Term := do + let mut variantTerms : Array Term := #[] + for (variant, idx) in adtDecl.variants.zipIdx do + variantTerms := variantTerms.push (← mkAdtVariantTerm variant idx) + `(Compiler.CompilationModel.AdtTypeDef.mk + $(strTerm adtDecl.name) + [ $[$variantTerms],* ]) + private def mkSpecCommand (contractName : String) (fields : Array StorageFieldDecl) @@ -3593,7 +4121,9 @@ private def mkSpecCommand (immutableDecls : Array ImmutableDecl) (externalDecls : Array ExternalDecl) (ctor : Option ConstructorDecl) - (functions : Array FunctionDecl) : CommandElabM Cmd := do + (functions : Array FunctionDecl) + (adtDecls : Array AdtDecl) + (storageNamespace : Option Nat) : CommandElabM Cmd := do let immutableFields := immutableDecls.zipIdx.map (fun (imm, idx) => immutableStorageFieldDecl fields imm idx) let allFields := fields ++ immutableFields let fieldTerms ← allFields.mapM mkModelFieldTerm @@ -3607,7 +4137,7 @@ private def mkSpecCommand let ctorPayable ← if ctor.isPayable then `(true) else `(false) let ctorLocalObligationTerms ← ctor.localObligations.mapM mkModelLocalObligationTerm let immutableInitTerms ← immutableInitStmtTerms fields constDecls immutableDecls ctor.params - let ctorBodyTerms ← translateConstructorBodyToStmtTerms fields constDecls immutableDecls functions ctor + let ctorBodyTerms ← translateConstructorBodyToStmtTerms fields constDecls immutableDecls externalDecls functions ctor let ctorAllTerms := immutableInitTerms ++ ctorBodyTerms `(some { params := $ctorParams @@ -3631,6 +4161,16 @@ private def mkSpecCommand let localObligationTerms ← fn.localObligations.mapM mkModelLocalObligationTerm let payableTerm ← if fn.isPayable then `(true) else `(false) let viewTerm ← if fn.isView then `(true) else `(false) + let noExternalCallsTerm ← if fn.noExternalCalls then `(true) else `(false) + let allowPostInteractionWritesTerm ← if fn.allowPostInteractionWrites then `(true) else `(false) + let nonReentrantLockTerm ← match fn.nonReentrantLock with + | some lockIdent => `(some $(strTerm (toString lockIdent.getId))) + | none => `(none) + let ceiSafeTerm ← if fn.ceiSafe then `(true) else `(false) + let requiresRoleTerm ← match fn.requiresRole with + | some roleIdent => `(some $(strTerm (toString roleIdent.getId))) + | none => `(none) + let internalModifiesTerms : Array Term := fn.modifies.map fun ident => strTerm (toString ident.getId) let returnTypeTerm ← modelReturnTypeTerm fn.returnTy let returnsTerm ← modelReturnsTerm fn.returnTy pure <| some (← `( ({ @@ -3640,12 +4180,22 @@ private def mkSpecCommand «returns» := $returnsTerm isPayable := $payableTerm isView := $viewTerm + noExternalCalls := $noExternalCallsTerm + allowPostInteractionWrites := $allowPostInteractionWritesTerm + nonReentrantLock := $nonReentrantLockTerm + ceiSafe := $ceiSafeTerm + requiresRole := $requiresRoleTerm + modifies := [ $[$internalModifiesTerms],* ] localObligations := [ $[$localObligationTerms],* ] body := $modelBodyName isInternal := true } : Compiler.CompilationModel.FunctionSpec) )) else pure none + let adtTypeTerms ← adtDecls.mapM mkAdtTypeDefTerm + let namespaceTerm ← match storageNamespace with + | some ns => `(some $(natTerm ns)) + | none => `(none) `(command| def spec : Compiler.CompilationModel.CompilationModel := { name := $(strTerm contractName) fields := [ $[$fieldTerms],* ] @@ -3653,6 +4203,8 @@ private def mkSpecCommand «constructor» := $constructorTerm functions := [ $[$functionModelIds],*, $[$internalFunctionTerms],* ] «externals» := [ $[$externalTerms],* ] + adtTypes := [ $[$adtTypeTerms],* ] + storageNamespace := $namespaceTerm }) private def mkFindIdxFieldSimpCommands @@ -3733,37 +4285,114 @@ private def mkFindIdxParamSimpCommands cmds := cmds ++ fnCmds pure cmds +/-- Convert a big-endian `ByteArray` to a `Nat`, treating byte 0 as most + significant. Used for storage namespace computation (#1730, Axis 4). -/ +private def byteArrayToNatBE (ba : ByteArray) : Nat := + ba.foldl (fun acc byte => acc * 256 + byte.toNat) 0 + +/-- Compute the storage namespace for a contract. + `storageNamespace("Foo") = keccak256("Foo.storage.v0")` as a 256-bit Nat. + The result can be used as a base offset so different contracts never collide + in the shared 2^256 storage address space. + (#1730, Axis 4 Step 4a) -/ +def computeStorageNamespace (contractName : String) : Nat := + byteArrayToNatBE (KeccakEngine.keccak256_str s!"{contractName}.storage.v0") + +/-- Compute a storage namespace from an explicit user-provided namespace key. -/ +def computeStorageNamespaceKey (key : String) : Nat := + byteArrayToNatBE (KeccakEngine.keccak256_str key) + def parseContractSyntax (stx : Syntax) : CommandElabM - (Ident × Array StorageFieldDecl × Array ErrorDecl × Array ConstantDecl × Array ImmutableDecl × Array ExternalDecl × Option ConstructorDecl × Array FunctionDecl) := do + (Ident × Array NewtypeDecl × Array AdtDecl × Array StorageFieldDecl × Array ErrorDecl × Array ConstantDecl × Array ImmutableDecl × Array ExternalDecl × Option ConstructorDecl × Array FunctionDecl × Option Nat) := do match stx with - | `(command| verity_contract $contractName:ident where storage $[$storageFields:verityStorageField]* $[errors $[$errorDecls:verityError]*]? $[constants $[$constantDecls:verityConstant]*]? $[immutables $[$immutableDecls:verityImmutable]*]? $[linked_externals $[$externalDecls:verityExternal]*]? $[$ctor:verityConstructor]? $[$entrypoints:veritySpecialEntrypoint]* $[$functions:verityFunction]*) => + | `(command| verity_contract $contractName:ident where $[types $[$newtypeDecls:verityNewtype]*]? $[inductive $[$adtDecls:verityAdtDecl]*]? $[$nsSpec:verityNamespaceSpec]? storage $[$storageFields:verityStorageField]* $[errors $[$errorDecls:verityError]*]? $[constants $[$constantDecls:verityConstant]*]? $[immutables $[$immutableDecls:verityImmutable]*]? $[linked_externals $[$externalDecls:verityExternal]*]? $[$ctor:verityConstructor]? $[$entrypoints:veritySpecialEntrypoint]* $[$functions:verityFunction]*) => + -- Parse newtypes first — they are needed by all downstream type resolution + let parsedNewtypes ← + match newtypeDecls with + | some decls => decls.mapM parseNewtype + | none => pure #[] + -- Validate: no duplicate type names + let mut seenNames : Array String := #[] + for nt in parsedNewtypes do + if seenNames.contains nt.name then + throwErrorAt nt.ident s!"duplicate type name '{nt.name}'" + seenNames := seenNames.push nt.name + -- Validate: type names don't shadow built-in types + let builtinTypeNames := #["Uint256", "Int256", "Uint8", "Address", "Bytes32", "Bool", "String", "Bytes", "Unit", "Array", "Tuple"] + for nt in parsedNewtypes do + if builtinTypeNames.contains nt.name then + throwErrorAt nt.ident s!"type name '{nt.name}' shadows a built-in type" + -- Parse ADT declarations (#1727, Axis 1 Step 5a) + let parsedAdts ← + match adtDecls with + | some decls => decls.mapM (parseAdtDecl parsedNewtypes) + | none => pure #[] + -- Validate: no duplicate ADT names + for adtDecl in parsedAdts do + if seenNames.contains adtDecl.name then + throwErrorAt adtDecl.ident s!"duplicate type name '{adtDecl.name}'" + seenNames := seenNames.push adtDecl.name + -- Validate: ADT names don't shadow built-in types + for adtDecl in parsedAdts do + if builtinTypeNames.contains adtDecl.name then + throwErrorAt adtDecl.ident s!"ADT name '{adtDecl.name}' shadows a built-in type" + -- Compute namespace offset (#1730, Axis 4 Steps 4b/4c): when `storage_namespace` + -- is present, every user-declared slot N becomes (namespaceBase + N). + -- With `storage_namespace "custom"`, the custom string replaces the default + -- "{ContractName}.storage.v0" key. + let namespaceOffset : Nat ← + match nsSpec with + | some spec => + -- `storage_namespace` alone → default; `storage_namespace "key"` → custom. + -- Match the syntax category directly so the custom string is not lost + -- behind parser wrapper nodes. + match spec with + | `(verityNamespaceSpec| storage_namespace $customKey:str) => + match customKey.raw.isStrLit? with + | some key => pure (computeStorageNamespaceKey key) + | none => throwErrorAt customKey "expected storage namespace string literal" + | `(verityNamespaceSpec| storage_namespace) => + pure (computeStorageNamespace (toString contractName.getId)) + | _ => + throwErrorAt spec "unsupported storage namespace syntax" + | none => pure 0 let parsedErrors ← match errorDecls with - | some decls => decls.mapM parseError + | some decls => decls.mapM (parseError parsedNewtypes parsedAdts) | none => pure #[] let parsedConstants ← match constantDecls with - | some decls => decls.mapM parseConstant + | some decls => decls.mapM (parseConstant parsedNewtypes) | none => pure #[] let parsedImmutables ← match immutableDecls with - | some decls => decls.mapM parseImmutable + | some decls => decls.mapM (parseImmutable parsedNewtypes) | none => pure #[] let parsedExternals ← match externalDecls with - | some decls => decls.mapM parseExternal + | some decls => decls.mapM (parseExternal parsedNewtypes parsedAdts) | none => pure #[] + -- Apply namespace offset to parsed storage fields (#1730, Axis 4 Step 4b) + let parsedFields ← storageFields.mapM (parseStorageField parsedNewtypes parsedAdts) + let parsedFields := parsedFields.map fun field => + { field with slotNum := field.slotNum + namespaceOffset } + -- Compute the Option Nat for the spec's storageNamespace field (#1730, Axis 4 Step 4d) + let namespaceOpt : Option Nat := + if nsSpec.isSome then some namespaceOffset else none pure ( contractName - , (← storageFields.mapM parseStorageField) + , parsedNewtypes + , parsedAdts + , parsedFields , parsedErrors , parsedConstants , parsedImmutables , parsedExternals - , (← ctor.mapM parseConstructor) - , (← entrypoints.mapM parseSpecialEntrypoint) ++ (← functions.mapM parseFunction) + , (← ctor.mapM (parseConstructor parsedNewtypes parsedAdts)) + , (← entrypoints.mapM parseSpecialEntrypoint) ++ (← functions.mapM (parseFunction parsedNewtypes parsedAdts)) + , namespaceOpt ) | _ => throwErrorAt stx "invalid verity_contract declaration" @@ -3776,6 +4405,16 @@ def mkStorageDefCommandPublic (field : StorageFieldDecl) : CommandElabM Cmd := def mkConstantDefCommandPublic (constant : ConstantDecl) : CommandElabM Cmd := mkConstantDefCommand constant +/-- Generate a `def storageNamespace : Nat := ` command for + the current contract. Uses the resolved namespace value from + `parseContractSyntax` to respect custom `storage_namespace "key"`. + (#1730, Axis 4 Step 4a) -/ +def mkStorageNamespaceCommand (contractName : String) (resolvedNamespace : Option Nat := none) : CommandElabM Cmd := do + let _ := contractName + let ns := resolvedNamespace.getD 0 + let id : Ident := mkIdent (Name.mkSimple "storageNamespace") + `(command| def $id : Nat := $(natTerm ns)) + def validateConstantDeclsPublic (constDecls : Array ConstantDecl) : CommandElabM Unit := do for constant in constDecls do validateConstantBody constDecls constant.body [constant.name] @@ -3785,7 +4424,7 @@ def validateGeneratedDefNamesPublic (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (functions : Array FunctionDecl) : CommandElabM Unit := do - let reservedGeneratedNames : Array String := #["spec"] + let reservedGeneratedNames : Array String := #["spec", "storageNamespace"] let mut generatedHelperNames : Array String := reservedGeneratedNames let mut storageNames : Array String := #[] @@ -3831,12 +4470,17 @@ def validateGeneratedDefNamesPublic , s!"{fn.name}_model" , s!"{fn.name}_bridge" , s!"{fn.name}_semantic_preservation" + , s!"{fn.name}_is_view" + , s!"{fn.name}_no_calls" + , s!"{fn.name}_modifies" + , s!"{fn.name}_frame" + , s!"{fn.name}_frame_rfl" + , s!"{fn.name}_effects" + , s!"{fn.name}_cei_compliant" + , s!"{fn.name}_nonreentrant" + , s!"{fn.name}_cei_safe" + , s!"{fn.name}_requires_role" ] - let helperNames := - if fn.isView then - helperNames.push s!"{fn.name}_is_view" - else - helperNames for helperName in helperNames do if storageNames.contains helperName then throwErrorAt fn.ident @@ -3881,9 +4525,9 @@ def validateExternalDeclsPublic if seenNames.contains ext.name then throwErrorAt ext.ident s!"duplicate external declaration '{ext.name}'" - if ext.returnTys.size > 1 then - throwErrorAt ext.ident - s!"linked external '{ext.name}' currently supports at most one return value; statement-style external bindings are not exposed from verity_contract yet" + -- Multi-return externals are allowed; the auto-revert expression form (externalCall) + -- only supports single-return, but tryExternalCall supports any return count. + -- (#1727, Axis 1 Step 5f) for paramTy in ext.params do validateExternalExecutableType ext.ident ext.name paramTy "parameter" for returnTy in ext.returnTys do @@ -3913,30 +4557,78 @@ def validateFunctionDeclsPublic (functions : Array FunctionDecl) : CommandElabM Unit := do match ctor with | some ctor => + for param in ctor.params do + rejectExecutableBoundaryAdt param.ident s!"constructor parameter '{param.name}'" param.ty validateLocalObligationDecls "constructor" ctor.localObligations validateConstructorBodyExprTypes fields errorDecls constDecls immutableDecls externalDecls functions ctor | none => pure () for fn in functions do + for param in fn.params do + rejectExecutableBoundaryAdt param.ident s!"function '{fn.name}' parameter '{param.name}'" param.ty + rejectExecutableBoundaryAdt fn.ident s!"function '{fn.name}' return type" fn.returnTy validateLocalObligationDecls s!"function '{fn.name}'" fn.localObligations + -- Validate modifies field names exist in the storage section + for modField in fn.modifies do + let modName := toString modField.getId + let allFieldNames := fields.map (·.name) + if !allFieldNames.contains modName then + throwErrorAt modField s!"function '{fn.name}': modifies references unknown storage field '{modName}'; known fields: {allFieldNames.toList}" + -- view functions must not use modifies (they already imply no writes) + if fn.isView && !fn.modifies.isEmpty then + throwErrorAt fn.ident s!"function '{fn.name}' is marked view and modifies(...); view already guarantees no state writes" + -- Validate nonreentrant lock field references a valid storage field of scalar uint256 type + match fn.nonReentrantLock with + | some lockField => + let lockName := toString lockField.getId + let allFieldNames := fields.map (·.name) + match fields.find? (fun field => field.name == lockName) with + | none => + throwErrorAt lockField s!"function '{fn.name}': nonreentrant references unknown storage field '{lockName}'; known fields: {allFieldNames.toList}" + | some field => + match field.ty with + | .scalar .uint256 => pure () + | _ => + throwErrorAt lockField s!"function '{fn.name}': nonreentrant lock field '{lockName}' must be a scalar Uint256 storage field" + | none => pure () + -- cei_safe and allow_post_interaction_writes are mutually exclusive with nonreentrant + if fn.ceiSafe && fn.allowPostInteractionWrites then + throwErrorAt fn.ident s!"function '{fn.name}': cei_safe and allow_post_interaction_writes are mutually exclusive" + if fn.nonReentrantLock.isSome && fn.allowPostInteractionWrites then + throwErrorAt fn.ident s!"function '{fn.name}': nonreentrant and allow_post_interaction_writes are mutually exclusive" + if fn.nonReentrantLock.isSome && fn.ceiSafe then + throwErrorAt fn.ident s!"function '{fn.name}': nonreentrant and cei_safe are mutually exclusive" validateFunctionBodyExprTypes fields errorDecls constDecls immutableDecls externalDecls functions fn def mkFunctionCommandsPublic (fields : Array StorageFieldDecl) (constDecls : Array ConstantDecl) (immutableDecls : Array ImmutableDecl) + (externalDecls : Array ExternalDecl) (functions : Array FunctionDecl) (fn : FunctionDecl) : CommandElabM (Array Cmd) := do let fnType ← mkContractFnType fn.params fn.returnTy - let fnGuardedBody ← mkInitGuardedBody fields fn + let fnRoleGuardedBody ← mkRoleGuardedBody fields fn + let fnDecl := { fn with body := fnRoleGuardedBody } + let fnGuardedBody ← mkInitGuardedBody fields fnDecl let fnBody ← mkImmutableBoundBody fields immutableDecls fn fnGuardedBody let fnValue ← mkContractFnValue fn.params fnBody let modelBodyName ← mkSuffixedIdent fn.ident "_modelBody" let modelName ← mkSuffixedIdent fn.ident "_model" - let stmtTerms ← translateBodyToStmtTerms fields constDecls immutableDecls functions fn + let stmtTerms ← translateBodyToStmtTerms fields constDecls immutableDecls externalDecls functions fn let modelParams ← mkModelParamsTerm fn.params let localObligationTerms ← fn.localObligations.mapM mkModelLocalObligationTerm let payableTerm ← if fn.isPayable then `(true) else `(false) let viewTerm ← if fn.isView then `(true) else `(false) + let noExternalCallsTerm ← if fn.noExternalCalls then `(true) else `(false) + let allowPostInteractionWritesTerm ← if fn.allowPostInteractionWrites then `(true) else `(false) + let nonReentrantLockTerm ← match fn.nonReentrantLock with + | some lockIdent => `(some $(strTerm (toString lockIdent.getId))) + | none => `(none) + let ceiSafeTerm ← if fn.ceiSafe then `(true) else `(false) + let requiresRoleTerm ← match fn.requiresRole with + | some roleIdent => `(some $(strTerm (toString roleIdent.getId))) + | none => `(none) + let modifiesTerms : Array Term := fn.modifies.map fun ident => strTerm (toString ident.getId) let returnTypeTerm ← modelReturnTypeTerm fn.returnTy let returnsTerm ← modelReturnsTerm fn.returnTy @@ -3949,6 +4641,12 @@ def mkFunctionCommandsPublic «returns» := $returnsTerm isPayable := $payableTerm isView := $viewTerm + noExternalCalls := $noExternalCallsTerm + allowPostInteractionWrites := $allowPostInteractionWritesTerm + nonReentrantLock := $nonReentrantLockTerm + ceiSafe := $ceiSafeTerm + requiresRole := $requiresRoleTerm + modifies := [ $[$modifiesTerms],* ] localObligations := [ $[$localObligationTerms],* ] body := $modelBodyName }) @@ -3962,8 +4660,10 @@ def mkSpecCommandPublic (immutableDecls : Array ImmutableDecl) (externalDecls : Array ExternalDecl) (ctor : Option ConstructorDecl) - (functions : Array FunctionDecl) : CommandElabM Cmd := - mkSpecCommand contractName fields errorDecls constDecls immutableDecls externalDecls ctor functions + (functions : Array FunctionDecl) + (adtDecls : Array AdtDecl) + (storageNamespace : Option Nat) : CommandElabM Cmd := + mkSpecCommand contractName fields errorDecls constDecls immutableDecls externalDecls ctor functions adtDecls storageNamespace def mkFindIdxFieldSimpCommandsPublic (contractIdent : Ident) diff --git a/artifacts/macro_property_tests/PropertyAdtMixedFieldCounts.t.sol b/artifacts/macro_property_tests/PropertyAdtMixedFieldCounts.t.sol new file mode 100644 index 000000000..7bfa8b6ea --- /dev/null +++ b/artifacts/macro_property_tests/PropertyAdtMixedFieldCounts.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyAdtMixedFieldCountsTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyAdtMixedFieldCountsTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("AdtMixedFieldCounts"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: clear has no unexpected revert + function testAuto_Clear_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("clear()")); + require(ok, "clear reverted unexpectedly"); + } + // Property 2: set has no unexpected revert + function testAuto_Set_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("set(uint256)", uint256(1))); + require(ok, "set reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyAdtNewtypeCombo.t.sol b/artifacts/macro_property_tests/PropertyAdtNewtypeCombo.t.sol new file mode 100644 index 000000000..b58d7092e --- /dev/null +++ b/artifacts/macro_property_tests/PropertyAdtNewtypeCombo.t.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyAdtNewtypeComboTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyAdtNewtypeComboTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("AdtNewtypeCombo"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: pause has no unexpected revert + function testAuto_Pause_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("pause()")); + require(ok, "pause reverted unexpectedly"); + } + // Property 2: unpause has no unexpected revert + function testAuto_Unpause_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("unpause()")); + require(ok, "unpause reverted unexpectedly"); + } + // Property 3: setLastId has no unexpected revert + function testAuto_SetLastId_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("setLastId(uint256)", uint256(1))); + require(ok, "setLastId reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyAdtSingleVariant.t.sol b/artifacts/macro_property_tests/PropertyAdtSingleVariant.t.sol new file mode 100644 index 000000000..595d5f08d --- /dev/null +++ b/artifacts/macro_property_tests/PropertyAdtSingleVariant.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyAdtSingleVariantTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyAdtSingleVariantTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("AdtSingleVariant"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: store has no unexpected revert + function testAuto_Store_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("store()")); + require(ok, "store reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyAdtSmoke.t.sol b/artifacts/macro_property_tests/PropertyAdtSmoke.t.sol new file mode 100644 index 000000000..bd2988239 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyAdtSmoke.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyAdtSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyAdtSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("AdtSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: increment has no unexpected revert + function testAuto_Increment_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("increment()")); + require(ok, "increment reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyCEILadderSmoke.t.sol b/artifacts/macro_property_tests/PropertyCEILadderSmoke.t.sol new file mode 100644 index 000000000..8c95e9f52 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyCEILadderSmoke.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyCEILadderSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyCEILadderSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("CEILadderSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: TODO decode and assert `storeThenCall` result + function testTODO_StoreThenCall_DecodeAndAssert() public { + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("storeThenCall(uint256)", uint256(1))); + require(ok, "storeThenCall reverted unexpectedly"); + assertEq(ret.length, 32, "storeThenCall ABI return length mismatch (expected 32 bytes)"); + // TODO(#1011): decode `ret` and assert the concrete postcondition from Lean theorem. + ret; + } + // Property 2: increment has no unexpected revert + function testAuto_Increment_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("increment()")); + require(ok, "increment reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyCEISmoke.t.sol b/artifacts/macro_property_tests/PropertyCEISmoke.t.sol new file mode 100644 index 000000000..a0dab2277 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyCEISmoke.t.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyCEISmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyCEISmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("CEISmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: increment has no unexpected revert + function testAuto_Increment_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("increment()")); + require(ok, "increment reverted unexpectedly"); + } + // Property 2: getCounter reads storage slot 0 and decodes the result + function testAuto_GetCounter_ReadsConfiguredStorage() public { + uint256 expected = uint256(1); + vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getCounter()")); + require(ok, "getCounter reverted unexpectedly"); + assertEq(ret.length, 32, "getCounter ABI return length mismatch (expected 32 bytes)"); + uint256 actual = abi.decode(ret, (uint256)); + assertEq(actual, expected, "getCounter should return storage slot 0"); + } + // Property 3: TODO decode and assert `updateThenCall` result + function testTODO_UpdateThenCall_DecodeAndAssert() public { + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("updateThenCall(uint256)", uint256(1))); + require(ok, "updateThenCall reverted unexpectedly"); + assertEq(ret.length, 32, "updateThenCall ABI return length mismatch (expected 32 bytes)"); + // TODO(#1011): decode `ret` and assert the concrete postcondition from Lean theorem. + ret; + } +} diff --git a/artifacts/macro_property_tests/PropertyCustomNamespacedSmoke.t.sol b/artifacts/macro_property_tests/PropertyCustomNamespacedSmoke.t.sol new file mode 100644 index 000000000..775458f6a --- /dev/null +++ b/artifacts/macro_property_tests/PropertyCustomNamespacedSmoke.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyCustomNamespacedSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyCustomNamespacedSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("CustomNamespacedSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: deposit has no unexpected revert + function testAuto_Deposit_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("deposit(uint256)", uint256(1))); + require(ok, "deposit reverted unexpectedly"); + } + // Property 2: getOwner reads storage slot 1 and decodes the result + function testAuto_GetOwner_ReadsConfiguredStorage() public { + address expected = alice; + vm.store(target, bytes32(uint256(1)), bytes32(uint256(uint160(expected)))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getOwner()")); + require(ok, "getOwner reverted unexpectedly"); + assertEq(ret.length, 32, "getOwner ABI return length mismatch (expected 32 bytes)"); + address actual = abi.decode(ret, (address)); + assertEq(actual, expected, "getOwner should return storage slot 1"); + } +} diff --git a/artifacts/macro_property_tests/PropertyDirectHelperCallSmoke.t.sol b/artifacts/macro_property_tests/PropertyDirectHelperCallSmoke.t.sol index 1b0610306..35c6c9718 100644 --- a/artifacts/macro_property_tests/PropertyDirectHelperCallSmoke.t.sol +++ b/artifacts/macro_property_tests/PropertyDirectHelperCallSmoke.t.sol @@ -46,18 +46,7 @@ contract PropertyDirectHelperCallSmokeTest is YulTestBase { assertEq(actual0, expected, "pairWithTotal tuple element 0 should preserve the inferred result"); assertEq(actual1, (expected + uint256(1)), "pairWithTotal tuple element 1 should preserve the inferred result"); } - // Property 4: runHelpers decodes and matches the inferred straight-line result - function testAuto_RunHelpers_ReturnsInferredStraightLineResult() public { - uint256 expected = uint256(1); - vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); - vm.prank(alice); - (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("runHelpers(uint256,uint256,uint256)", uint256(1), uint256(1), uint256(1))); - require(ok, "runHelpers reverted unexpectedly"); - assertEq(ret.length, 32, "runHelpers ABI return length mismatch (expected 32 bytes)"); - uint256 actual = abi.decode(ret, (uint256)); - assertEq(actual, ((expected + uint256(1)) + uint256(1)), "runHelpers should preserve the inferred result"); - } - // Property 5: snapshot decodes and matches the inferred tuple result + // Property 4: snapshot decodes and matches the inferred tuple result function testAuto_Snapshot_ReturnsInferredTupleResult() public { uint256 expected = uint256(1); vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); diff --git a/artifacts/macro_property_tests/PropertyERC20HelperSmoke.t.sol b/artifacts/macro_property_tests/PropertyERC20HelperSmoke.t.sol index 2be6cc1df..f45c9f9b3 100644 --- a/artifacts/macro_property_tests/PropertyERC20HelperSmoke.t.sol +++ b/artifacts/macro_property_tests/PropertyERC20HelperSmoke.t.sol @@ -35,40 +35,4 @@ contract PropertyERC20HelperSmokeTest is YulTestBase { (bool ok,) = target.call(abi.encodeWithSignature("approveTokens(address,address,uint256)", alice, alice, uint256(1))); require(ok, "approveTokens reverted unexpectedly"); } - // Property 4: snapshotBalance decodes the mocked ERC20 balance read - function testAuto_SnapshotBalance_ReturnsMockedBalanceRead() public { - uint256 expected = uint256(1); - vm.mockCall(alice, abi.encodeWithSignature("balanceOf(address)", alice), abi.encode(expected)); - vm.prank(alice); - (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("snapshotBalance(address,address)", alice, alice)); - require(ok, "snapshotBalance reverted unexpectedly"); - assertEq(ret.length, 32, "snapshotBalance ABI return length mismatch (expected 32 bytes)"); - uint256 actual = abi.decode(ret, (uint256)); - assertEq(actual, expected, "snapshotBalance should return the mocked external read"); - assertEq(vm.load(target, bytes32(uint256(0))), bytes32(uint256(expected)), "snapshotBalance should persist the mocked external read"); - } - // Property 5: snapshotAllowance decodes the mocked ERC20 allowance read - function testAuto_SnapshotAllowance_ReturnsMockedAllowanceRead() public { - uint256 expected = uint256(1); - vm.mockCall(alice, abi.encodeWithSignature("allowance(address,address)", alice, alice), abi.encode(expected)); - vm.prank(alice); - (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("snapshotAllowance(address,address,address)", alice, alice, alice)); - require(ok, "snapshotAllowance reverted unexpectedly"); - assertEq(ret.length, 32, "snapshotAllowance ABI return length mismatch (expected 32 bytes)"); - uint256 actual = abi.decode(ret, (uint256)); - assertEq(actual, expected, "snapshotAllowance should return the mocked external read"); - assertEq(vm.load(target, bytes32(uint256(1))), bytes32(uint256(expected)), "snapshotAllowance should persist the mocked external read"); - } - // Property 6: snapshotSupply decodes the mocked ERC20 supply read - function testAuto_SnapshotSupply_ReturnsMockedSupplyRead() public { - uint256 expected = uint256(1); - vm.mockCall(alice, abi.encodeWithSignature("totalSupply()"), abi.encode(expected)); - vm.prank(alice); - (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("snapshotSupply(address)", alice)); - require(ok, "snapshotSupply reverted unexpectedly"); - assertEq(ret.length, 32, "snapshotSupply ABI return length mismatch (expected 32 bytes)"); - uint256 actual = abi.decode(ret, (uint256)); - assertEq(actual, expected, "snapshotSupply should return the mocked external read"); - assertEq(vm.load(target, bytes32(uint256(2))), bytes32(uint256(expected)), "snapshotSupply should persist the mocked external read"); - } } diff --git a/artifacts/macro_property_tests/PropertyEffectCompositionSmoke.t.sol b/artifacts/macro_property_tests/PropertyEffectCompositionSmoke.t.sol new file mode 100644 index 000000000..a5f02bc8c --- /dev/null +++ b/artifacts/macro_property_tests/PropertyEffectCompositionSmoke.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyEffectCompositionSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyEffectCompositionSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("EffectCompositionSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: deposit has no unexpected revert + function testAuto_Deposit_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("deposit(uint256)", uint256(1))); + require(ok, "deposit reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyExternalCallMultiReturn.t.sol b/artifacts/macro_property_tests/PropertyExternalCallMultiReturn.t.sol new file mode 100644 index 000000000..3be3fc62b --- /dev/null +++ b/artifacts/macro_property_tests/PropertyExternalCallMultiReturn.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyExternalCallMultiReturnTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyExternalCallMultiReturnTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("ExternalCallMultiReturn"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: noop has no unexpected revert + function testAuto_Noop_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("noop()")); + require(ok, "noop reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyExternalCallSmoke.t.sol b/artifacts/macro_property_tests/PropertyExternalCallSmoke.t.sol index 3df249cc9..0018d7e5a 100644 --- a/artifacts/macro_property_tests/PropertyExternalCallSmoke.t.sol +++ b/artifacts/macro_property_tests/PropertyExternalCallSmoke.t.sol @@ -17,13 +17,7 @@ contract PropertyExternalCallSmokeTest is YulTestBase { require(target != address(0), "Deploy failed"); } - // Property 1: storeEcho has no unexpected revert - function testAuto_StoreEcho_NoUnexpectedRevert() public { - vm.prank(alice); - (bool ok,) = target.call(abi.encodeWithSignature("storeEcho(uint256)", uint256(1))); - require(ok, "storeEcho reverted unexpectedly"); - } - // Property 2: getEchoedValue reads storage slot 0 and decodes the result + // Property 1: getEchoedValue reads storage slot 0 and decodes the result function testAuto_GetEchoedValue_ReadsConfiguredStorage() public { uint256 expected = uint256(1); vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); diff --git a/artifacts/macro_property_tests/PropertyFullComboSmoke.t.sol b/artifacts/macro_property_tests/PropertyFullComboSmoke.t.sol new file mode 100644 index 000000000..071889ccc --- /dev/null +++ b/artifacts/macro_property_tests/PropertyFullComboSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyFullComboSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyFullComboSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("FullComboSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyGenericECMReadSmoke.t.sol b/artifacts/macro_property_tests/PropertyGenericECMReadSmoke.t.sol index 55aaae672..7d4af6d7c 100644 --- a/artifacts/macro_property_tests/PropertyGenericECMReadSmoke.t.sol +++ b/artifacts/macro_property_tests/PropertyGenericECMReadSmoke.t.sol @@ -17,16 +17,4 @@ contract PropertyGenericECMReadSmokeTest is YulTestBase { require(target != address(0), "Deploy failed"); } - // Property 1: snapshotQuote decodes the mocked ECM oracle read - function testAuto_SnapshotQuote_ReturnsMockedEcmRead() public { - uint256 expected = uint256(1); - vm.mockCall(alice, abi.encodeWithSelector(bytes4(0x12345678), alice), abi.encode(expected)); - vm.prank(alice); - (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("snapshotQuote(address,address)", alice, alice)); - require(ok, "snapshotQuote reverted unexpectedly"); - assertEq(ret.length, 32, "snapshotQuote ABI return length mismatch (expected 32 bytes)"); - uint256 actual = abi.decode(ret, (uint256)); - assertEq(actual, expected, "snapshotQuote should return the mocked external read"); - assertEq(vm.load(target, bytes32(uint256(0))), bytes32(uint256(expected)), "snapshotQuote should persist the mocked external read"); - } } diff --git a/artifacts/macro_property_tests/PropertyModifiesNamespaceSmoke.t.sol b/artifacts/macro_property_tests/PropertyModifiesNamespaceSmoke.t.sol new file mode 100644 index 000000000..a44ce643f --- /dev/null +++ b/artifacts/macro_property_tests/PropertyModifiesNamespaceSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyModifiesNamespaceSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyModifiesNamespaceSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("ModifiesNamespaceSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyModifiesRolesSmoke.t.sol b/artifacts/macro_property_tests/PropertyModifiesRolesSmoke.t.sol new file mode 100644 index 000000000..ecb02fd76 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyModifiesRolesSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyModifiesRolesSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyModifiesRolesSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("ModifiesRolesSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyModifiesSmoke.t.sol b/artifacts/macro_property_tests/PropertyModifiesSmoke.t.sol new file mode 100644 index 000000000..ddb5468bd --- /dev/null +++ b/artifacts/macro_property_tests/PropertyModifiesSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyModifiesSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyModifiesSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("ModifiesSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyNamespacedStorageSmoke.t.sol b/artifacts/macro_property_tests/PropertyNamespacedStorageSmoke.t.sol new file mode 100644 index 000000000..16a4badd6 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNamespacedStorageSmoke.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNamespacedStorageSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNamespacedStorageSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("NamespacedStorageSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + + // Property 1: deposit has no unexpected revert + function testAuto_Deposit_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("deposit(uint256)", uint256(1))); + require(ok, "deposit reverted unexpectedly"); + } + // Property 2: getOwner reads storage slot 1 and decodes the result + function testAuto_GetOwner_ReadsConfiguredStorage() public { + address expected = alice; + vm.store(target, bytes32(uint256(1)), bytes32(uint256(uint160(expected)))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getOwner()")); + require(ok, "getOwner reverted unexpectedly"); + assertEq(ret.length, 32, "getOwner ABI return length mismatch (expected 32 bytes)"); + address actual = abi.decode(ret, (address)); + assertEq(actual, expected, "getOwner should return storage slot 1"); + } +} diff --git a/artifacts/macro_property_tests/PropertyNewtypeModifiesSmoke.t.sol b/artifacts/macro_property_tests/PropertyNewtypeModifiesSmoke.t.sol new file mode 100644 index 000000000..ab7ef5281 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNewtypeModifiesSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNewtypeModifiesSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNewtypeModifiesSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("NewtypeModifiesSmoke"); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyNewtypeNamespaceSmoke.t.sol b/artifacts/macro_property_tests/PropertyNewtypeNamespaceSmoke.t.sol new file mode 100644 index 000000000..c8051d52d --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNewtypeNamespaceSmoke.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNewtypeNamespaceSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNewtypeNamespaceSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("NewtypeNamespaceSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: setId has no unexpected revert + function testAuto_SetId_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("setId(uint256)", uint256(1))); + require(ok, "setId reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyNewtypeSmoke.t.sol b/artifacts/macro_property_tests/PropertyNewtypeSmoke.t.sol new file mode 100644 index 000000000..996964473 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNewtypeSmoke.t.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNewtypeSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNewtypeSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("NewtypeSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + + // Property 1: mint has no unexpected revert + function testAuto_Mint_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("mint(uint256,uint256)", uint256(1), uint256(1))); + require(ok, "mint reverted unexpectedly"); + } + // Property 2: setMinter has no unexpected revert + function testAuto_SetMinter_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("setMinter(address)", alice)); + require(ok, "setMinter reverted unexpectedly"); + } + // Property 3: getNextTokenId reads storage slot 0 and decodes the result + function testAuto_GetNextTokenId_ReadsConfiguredStorage() public { + uint256 expected = uint256(1); + vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getNextTokenId()")); + require(ok, "getNextTokenId reverted unexpectedly"); + assertEq(ret.length, 32, "getNextTokenId ABI return length mismatch (expected 32 bytes)"); + uint256 actual = abi.decode(ret, (uint256)); + assertEq(actual, expected, "getNextTokenId should return storage slot 0"); + } +} diff --git a/artifacts/macro_property_tests/PropertyNewtypeStorageSmoke.t.sol b/artifacts/macro_property_tests/PropertyNewtypeStorageSmoke.t.sol new file mode 100644 index 000000000..1a2d50f49 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNewtypeStorageSmoke.t.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNewtypeStorageSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNewtypeStorageSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("NewtypeStorageSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + + // Property 1: setTokenId has no unexpected revert + function testAuto_SetTokenId_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("setTokenId(uint256)", uint256(1))); + require(ok, "setTokenId reverted unexpectedly"); + } + // Property 2: getTokenId reads storage slot 0 and decodes the result + function testAuto_GetTokenId_ReadsConfiguredStorage() public { + uint256 expected = uint256(1); + vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getTokenId()")); + require(ok, "getTokenId reverted unexpectedly"); + assertEq(ret.length, 32, "getTokenId ABI return length mismatch (expected 32 bytes)"); + uint256 actual = abi.decode(ret, (uint256)); + assertEq(actual, expected, "getTokenId should return storage slot 0"); + } + // Property 3: setAdmin has no unexpected revert + function testAuto_SetAdmin_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("setAdmin(address)", alice)); + require(ok, "setAdmin reverted unexpectedly"); + } + // Property 4: getAdmin reads storage slot 1 and decodes the result + function testAuto_GetAdmin_ReadsConfiguredStorage() public { + address expected = alice; + vm.store(target, bytes32(uint256(1)), bytes32(uint256(uint160(expected)))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getAdmin()")); + require(ok, "getAdmin reverted unexpectedly"); + assertEq(ret.length, 32, "getAdmin ABI return length mismatch (expected 32 bytes)"); + address actual = abi.decode(ret, (address)); + assertEq(actual, expected, "getAdmin should return storage slot 1"); + } +} diff --git a/artifacts/macro_property_tests/PropertyNoExternalCallsSmoke.t.sol b/artifacts/macro_property_tests/PropertyNoExternalCallsSmoke.t.sol new file mode 100644 index 000000000..c07c148a3 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNoExternalCallsSmoke.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNoExternalCallsSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNoExternalCallsSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("NoExternalCallsSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: setOwner has no unexpected revert + function testAuto_SetOwner_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("setOwner(address)", alice)); + require(ok, "setOwner reverted unexpectedly"); + } +} diff --git a/artifacts/macro_property_tests/PropertyNonreentrantModifiesSmoke.t.sol b/artifacts/macro_property_tests/PropertyNonreentrantModifiesSmoke.t.sol new file mode 100644 index 000000000..16784f87a --- /dev/null +++ b/artifacts/macro_property_tests/PropertyNonreentrantModifiesSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyNonreentrantModifiesSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyNonreentrantModifiesSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("NonreentrantModifiesSmoke"); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyRolesCEISmoke.t.sol b/artifacts/macro_property_tests/PropertyRolesCEISmoke.t.sol new file mode 100644 index 000000000..a47f81461 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyRolesCEISmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyRolesCEISmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyRolesCEISmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("RolesCEISmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyRolesSmoke.t.sol b/artifacts/macro_property_tests/PropertyRolesSmoke.t.sol new file mode 100644 index 000000000..da8c733d2 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyRolesSmoke.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyRolesSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyRolesSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYulWithArgs("RolesSmoke", abi.encode(alice)); + require(target != address(0), "Deploy failed"); + } + + // Property 1: getCounter reads storage slot 1 and decodes the result + function testAuto_GetCounter_ReadsConfiguredStorage() public { + uint256 expected = uint256(1); + vm.store(target, bytes32(uint256(1)), bytes32(uint256(expected))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getCounter()")); + require(ok, "getCounter reverted unexpectedly"); + assertEq(ret.length, 32, "getCounter ABI return length mismatch (expected 32 bytes)"); + uint256 actual = abi.decode(ret, (uint256)); + assertEq(actual, expected, "getCounter should return storage slot 1"); + } +} diff --git a/artifacts/macro_property_tests/PropertyTryExternalCallSmoke.t.sol b/artifacts/macro_property_tests/PropertyTryExternalCallSmoke.t.sol new file mode 100644 index 000000000..430153eb1 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyTryExternalCallSmoke.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyTryExternalCallSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyTryExternalCallSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("TryExternalCallSmoke"); + require(target != address(0), "Deploy failed"); + } + +} diff --git a/artifacts/macro_property_tests/PropertyUnsafeBlockSmoke.t.sol b/artifacts/macro_property_tests/PropertyUnsafeBlockSmoke.t.sol new file mode 100644 index 000000000..ad89c3889 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyUnsafeBlockSmoke.t.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyUnsafeBlockSmokeTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyUnsafeBlockSmokeTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("UnsafeBlockSmoke"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: incrementUnsafe has no unexpected revert + function testAuto_IncrementUnsafe_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("incrementUnsafe()")); + require(ok, "incrementUnsafe reverted unexpectedly"); + } + // Property 2: getCounter reads storage slot 0 and decodes the result + function testAuto_GetCounter_ReadsConfiguredStorage() public { + uint256 expected = uint256(1); + vm.store(target, bytes32(uint256(0)), bytes32(uint256(expected))); + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("getCounter()")); + require(ok, "getCounter reverted unexpectedly"); + assertEq(ret.length, 32, "getCounter ABI return length mismatch (expected 32 bytes)"); + uint256 actual = abi.decode(ret, (uint256)); + assertEq(actual, expected, "getCounter should return storage slot 0"); + } +} diff --git a/artifacts/macro_property_tests/PropertyUnsafeCEICompliant.t.sol b/artifacts/macro_property_tests/PropertyUnsafeCEICompliant.t.sol new file mode 100644 index 000000000..3289baa05 --- /dev/null +++ b/artifacts/macro_property_tests/PropertyUnsafeCEICompliant.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyUnsafeCEICompliantTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyUnsafeCEICompliantTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("UnsafeCEICompliant"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: TODO decode and assert `writeBeforeUnsafeCall` result + function testTODO_WriteBeforeUnsafeCall_DecodeAndAssert() public { + vm.prank(alice); + (bool ok, bytes memory ret) = target.call(abi.encodeWithSignature("writeBeforeUnsafeCall(uint256)", uint256(1))); + require(ok, "writeBeforeUnsafeCall reverted unexpectedly"); + assertEq(ret.length, 32, "writeBeforeUnsafeCall ABI return length mismatch (expected 32 bytes)"); + // TODO(#1011): decode `ret` and assert the concrete postcondition from Lean theorem. + ret; + } +} diff --git a/artifacts/macro_property_tests/PropertyUnsafeGatingAccepted.t.sol b/artifacts/macro_property_tests/PropertyUnsafeGatingAccepted.t.sol new file mode 100644 index 000000000..73e28227d --- /dev/null +++ b/artifacts/macro_property_tests/PropertyUnsafeGatingAccepted.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.33; + +import "./yul/YulTestBase.sol"; + +/** + * @title PropertyUnsafeGatingAcceptedTest + * @notice Auto-generated baseline property stubs from `verity_contract` declarations. + * @dev Source: Contracts/Smoke.lean + */ +contract PropertyUnsafeGatingAcceptedTest is YulTestBase { + address target; + address alice = address(0x1111); + + function setUp() public { + target = deployYul("UnsafeGatingAccepted"); + require(target != address(0), "Deploy failed"); + } + + // Property 1: writeMem has no unexpected revert + function testAuto_WriteMem_NoUnexpectedRevert() public { + vm.prank(alice); + (bool ok,) = target.call(abi.encodeWithSignature("writeMem()")); + require(ok, "writeMem reverted unexpectedly"); + } +} diff --git a/docs/LANGUAGE_DESIGN_AXES.md b/docs/LANGUAGE_DESIGN_AXES.md new file mode 100644 index 000000000..fd1f3f5a0 --- /dev/null +++ b/docs/LANGUAGE_DESIGN_AXES.md @@ -0,0 +1,141 @@ +# Language Design Axes — Implementation Plan + +> Tracking issues: #1726 (umbrella), #1727 (Axis 1), #1728 (Axis 2), #1729 (Axis 3), #1730 (Axis 4) +> Connects to: #1724 (Solidity parity), #1723 (proven fragment extension) + +## Overview + +This document describes the plan to take Verity's `verity_contract` EDSL beyond Solidity feature parity by fixing Solidity's systemic design flaws at the language level. Four axes of improvement have been identified, each with concrete features, implementation strategies, and integration points with the existing codebase. + +**Design philosophy**: Easy to translate from Solidity, hard to introduce Solidity's bugs. + +## Suggested Implementation Order + +The order is driven by **impact x effort x unblocking power**: + +### Phase 1: Effect System & Auto-Theorem Generation (Axis 3, #1729) + +**Why first**: Lowest friction, highest proof payoff. + +- `@[view]` verification already exists in `Validation.lean` (`stmtWritesState`) — the infrastructure is 80% there +- Frame condition theorems are the #1 bottleneck in compositional verification — every annotated function instantly contributes to #1723 +- No `verity_contract` grammar changes needed — only extends existing function-level attributes +- Builds the **auto-theorem generation machinery** that Axis 2 reuses + +| Step | Feature | Effort | Detail | +|---|---|---|---| +| 1a | Strengthen `@[view]` with auto-generated `_is_view` theorem | 1 week | Keep existing validation; add theorem emission in `Macro/Elaborate.lean` | +| 1b | Add `@[modifies slot1, slot2]` with `_frame` theorem | 2–3 weeks | New validation pass (enumerate write targets vs declared set); generate universally-quantified frame theorem | +| 1c | Add `@[no_external_calls]` with `_no_calls` theorem | 1 week | Reject external call statements; generate no-calls theorem | +| 1d | Annotation composition | 1 week | Multiple annotations on one function; conjunction theorem | + +**Files touched**: `Macro/Elaborate.lean`, `Macro/Translate.lean`, `CompilationModel/Validation.lean` +**Estimated total**: 3–4 weeks + +### Phase 2: CEI Enforcement + Access Control (Axis 2 partial, #1728) + +**Why second**: Reuses Phase 1's theorem generation infrastructure. + +| Step | Feature | Effort | Detail | +|---|---|---|---| +| 2a | CEI static analysis | 1–2 weeks | Walk function body AST, track external call position, error on writes after calls | +| 2b | CEI opt-out ladder | 1 week | `@[cei_safe by proof]` (machine-checked), `nonReentrant` recognition (low trust), `@[allow_post_interaction_writes]` (trust surface) | +| 2c | `roles` section + `@[requires Role]` | 2–3 weeks | New grammar section; macro-generated require + auto-generated access control theorem | + +**Escalation ladder** (consistent across all safety features): +``` +SAFE BY DEFAULT (compiler enforces) + → LEAN PROOF (machine-checked, zero trust) + → KNOWN-SAFE GUARD (nonReentrant, require) + → EXPLICIT ANNOTATION (trust surface in --trust-report) +``` + +**Files touched**: `Macro/Syntax.lean` (roles section), `Macro/Translate.lean`, new `Macro/CEICheck.lean` +**Estimated total**: 4 weeks + +### Phase 3: Semantic Newtypes (Axis 1 partial, #1727) + +**Why third**: Standalone grammar change, doesn't depend on Phase 1–2. + +| Step | Feature | Effort | Detail | +|---|---|---|---| +| 3a | `types` section in grammar | 1 week | Add to `Macro/Syntax.lean`; parse `TypeName : BaseType` entries — **done** (dc4b40d2) | +| 3b | `ParamType.newtypeOf` + `ValueType.newtype` | 1 week | Extend `ParamType` inductive with `newtypeOf` constructor; add `ValueType.newtype`; wire through all exhaustive pattern matches across 11 files — **done** | +| 3c | Type checking + Yul erasure | 1 week | Newtypes erased to base type at every Yul boundary (zero overhead); `#check_contract NewtypeSmoke` passes — **done** | + +**Key insight from research**: `ParamType` is a closed inductive with 13 variants (including `adt` and `newtypeOf`) and `valueTypeFromSyntax` resolves user-defined type names via `NewtypeDecl` lookup. The `newtypeOf` constructor is erased to `baseType` at every compilation boundary. + +**Files touched**: `CompilationModel/Types.lean`, `CompilationModel/AbiTypeLayout.lean`, `CompilationModel/AbiHelpers.lean`, `CompilationModel/AbiEncoding.lean`, `CompilationModel/ParamLoading.lean`, `CompilationModel/EventAbiHelpers.lean`, `CompilationModel/ScopeValidation.lean`, `CompilationModel/ValidationCalls.lean`, `Compiler/ABI.lean`, `Compiler/TypedIRCompiler.lean`, `Verity/Macro/Translate.lean` +**Estimated total**: 2–3 weeks + +### Phase 4: Namespaced Storage (Axis 4, #1730) + +**Why fourth**: Standalone, doesn't depend on other phases. + +| Step | Feature | Effort | Detail | +|---|---|---|---| +| 4a | Namespace computation | 1 week | Compute `keccak256("{ContractName}.storage.v0")` at elaboration time using kernel Keccak | +| 4b | Slot offsetting | 1 week | All `slot N` declarations become `slot (namespace_base + N)` | +| 4c | Override attributes | 0.5 weeks | `@[no_namespace]` to opt out; `@[namespace "custom"]` for custom string | +| 4d | ABI + tooling integration | 0.5 weeks | Emit namespace in ABI JSON; `--print-storage-layout` flag | + +**Files touched**: `Macro/Elaborate.lean`, `Macro/Translate.lean`, `CompilationModel/AbiHelpers.lean` +**Estimated total**: 2–3 weeks + +### Phase 5: ADTs + Pattern Matching + Result Types (Axis 1 remainder, #1727) + +**Why last**: Largest change, touches the most files, benefits from all prior infrastructure. + +| Step | Feature | Effort | Detail | +|---|---|---|---| +| 5a | `inductive` section in grammar | 1 week | Parse variant definitions with typed fields | +| 5b | New CompilationModel constructs | 2 weeks | `ParamType.adt`, `Expr.adtConstruct/adtTag/adtField`, `Stmt.matchAdt` | +| 5c | Storage encoding (tagged unions) | 1 week | Tag byte + max-width fields in consecutive slots | +| 5d | Yul lowering | 1 week | `matchAdt` → `YulStmt.switch`; `adtConstruct` → sequential `sstore` | +| 5e | ABI encoding | 1 week | `(uint8 tag, fields...)` with max-width encoding | +| 5f | `Call.Result` + `!` sugar | 2 weeks | Built-in ADT for external call results; `externalCall!` desugars to match+revert | + +**Key design decisions**: +- Storage layout uses max-width of all variants (like Rust enum layout) +- Exhaustiveness is free — Lean's kernel checks it during elaboration +- `YulStmt.switch` already exists in the Yul AST (used by the dispatcher) — no Yul-level changes needed + +**Files touched**: `Macro/Syntax.lean`, `CompilationModel/Types.lean`, `Macro/Translate.lean`, `CompilationModel/Compile.lean`, `CompilationModel/Validation.lean`, `CompilationModel/AbiHelpers.lean` +**Estimated total**: 6–8 weeks + +### Phase 6: Unsafe Blocks (Axis 2 remainder, #1728) + +| Step | Feature | Effort | Detail | +|---|---|---|---| +| 6a | `unsafe "reason" do` syntax | 1 week | Add to grammar; track safe/unsafe context | +| 6b | Restricted operation gating | 1 week | Error if `delegatecall`/raw `sstore`/raw `mstore`/`rawLog` appears outside `unsafe` | +| 6c | Trust report + deny flag | 0.5 weeks | Each `unsafe` block → `--trust-report` entry; `--deny-unsafe` flag | + +**Files touched**: `Macro/Syntax.lean`, `Macro/Translate.lean`, trust report infrastructure +**Estimated total**: 2 weeks + +--- + +## Total Estimated Timeline + +| Phase | Axis | Weeks | Parallelizable with | +|---|---|---|---| +| 1 | Effect system (#1729) | 3–4 | — | +| 2 | CEI + access control (#1728) | 4 | Phase 3 | +| 3 | Newtypes (#1727) | 2–3 | Phase 2 | +| 4 | Namespaced storage (#1730) | 2–3 | Phase 5 | +| 5 | ADTs + Result types (#1727) | 6–8 | Phase 4 | +| 6 | Unsafe blocks (#1728) | 2 | Phase 5 | + +**Critical path**: Phase 1 → Phase 2 → Phase 5 (13–16 weeks) +**With parallelization**: ~12–14 weeks total + +## Integration with #1724 Parity Waves + +| #1724 Wave | Language Design Enrichment | +|---|---| +| Wave 1: Type System (widths, enum) | + Newtypes (Phase 3), ADTs (Phase 5) | +| Wave 2: Control Flow & Safety | + CEI (Phase 2), `@[requires]` (Phase 2), effect annotations (Phase 1) | +| Wave 3: Storage | + EIP-7201 namespaces (Phase 4) | +| Wave 4: ABI & Memory | + `Call.Result` + `!` sugar (Phase 5) | +| Wave 5: Composition | + `unsafe` blocks (Phase 6) | diff --git a/scripts/check_macro_roundtrip_fuzz_coverage.py b/scripts/check_macro_roundtrip_fuzz_coverage.py index de73e1b49..8abbdfaed 100644 --- a/scripts/check_macro_roundtrip_fuzz_coverage.py +++ b/scripts/check_macro_roundtrip_fuzz_coverage.py @@ -18,6 +18,12 @@ "StringEqSmoke", # Direct string equality lowering still depends on ABI-dynamic string inputs, which this numeric-only fuzz harness does not model "LocalObligationRequiredForUnsafeFunctionBoundary", # Intentionally fails compilation (#guard_msgs negative test) "LocalObligationRequiredForUnsafeConstructorBoundary", # Intentionally fails compilation (#guard_msgs negative test) + "CEIViolationRejected", # Intentionally fails compilation (CEI violation #guard_msgs negative test) + "CEIWriteInBranchAfterCall", # Intentionally fails compilation (CEI violation #guard_msgs negative test) + "CEICallBothBranchesWrite", # Intentionally fails compilation (CEI violation #guard_msgs negative test) + "UnsafeCEIViolation", # Intentionally fails compilation (CEI violation #guard_msgs negative test) + "CEIInternalCallAfterExternalRejected", # Intentionally fails validation (CEI violation — internal call after external call) + "UnsafeGatingRejected", # Intentionally fails compilation (unsafe gating #guard_msgs negative test) } CONTRACT_RE = re.compile(r"\bverity_contract\s+([A-Za-z_][A-Za-z0-9_]*)\s+where\b") diff --git a/scripts/check_proof_length.py b/scripts/check_proof_length.py index 83f708fb9..56455090e 100644 --- a/scripts/check_proof_length.py +++ b/scripts/check_proof_length.py @@ -45,9 +45,11 @@ # --- Core expression/statement compilation --- "compileExpr_core_ok", "compileRequireFailCond_core_ok", + "compileStmt_core_ok_any_scope", # mstore/tstore widening — mechanical per-constructor cases "compileStmtList_core_ok", "compileStmtList_terminal_core_ok", "compileStmtList_terminal_core_ok_nonempty", + "execStmtList_terminal_core_not_continue", # mstore/tstore widening — 9 per-constructor cases "compileStmtList_terminal_ite_ok_inv", "compileStmt_terminal_ite_ok_inv", "compileStmt_ok_any_scope_aux", @@ -133,7 +135,9 @@ "exprTouchesInternalHelperSurface_eq_false_of_helperSurfaceClosed", "exprTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed", "stmtTouchesInternalHelperSurface_eq_false_of_helperSurfaceClosed", + "stmtListExprHelperCallNames_subset_stmtListInternalHelperCallNames", "stmtTouchesUnsupportedContractSurface_eq_false_of_featureClosed", + "stmtListTerminalCore_helperSurfaceClosed", # mstore/tstore widening — mechanical per-constructor cases "SupportedStmtList.helperSurfaceClosed", "SupportedStmtList.internalHelperCallNames_nil", "supportedStmtList_usesStorageArrayElement_false", diff --git a/scripts/generate_macro_property_tests.py b/scripts/generate_macro_property_tests.py index 7d5e8e16d..3a875bc72 100644 --- a/scripts/generate_macro_property_tests.py +++ b/scripts/generate_macro_property_tests.py @@ -26,6 +26,9 @@ ) CONSTRUCTOR_RE = re.compile(r"^\s*constructor\s*\(([^)]*)\)\s*:=\s*") PARAM_RE = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+?)\s*$") +NEWTYPE_RE = re.compile( + r"^\s*([A-Z][A-Za-z0-9_]*)\s*:\s*([A-Za-z0-9_]+)\s*$", +) STORAGE_RE = re.compile( r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+?)\s*:=\s*slot\s+([0-9]+)\s*$", ) @@ -122,6 +125,7 @@ class ContractDecl: storage_slots: dict[str, int] source: Path storage_types: dict[str, str] = field(default_factory=dict) + newtypes: dict[str, str] = field(default_factory=dict) constants: dict[str, ValueDecl] = field(default_factory=dict) immutables: dict[str, ValueDecl] = field(default_factory=dict) @@ -187,18 +191,37 @@ def _split_params(params_src: str) -> tuple[ParamDecl, ...]: return tuple(out) +def _resolve_newtypes_in_params( + params: tuple[ParamDecl, ...], newtypes: dict[str, str] +) -> tuple[ParamDecl, ...]: + """Replace newtype names with their base types in param declarations.""" + if not newtypes: + return params + return tuple( + ParamDecl(name=p.name, lean_type=newtypes.get(p.lean_type, p.lean_type)) + for p in params + ) + + +def _resolve_newtype(ty: str, newtypes: dict[str, str]) -> str: + """Replace a newtype name with its base type if found.""" + return newtypes.get(ty, ty) + + def parse_contracts(text: str, source: Path) -> dict[str, ContractDecl]: contracts: dict[str, ContractDecl] = {} current_name: str | None = None current_constructor: ConstructorDecl | None = None current_storage_slots: dict[str, int] = {} current_storage_types: dict[str, str] = {} + current_newtypes: dict[str, str] = {} current_constants: dict[str, ValueDecl] = {} current_immutables: dict[str, ValueDecl] = {} current_functions: list[FunctionDecl] = [] current_function: FunctionDecl | None = None current_body: list[str] = [] guard_pending = False + in_types_block = False in_storage_block = False in_constants_block = False in_immutables_block = False @@ -220,7 +243,7 @@ def flush_function() -> None: current_body = [] def flush_current() -> None: - nonlocal current_name, current_constructor, current_storage_slots, current_storage_types, current_constants, current_immutables, current_functions, in_storage_block, in_constants_block, in_immutables_block, pending_storage_lines + nonlocal current_name, current_constructor, current_storage_slots, current_storage_types, current_newtypes, current_constants, current_immutables, current_functions, in_types_block, in_storage_block, in_constants_block, in_immutables_block, pending_storage_lines if current_name is None: return flush_function() @@ -231,6 +254,7 @@ def flush_current() -> None: storage_slots=dict(current_storage_slots), source=source, storage_types=dict(current_storage_types), + newtypes=dict(current_newtypes), constants=dict(current_constants), immutables=dict(current_immutables), ) @@ -238,9 +262,11 @@ def flush_current() -> None: current_constructor = None current_storage_slots = {} current_storage_types = {} + current_newtypes = {} current_constants = {} current_immutables = {} current_functions = [] + in_types_block = False in_storage_block = False in_constants_block = False in_immutables_block = False @@ -260,13 +286,37 @@ def flush_current() -> None: current_name = cm.group(1) continue + # Clear guard_pending on any non-blank, non-comment line that isn't + # a verity_contract (e.g. `#check_contract Foo` after `#guard_msgs in`) + if guard_pending and line.strip() and not line.strip().startswith("--"): + guard_pending = False + if current_name is None: continue if current_function is not None and line.strip() and not line.startswith(" "): flush_function() + if line.strip() == "types": + in_types_block = True + in_storage_block = False + in_constants_block = False + in_immutables_block = False + pending_storage_lines = [] + continue + + if in_types_block: + stripped = line.strip() + nt = NEWTYPE_RE.match(stripped) + if nt: + current_newtypes[nt.group(1)] = _normalize_type(nt.group(2)) + continue + if stripped and not nt: + in_types_block = False + # fall through to check other sections + if line.strip() == "storage": + in_types_block = False in_storage_block = True in_constants_block = False in_immutables_block = False @@ -292,7 +342,8 @@ def flush_current() -> None: flush_function() if current_constructor is not None: raise ValueError(f"duplicate constructor in contract '{current_name}'") - current_constructor = ConstructorDecl(params=_split_params(ctor.group(1))) + current_constructor = ConstructorDecl(params=_resolve_newtypes_in_params(_split_params(ctor.group(1)), current_newtypes)) + in_types_block = False in_storage_block = False in_constants_block = False in_immutables_block = False @@ -303,10 +354,10 @@ def flush_current() -> None: flush_function() fn_name = fm.group(1) params_src = fm.group(2) - ret_ty = _normalize_type(fm.group(3)) + ret_ty = _resolve_newtype(_normalize_type(fm.group(3)), current_newtypes) current_function = FunctionDecl( name=fn_name, - params=_split_params(params_src), + params=_resolve_newtypes_in_params(_split_params(params_src), current_newtypes), return_type=ret_ty, ) in_storage_block = False