From f1a68067f06b3a5778a4f0e73676fa58c03e2a06 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Sun, 24 May 2026 08:28:12 +0200 Subject: [PATCH 01/27] Add IR for-loop execution lemmas --- .../Proofs/IRGeneration/IRInterpreter.lean | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index 113c7ba36..33dd93aad 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -983,6 +983,73 @@ def execIRStmts (fuel : Nat) (state : IRState) : List YulStmt → IRExecResult end -- mutual +theorem execIRStmt_for_init_noncontinue + (fuel : Nat) (state : IRState) + (init post body : List YulStmt) (cond : YulExpr) (r : IRExecResult) + (hinit : execIRStmts fuel state init = r) + (hterm : ∀ s, r ≠ .continue s) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = r := by + simp only [execIRStmt, hinit] + +theorem execIRStmt_for_cond_none + (fuel : Nat) (state sInit : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = none) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = + .revert sInit := by + simp [execIRStmt, hinit, hcond] + +theorem execIRStmt_for_init_cond_zero + (fuel : Nat) (state sInit : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some 0) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = + .continue sInit := by + simp [execIRStmt, hinit, hcond] + +theorem execIRStmt_for_body_noncontinue + (fuel : Nat) (state sInit : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (condValue : Nat) (r : IRExecResult) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some condValue) + (hcondNZ : condValue ≠ 0) + (hbody : execIRStmts fuel sInit body = r) + (hterm : ∀ s, r ≠ .continue s) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = r := by + simp only [execIRStmt, hinit, hcond] + simp [hcondNZ, hbody] + +theorem execIRStmt_for_post_noncontinue + (fuel : Nat) (state sInit sBody : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (condValue : Nat) (r : IRExecResult) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some condValue) + (hcondNZ : condValue ≠ 0) + (hbody : execIRStmts fuel sInit body = .continue sBody) + (hpost : execIRStmts fuel sBody post = r) + (hterm : ∀ s, r ≠ .continue s) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = r := by + simp only [execIRStmt, hinit, hcond] + simp [hcondNZ, hbody, hpost] + +theorem execIRStmt_for_one_continue + (fuel : Nat) (state sInit sBody sPost : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (condValue : Nat) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some condValue) + (hcondNZ : condValue ≠ 0) + (hbody : execIRStmts fuel sInit body = .continue sBody) + (hpost : execIRStmts fuel sBody post = .continue sPost) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = + execIRStmt fuel sPost (.for_ [] cond post body) := by + simp only [execIRStmt, hinit, hcond] + simp [hcondNZ, hbody, hpost] + @[simp] theorem execIRStmt_stop_succ (fuel : Nat) (state : IRState) : execIRStmt (Nat.succ fuel) state (YulStmt.expr (YulExpr.call "stop" [])) = .stop state := by From d7036bbb6e807d936181f60c87025df4924fcb76 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Sun, 24 May 2026 10:40:39 +0200 Subject: [PATCH 02/27] Add source forEach loop semantics --- Compiler/Proofs/IRGeneration/Contract.lean | 5 + .../Proofs/IRGeneration/GenericInduction.lean | 4 + .../Proofs/IRGeneration/IRInterpreter.lean | 104 +++++++++++++++++- .../Proofs/IRGeneration/SourceSemantics.lean | 83 +++++++++++++- 4 files changed, 192 insertions(+), 4 deletions(-) diff --git a/Compiler/Proofs/IRGeneration/Contract.lean b/Compiler/Proofs/IRGeneration/Contract.lean index 48b966521..8c01c09a9 100644 --- a/Compiler/Proofs/IRGeneration/Contract.lean +++ b/Compiler/Proofs/IRGeneration/Contract.lean @@ -132,6 +132,11 @@ private theorem legacyCompatibleExternalStmtList_append | block body rest hbody hrest ihBody ihRest => intro after hafter simpa using LegacyCompatibleExternalStmtList.block body (rest ++ after) hbody (ihRest after hafter) + | for_ init cond post body rest hinit hpost hbody hrest ihInit ihPost ihBody ihRest => + intro after hafter + simpa using + LegacyCompatibleExternalStmtList.for_ init cond post body (rest ++ after) + hinit hpost hbody (ihRest after hafter) | funcDef name params rets body rest hbody hrest ihBody ihRest => intro after hafter simpa using LegacyCompatibleExternalStmtList.funcDef name params rets body (rest ++ after) hbody (ihRest after hafter) diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 47d3031f6..171e8a08e 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -522,6 +522,10 @@ private theorem legacyCompatibleExternalStmtList_append simpa using LegacyCompatibleExternalStmtList.if_ cond body (rest ++ after) hbody (ihRest hafter) | block body rest hbody hrest ihBody ihRest => simpa using LegacyCompatibleExternalStmtList.block body (rest ++ after) hbody (ihRest hafter) + | for_ init cond post body rest hinit hpost hbody hrest ihInit ihPost ihBody ihRest => + simpa using + LegacyCompatibleExternalStmtList.for_ init cond post body (rest ++ after) + hinit hpost hbody (ihRest hafter) | funcDef name params rets body rest hbody hrest ihBody ihRest => simpa using LegacyCompatibleExternalStmtList.funcDef name params rets body (rest ++ after) hbody (ihRest hafter) diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index 33dd93aad..afdcf8a0e 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -366,9 +366,10 @@ private def prepareInternalCalleeState /-- Legacy IR-theorem compatibility subset for external bodies. This excludes the helper-only Yul forms whose semantics differ from the current helper-free -interpreter target (`letMany`, `leave`, `switch`, and `for`) while still -allowing nested blocks / branches so the conservative-extension theorem can -talk about today's compiled external-body shape explicitly. -/ +interpreter target (`letMany`, `leave`, and `switch`) while still allowing +nested blocks, branches, and structurally compatible loops so the +conservative-extension theorem can talk about today's compiled external-body +shape explicitly. -/ inductive LegacyCompatibleExternalStmtList : List YulStmt → Prop | nil : LegacyCompatibleExternalStmtList [] @@ -392,6 +393,12 @@ inductive LegacyCompatibleExternalStmtList : List YulStmt → Prop LegacyCompatibleExternalStmtList body → LegacyCompatibleExternalStmtList rest → LegacyCompatibleExternalStmtList (.block body :: rest) + | for_ (init : List YulStmt) (cond : YulExpr) (post body rest : List YulStmt) : + LegacyCompatibleExternalStmtList init → + LegacyCompatibleExternalStmtList post → + LegacyCompatibleExternalStmtList body → + LegacyCompatibleExternalStmtList rest → + LegacyCompatibleExternalStmtList (.for_ init cond post body :: rest) | funcDef (name : String) (params rets : List String) (body rest : List YulStmt) : LegacyCompatibleExternalStmtList body → LegacyCompatibleExternalStmtList rest → @@ -3035,6 +3042,10 @@ theorem YulStmtListCallsDisjointFromInternalTable_of_internalFunctions_nil (yulExprCallsDisjointFromInternalTable_of_internalFunctions_nil contract hinternal cond) ihBody ihRest | block body rest hbody _ ihBody ihRest => exact .block body rest ihBody ihRest + | for_ init cond post body rest hinit hpost hbody _ ihInit ihPost ihBody ihRest => + exact .for_ init cond post body rest ihInit + (yulExprCallsDisjointFromInternalTable_of_internalFunctions_nil contract hinternal cond) + ihPost ihBody ihRest | funcDef name params rets body rest hbody _ ihBody ihRest => exact .funcDef name params rets body rest ihBody ihRest @@ -3567,6 +3578,81 @@ theorem execIRStmtsWithInternals_eq_execIRStmts_of_exprCompatibility rw [execIRStmtsWithInternals, execIRStmts, hhead] cases hstep : execIRStmt fuel state (.block body) <;> simp only [ihRest] + | for_ init cond post body rest hinit hpost hbody hrest ihInit ihPost ihBody ihRest => + cases fuel with + | zero => + simp only [execIRStmtsWithInternals, execIRStmts] + | succ fuel => + have hhead : + execIRStmtWithInternals contract fuel state (.for_ init cond post body) = + match execIRStmt fuel state (.for_ init cond post body) with + | .continue next => .continue next + | .return value next => .return value next + | .stop next => .stop next + | .revert next => .revert next := by + suffices hgen : ∀ (f : Nat) (s : IRState) (initStmts : List YulStmt), + (∀ f' s', execIRStmtsWithInternals contract f' s' initStmts = + match execIRStmts f' s' initStmts with + | .continue n => .continue n | .return v n => .return v n + | .stop n => .stop n | .revert n => .revert n) → + execIRStmtWithInternals contract f s (.for_ initStmts cond post body) = + match execIRStmt f s (.for_ initStmts cond post body) with + | .continue next => .continue next + | .return value next => .return value next + | .stop next => .stop next + | .revert next => .revert next by + exact hgen fuel state init (ihInit · ·) + intro f + induction f with + | zero => + intro s initStmts _ + simp only [execIRStmtWithInternals, execIRStmt] + | succ f ihFuel => + intro s initStmts hInitEq + simp only [execIRStmtWithInternals, execIRStmt] + rw [hInitEq] + cases hInitStep : execIRStmts f s initStmts with + | «continue» s' => + simp only [] + rw [evalIRExprWithInternals_eq_evalIRExpr_of_no_internal + contract hinternal f s' cond] + cases hCondVal : evalIRExpr s' cond with + | none => + simp + | some condValue => + by_cases hnonzero : condValue ≠ 0 + · simp only [if_pos hnonzero] + rw [ihBody f s'] + cases hBodyStep : execIRStmts f s' body with + | «continue» s'' => + simp only [] + rw [ihPost f s''] + cases hPostStep : execIRStmts f s'' post with + | «continue» s''' => + simp only [] + have hNilEq : ∀ f' s', + execIRStmtsWithInternals contract f' s' [] = + match execIRStmts f' s' [] with + | .continue n => .continue n + | .return v n => .return v n + | .stop n => .stop n + | .revert n => .revert n := by + intro f' s' + simp only [execIRStmtsWithInternals, execIRStmts] + exact ihFuel s''' [] hNilEq + | «return» v s''' => simp + | stop s''' => simp + | revert s''' => simp + | «return» v s'' => simp + | stop s'' => simp + | revert s'' => simp + · simp only [if_neg hnonzero] + | «return» v s' => simp + | stop s' => simp + | revert s' => simp + rw [execIRStmtsWithInternals, execIRStmts, hhead] + cases hstep : execIRStmt fuel state (.for_ init cond post body) <;> + simp only [ihRest] | funcDef name params rets body rest hbody hrest ihBody ihRest => cases fuel with | zero => @@ -3750,6 +3836,18 @@ theorem execIRStmtsWithInternals_eq_execIRStmts_of_stmtCompatibility rw [execIRStmtsWithInternals, execIRStmts, hhead] cases hstep : execIRStmt fuel state (.block body) <;> simp only [ihRest] + | for_ init cond post body rest hinit hpost hbody hrest ihInit ihPost ihBody ihRest => + cases fuel with + | zero => + simp only [execIRStmtsWithInternals, execIRStmts] + | succ fuel => + have hhead := + hstmt hinternal fuel state (.for_ init cond post body) + (LegacyCompatibleExternalStmtList.for_ init cond post body [] + hinit hpost hbody LegacyCompatibleExternalStmtList.nil) + rw [execIRStmtsWithInternals, execIRStmts, hhead] + cases hstep : execIRStmt fuel state (.for_ init cond post body) <;> + simp only [ihRest] | funcDef name params rets body rest hbody hrest ihBody ihRest => cases fuel with | zero => diff --git a/Compiler/Proofs/IRGeneration/SourceSemantics.lean b/Compiler/Proofs/IRGeneration/SourceSemantics.lean index ee0a9da8c..e1420fc09 100644 --- a/Compiler/Proofs/IRGeneration/SourceSemantics.lean +++ b/Compiler/Proofs/IRGeneration/SourceSemantics.lean @@ -549,6 +549,36 @@ inductive StmtResult where | return (value : Nat) (state : RuntimeState) | revert +def execForEachLoop + (varName : String) + (runBody : RuntimeState → StmtResult) : + RuntimeState → Nat → Nat → StmtResult + | state, _, 0 => .continue state + | state, index, remaining + 1 => + let loopState := + { state with bindings := bindValue state.bindings varName (wordNormalize index) } + match runBody loopState with + | .continue next => execForEachLoop varName runBody next (index + 1) remaining + | .stop next => .stop next + | .return value next => .return value next + | .revert => .revert + +theorem execForEachLoop_congr + {varName : String} + {runBodyA runBodyB : RuntimeState → StmtResult} + (hbody : ∀ state, runBodyA state = runBodyB state) : + ∀ (state : RuntimeState) (index remaining : Nat), + execForEachLoop varName runBodyA state index remaining = + execForEachLoop varName runBodyB state index remaining + | state, index, 0 => by + simp [execForEachLoop] + | state, index, remaining + 1 => by + simp only [execForEachLoop] + rw [hbody] + cases hrun : runBodyB + { state with bindings := bindValue state.bindings varName (wordNormalize index) } <;> + simp [hrun, execForEachLoop_congr hbody] + private def storageArraySetAt : List Verity.Core.Uint256 → Nat → Verity.Core.Uint256 → Option (List Verity.Core.Uint256) | [], _, _ => none | _ :: rest, 0, value => some (value :: rest) @@ -1871,6 +1901,13 @@ mutual events := state.world.events ++ [event] } } | _, _ => .revert | none => .revert + | state, .forEach varName count body => + match evalExpr fields state count with + | some bound => + execForEachLoop varName + (fun loopState => execStmtListWithEvents fields events loopState body) + state 0 bound + | none => .revert | _, _ => .revert def execStmtListWithEvents (fields : List Field) (events : List EventDef) : @@ -2106,6 +2143,13 @@ mutual events := state.world.events ++ [event] } } | _, _ => .revert | none => .revert + | state, .forEach varName count body => + match evalExpr fields state count with + | some bound => + execForEachLoop varName + (fun loopState => execStmtList fields loopState body) + state 0 bound + | none => .revert | _, _ => .revert def execStmtList (fields : List Field) : RuntimeState → List Stmt → StmtResult @@ -3113,6 +3157,13 @@ mutual events := state.world.events ++ [event] } } | _, _ => .revert | none => .revert + | .forEach varName count body => + match evalExprWithHelpers spec fields fuel state count with + | some bound => + execForEachLoop varName + (fun loopState => execStmtListWithHelpers spec fields fuel loopState body) + state 0 bound + | none => .revert | _ => .revert termination_by stmt => (fuel, sizeOf stmt) decreasing_by all_goals (simp_wf; omega) @@ -4163,6 +4214,13 @@ mutual simp [Stmt.ite.sizeOf_spec] omega + private theorem stmt_sizeOf_lt_forEach_body + (varName : String) (count : Expr) (body : List Stmt) : + sizeOf body + 1 < sizeOf (Stmt.forEach varName count body) := by + have hcount : 0 < sizeOf count := expr_sizeOf_pos count + simp [Stmt.forEach.sizeOf_spec] + omega + private theorem stmt_sizeOf_lt_cons (stmt : Stmt) (rest : List Stmt) : sizeOf stmt + 1 < sizeOf (stmt :: rest) := by cases rest with @@ -4344,7 +4402,30 @@ private theorem execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux | .ecm _ _ => simp [execStmtWithHelpers, execStmtWithEvents] | .forEach _ _ _ => simp only [stmtTouchesUnsupportedHelperSurface, Bool.or_eq_false_iff] at hsurface - simp [execStmtWithHelpers, execStmtWithEvents] + rename_i varName count body + have hcount := + evalExprWithHelpers_eq_evalExpr_of_helperSurfaceClosed + spec fields fuel state count hsurface.1 + simp only [execStmtWithHelpers, execStmtWithEvents, hcount] + cases evalExpr fields state count with + | none => rfl + | some bound => + exact execForEachLoop_congr + (varName := varName) + (runBodyA := fun loopState => + execStmtListWithHelpers spec fields fuel loopState body) + (runBodyB := fun loopState => + execStmtListWithEvents fields spec.events loopState body) + (fun loopState => + execStmtListWithHelpers_eq_execStmtList_of_helperSurfaceClosed_inner + spec fields fuel loopState body hsurface.2 + (fun st s hs hsf => + have : sizeOf s < sizeOf (Stmt.forEach varName count body) := by + have hbody := stmt_sizeOf_lt_forEach_body varName count body + omega + execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux + spec fields fuel st s hsf)) + state 0 bound termination_by sizeOf stmt theorem execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed From a588e94bd633c23c38a52c393379a54defdc5122 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Sun, 24 May 2026 11:39:50 +0200 Subject: [PATCH 03/27] Align forEach lowering with source semantics --- Compiler/CompilationModel/Compile.lean | 22 +++++-- .../Proofs/IRGeneration/SourceSemantics.lean | 16 +++-- .../Backends/EvmYulLeanBodyClosure.lean | 62 +++++++++++++------ 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/Compiler/CompilationModel/Compile.lean b/Compiler/CompilationModel/Compile.lean index 259563d41..efb916144 100644 --- a/Compiler/CompilationModel/Compile.lean +++ b/Compiler/CompilationModel/Compile.lean @@ -246,13 +246,25 @@ 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) + -- Bounded loop: evaluate `count` once into a cached local, drive iteration + -- with a fresh internal counter, and assign the user-visible `varName` to + -- the current counter at the top of each iteration. This matches the source + -- semantics where `count` is evaluated once and `varName` holds the last + -- iteration state after the loop rather than the post-incremented counter. let countExpr ← compileExpr fields dynamicSource count 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] + let forUsedNames := varName :: (inScopeNames ++ collectExprNames count ++ collectStmtListNames body) + let idxName := pickFreshName "__forEach_idx" forUsedNames + let countName := pickFreshName "__forEach_count" (idxName :: forUsedNames) + let initStmts := [ + YulStmt.let_ idxName (YulExpr.lit 0), + YulStmt.let_ countName countExpr, + YulStmt.let_ varName (YulExpr.lit 0) + ] + let condExpr := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName] + let postStmts := [YulStmt.assign idxName (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + let bodyWithBind := YulStmt.assign varName (YulExpr.ident idxName) :: bodyStmts + pure [YulStmt.for_ initStmts condExpr postStmts bodyWithBind] | Stmt.unsafeBlock _ body => do -- Unsafe block: transparent wrapper, compile inner body directly (#1728, Axis 6 Step 6a) diff --git a/Compiler/Proofs/IRGeneration/SourceSemantics.lean b/Compiler/Proofs/IRGeneration/SourceSemantics.lean index e1420fc09..770f2cea3 100644 --- a/Compiler/Proofs/IRGeneration/SourceSemantics.lean +++ b/Compiler/Proofs/IRGeneration/SourceSemantics.lean @@ -1904,9 +1904,11 @@ mutual | state, .forEach varName count body => match evalExpr fields state count with | some bound => + let initialLoopState := + { state with bindings := bindValue state.bindings varName (wordNormalize 0) } execForEachLoop varName (fun loopState => execStmtListWithEvents fields events loopState body) - state 0 bound + initialLoopState 0 bound | none => .revert | _, _ => .revert @@ -2146,9 +2148,11 @@ mutual | state, .forEach varName count body => match evalExpr fields state count with | some bound => + let initialLoopState := + { state with bindings := bindValue state.bindings varName (wordNormalize 0) } execForEachLoop varName (fun loopState => execStmtList fields loopState body) - state 0 bound + initialLoopState 0 bound | none => .revert | _, _ => .revert @@ -3160,9 +3164,11 @@ mutual | .forEach varName count body => match evalExprWithHelpers spec fields fuel state count with | some bound => + let initialLoopState := + { state with bindings := bindValue state.bindings varName (wordNormalize 0) } execForEachLoop varName (fun loopState => execStmtListWithHelpers spec fields fuel loopState body) - state 0 bound + initialLoopState 0 bound | none => .revert | _ => .revert termination_by stmt => (fuel, sizeOf stmt) @@ -4410,6 +4416,8 @@ private theorem execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux cases evalExpr fields state count with | none => rfl | some bound => + let initialLoopState := + { state with bindings := bindValue state.bindings varName (wordNormalize 0) } exact execForEachLoop_congr (varName := varName) (runBodyA := fun loopState => @@ -4425,7 +4433,7 @@ private theorem execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux omega execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux spec fields fuel st s hsf)) - state 0 bound + initialLoopState 0 bound termination_by sizeOf stmt theorem execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed diff --git a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean index b8f08ba15..3acaf51a6 100644 --- a/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean +++ b/Compiler/Proofs/YulGeneration/Backends/EvmYulLeanBodyClosure.lean @@ -4023,34 +4023,58 @@ theorem compileStmt_forEach_with_bridged_body intro yulStmt hMem simp only [List.mem_singleton] at hMem subst yulStmt + let usedNames := varName :: (inScopeNames ++ (collectExprNames count ++ collectStmtListNames body)) + let idxName := pickFreshName "__forEach_idx" usedNames + let countName := pickFreshName "__forEach_count" (idxName :: usedNames) refine BridgedStmt.for_ _ _ _ _ ?_ ?_ ?_ ?_ - · -- init: [YulStmt.let_ varName (YulExpr.lit 0)] + · -- init: idx := 0; cached count := countExpr; varName := 0 intro stmt hMemInit - simp only [List.mem_singleton] at hMemInit - subst stmt - exact BridgedStmt.straight _ - (BridgedStraightStmt.let_ varName (.lit 0) (BridgedExpr.lit 0)) - · -- cond: lt(ident varName, countExpr) + simp only [List.mem_cons, List.mem_singleton] at hMemInit + rcases hMemInit with rfl | hMemInit + · exact BridgedStmt.straight _ + (by + simpa [idxName] using + BridgedStraightStmt.let_ idxName (.lit 0) (BridgedExpr.lit 0)) + rcases hMemInit with rfl | hMemInit + · exact BridgedStmt.straight _ + (by + simpa [countName] using + BridgedStraightStmt.let_ countName countExpr hBC) + rcases hMemInit with rfl | hMemInit + · exact BridgedStmt.straight _ + (BridgedStraightStmt.let_ varName (.lit 0) (BridgedExpr.lit 0)) + · cases hMemInit + · -- cond: lt(ident idxName, ident countName) refine BridgedExpr.call "lt" _ (Or.inl (by simp [bridgedBuiltins])) ?_ intro arg hMemArg simp only [List.mem_cons, List.not_mem_nil, or_false] at hMemArg rcases hMemArg with rfl | rfl - · exact BridgedExpr.ident varName - · exact hBC - · -- post: [YulStmt.assign varName (add(ident varName, lit 1))] + · simpa [idxName] using BridgedExpr.ident idxName + · simpa [countName] using BridgedExpr.ident countName + · -- post: idxName := add(idxName, 1) intro stmt hMemPost simp only [List.mem_singleton] at hMemPost subst stmt - refine BridgedStmt.straight _ - (BridgedStraightStmt.assign varName _ ?_) - refine BridgedExpr.call "add" _ (Or.inl (by simp [bridgedBuiltins])) ?_ - intro arg hMemArg - simp only [List.mem_cons, List.not_mem_nil, or_false] at hMemArg - rcases hMemArg with rfl | rfl - · exact BridgedExpr.ident varName - · exact BridgedExpr.lit 1 - · -- body - exact hBBody + exact BridgedStmt.straight _ + (by + simpa [idxName] using + BridgedStraightStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1]) + (by + refine BridgedExpr.call "add" _ (Or.inl (by simp [bridgedBuiltins])) ?_ + intro arg hMemArg + simp only [List.mem_cons, List.not_mem_nil, or_false] at hMemArg + rcases hMemArg with rfl | rfl + · exact BridgedExpr.ident idxName + · exact BridgedExpr.lit 1)) + · -- body: assign the user-visible loop variable, then run the compiled body + exact BridgedStmts_cons + (BridgedStmt.straight _ + (by + simpa [idxName] using + BridgedStraightStmt.assign varName (YulExpr.ident idxName) + (BridgedExpr.ident idxName))) + hBBody theorem compileStmt_ite_with_noFuncDefs_body (fields : List Field) (events : List EventDef) (errors : List ErrorDef) From 58de17bed423f85890f49a2ada9499fe0c4b12b3 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Sun, 24 May 2026 13:05:54 +0200 Subject: [PATCH 04/27] Add bounded forEach fragment scaffolding --- Compiler/Proofs/EndToEnd.lean | 7 ++++ .../Proofs/IRGeneration/FunctionBody.lean | 39 +++++++++++++++++++ .../Proofs/IRGeneration/GenericInduction.lean | 11 ++++++ .../IRGeneration/SupportedFragment.lean | 8 ++++ .../Proofs/IRGeneration/SupportedSpec.lean | 27 +++++++++++-- 5 files changed, 89 insertions(+), 3 deletions(-) diff --git a/Compiler/Proofs/EndToEnd.lean b/Compiler/Proofs/EndToEnd.lean index 9a54659b6..4282948a6 100644 --- a/Compiler/Proofs/EndToEnd.lean +++ b/Compiler/Proofs/EndToEnd.lean @@ -5291,6 +5291,9 @@ theorem supportedStmtList_safe_of_state_effect_closed | setStructMember2Single => simp [stmtListTouchesUnsupportedStateSurface, stmtTouchesUnsupportedStateSurface] at hState + | forEachLiteralBounded => + simp [stmtListTouchesUnsupportedStateSurface, + stmtTouchesUnsupportedStateSurface] at hState | requireClause clause _ ih => simpa using Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.append @@ -5557,6 +5560,10 @@ theorem supportedStmtList_safe_of_state_except_mapping_writes_stmt_safety (Compiler.Proofs.YulGeneration.Backends.bridgedSourceExpr_of_exprCompileCore hKey2) (Compiler.Proofs.YulGeneration.Backends.bridgedSourceExpr_of_exprCompileCore hValue) hMapping2 hMembers hFindMember rfl hZero hSlots + | forEachLiteralBounded => + simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurface] at hState | @requireClause scope clause rest _ ih => have hTailSafety : ∀ stmt ∈ rest, StmtMappingWriteSlotSafe fields stmt := by diff --git a/Compiler/Proofs/IRGeneration/FunctionBody.lean b/Compiler/Proofs/IRGeneration/FunctionBody.lean index 976d6168f..b74fc2bb9 100644 --- a/Compiler/Proofs/IRGeneration/FunctionBody.lean +++ b/Compiler/Proofs/IRGeneration/FunctionBody.lean @@ -7269,6 +7269,23 @@ theorem runtimeStateMatchesIR_setVar_irrelevant cases state simpa [runtimeStateMatchesIR, IRState.setVar] using hmatch +theorem runtimeStateMatchesIR_setVars_irrelevant + {fields : List Field} + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + {updates : List (String × Nat)} + (hmatch : runtimeStateMatchesIR fields runtime state) : + runtimeStateMatchesIR fields runtime (state.setVars updates) := by + induction updates generalizing state with + | nil => + simpa [IRState.setVars] using hmatch + | cons update updates ih => + cases update with + | mk name value => + simp [IRState.setVars] + exact ih (runtimeStateMatchesIR_setVar_irrelevant + (state := state) (name := name) (value := value) hmatch) + def stmtResultMatchesIRExecExact : SourceSemantics.StmtResult → IRExecResult → Prop | .continue runtime, .continue state => @@ -7486,6 +7503,28 @@ theorem bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant · rw [getVar_setVar_ne state tempName name value hEq] exact hexact name hname +theorem bindingsExactlyMatchIRVarsOnScope_setVars_irrelevant + {scope : List String} + {bindings : List (String × Nat)} + {state : IRState} + {updates : List (String × Nat)} + (hexact : bindingsExactlyMatchIRVarsOnScope scope bindings state) + (hfresh : ∀ update ∈ updates, update.1 ∉ scope) : + bindingsExactlyMatchIRVarsOnScope scope bindings (state.setVars updates) := by + induction updates generalizing state with + | nil => + simpa [IRState.setVars] using hexact + | cons update updates ih => + cases update with + | mk name value => + simp [IRState.setVars] + apply ih + · exact bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (state := state) (tempName := name) (value := value) + hexact (hfresh (name, value) (by simp)) + · intro update hmem + exact hfresh update (by simp [hmem]) + theorem bindingsExactlyMatchIRVarsOnScope_setVar_bindValue {scope : List String} {bindings : List (String × Nat)} diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 171e8a08e..2b74f3ca8 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -14055,6 +14055,9 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface exact False.elim (false_of_supportedStmtList_setMapping2WordSingle_surface hsurface) | setStructMember2Single hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hslot hmembers hmember => exact False.elim (false_of_supportedStmtList_setStructMember2Single_surface hsurface) + | forEachLiteralBounded _ _ _ => + simp [stmtListTouchesUnsupportedContractSurface, + stmtTouchesUnsupportedContractSurface] at hsurface | requireClause clause _ ih => simp [stmtListTouchesUnsupportedContractSurface] at hsurface apply stmtListGenericCore_append @@ -14183,6 +14186,10 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites hvalue hscopeValue hslot hmembers hmember with ⟨hm, hws, hss⟩ exact stmtListGenericCore_singleton_setStructMember2Single_of_slotSafety hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hm hmembers hmember hws hss + | forEachLiteralBounded _ _ _ => + simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurface] at hsurface | requireClause clause _ ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause ih hsurface @@ -14500,6 +14507,10 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites_ subst hwordOffsetEq exact stmtListGenericCore_singleton_setStructMember2Single_of_slotSafety hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hm hmembers hmember hws hss + | forEachLiteralBounded _ _ _ => + simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurface] at hsurface | requireClause clause hsupportedRest ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause diff --git a/Compiler/Proofs/IRGeneration/SupportedFragment.lean b/Compiler/Proofs/IRGeneration/SupportedFragment.lean index de6274998..6cd341be8 100644 --- a/Compiler/Proofs/IRGeneration/SupportedFragment.lean +++ b/Compiler/Proofs/IRGeneration/SupportedFragment.lean @@ -340,6 +340,14 @@ inductive SupportedStmtList (fields : List Field) : List String → List Stmt findStructMember members memberName = some { name := memberName, wordOffset := wordOffset, packed := none } → SupportedStmtList fields scope [Stmt.setStructMember2 fieldName key1 key2 memberName value] + | forEachLiteralBounded + {scope : List String} + {varName : String} + {n : Nat} + {body : List Stmt} : + (∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) → + SupportedStmtList fields (varName :: scope) body → + SupportedStmtList fields scope [Stmt.forEach varName (.literal n) body] | requireClause {scope : List String} (clause : RequireLiteralGuardFamilyClause) diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index ea667e974..416fd94d4 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -1229,9 +1229,7 @@ def stmtTouchesUnsupportedStateSurface : Stmt → Bool exprTouchesUnsupportedStateSurface cond || stmtListTouchesUnsupportedStateSurface thenBranch || stmtListTouchesUnsupportedStateSurface elseBranch - | .forEach _ count body => - exprTouchesUnsupportedStateSurface count || - stmtListTouchesUnsupportedStateSurface body + | .forEach _ _ _ => true /-- Weaker Tier 2 state-surface gate used by the singleton storage-write bridge: all existing unsupported stateful forms remain excluded except for the proved @@ -3006,6 +3004,10 @@ theorem SupportedStmtList.helperSurfaceClosed exact supportedStmtList_setMapping2WordSingle_helperSurfaceClosed hkey1 hkey2 hvalue | setStructMember2Single hkey1 _ hkey2 _ hvalue _ _ _ _ => exact supportedStmtList_setStructMember2Single_helperSurfaceClosed hkey1 hkey2 hvalue + | forEachLiteralBounded _ _ ih => + simpa [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedHelperSurface] using ih | requireClause clause _ ih => simp [stmtListTouchesUnsupportedHelperSurface] constructor @@ -3184,6 +3186,10 @@ theorem SupportedStmtList.internalHelperCallNames_nil exprCompileCore_internalHelperCallNames_nil hkey2, exprCompileCore_internalHelperCallNames_nil hvalue, List.nil_append, List.append_nil] + | forEachLiteralBounded _ _ ih => + simpa [stmtListInternalHelperCallNames, + stmtInternalHelperCallNames, + exprInternalHelperCallNames] using ih | requireClause clause _ ih => simp [stmtListInternalHelperCallNames] constructor @@ -5043,6 +5049,9 @@ private theorem supportedStmtList_usesArrayElement_false exprCompileCore_usesArrayElement_false hkey1, exprCompileCore_usesArrayElement_false hkey2, exprCompileCore_usesArrayElement_false hvalue, Bool.false_or] + | forEachLiteralBounded _ _ ih => + simpa [stmtListUsesArrayElement, stmtUsesArrayElement, + exprUsesArrayElement] using ih | requireClause clause _ ih => simp only [stmtListUsesArrayElement, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5157,6 +5166,9 @@ private theorem supportedStmtList_usesStorageArrayElement_false exprCompileCore_usesStorageArrayElement_false hkey1, exprCompileCore_usesStorageArrayElement_false hkey2, exprCompileCore_usesStorageArrayElement_false hvalue, Bool.false_or] + | forEachLiteralBounded _ _ ih => + simpa [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprUsesStorageArrayElement] using ih | requireClause clause _ ih => simp only [stmtListUsesStorageArrayElement, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5264,6 +5276,9 @@ private theorem supportedStmtList_usesDynamicBytesEq_false exprCompileCore_usesDynamicBytesEq_false hkey1, exprCompileCore_usesDynamicBytesEq_false hkey2, exprCompileCore_usesDynamicBytesEq_false hvalue, Bool.false_or] + | forEachLiteralBounded _ _ ih => + simpa [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprUsesDynamicBytesEq] using ih | requireClause clause _ ih => simp only [stmtListUsesDynamicBytesEq, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5629,6 +5644,9 @@ private theorem supportedStmtList_usesMulDiv512_false exprCompileCore_usesMulDiv512_false hkey1, exprCompileCore_usesMulDiv512_false hkey2, exprCompileCore_usesMulDiv512_false hvalue, Bool.false_or] + | forEachLiteralBounded _ _ ih => + simpa [stmtListUsesMulDiv512, stmtUsesMulDiv512, + exprUsesMulDiv512] using ih | requireClause clause _ ih => simp only [stmtListUsesMulDiv512, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5736,6 +5754,9 @@ private theorem supportedStmtList_usesParamDynamicHeadWord_false exprCompileCore_usesParamDynamicHeadWord_false hkey1, exprCompileCore_usesParamDynamicHeadWord_false hkey2, exprCompileCore_usesParamDynamicHeadWord_false hvalue, Bool.false_or] + | forEachLiteralBounded _ _ ih => + simpa [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, + exprUsesParamDynamicHeadWord] using ih | requireClause clause _ ih => simp only [stmtListUsesParamDynamicHeadWord, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => From 2103347e21892a99b1771629761116fa40f4a550 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 25 May 2026 04:32:28 +0100 Subject: [PATCH 05/27] chore: auto-refresh derived artifacts --- PrintAxioms.lean | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/PrintAxioms.lean b/PrintAxioms.lean index 97a38a7fa..77a432e72 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -1989,6 +1989,7 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.FunctionBody.eval_compileRequireFailCond_core_onExpr Compiler.Proofs.IRGeneration.FunctionBody.runtimeStateMatchesIR_setVar_bindValue Compiler.Proofs.IRGeneration.FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + Compiler.Proofs.IRGeneration.FunctionBody.runtimeStateMatchesIR_setVars_irrelevant Compiler.Proofs.IRGeneration.FunctionBody.compileStmt_core_ok Compiler.Proofs.IRGeneration.FunctionBody.runtimeStateMatchesIR_setBothMemory Compiler.Proofs.IRGeneration.FunctionBody.runtimeStateMatchesIR_updateMemoryEvents @@ -1996,6 +1997,7 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.FunctionBody.bindingsExactlyMatchIRVars_setMemory Compiler.Proofs.IRGeneration.FunctionBody.bindingsExactlyMatchIRVarsOnScope_setMemory Compiler.Proofs.IRGeneration.FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + Compiler.Proofs.IRGeneration.FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVars_irrelevant Compiler.Proofs.IRGeneration.FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue Compiler.Proofs.IRGeneration.FunctionBody.encodeEvents_withTransactionContext Compiler.Proofs.IRGeneration.FunctionBody.encodeStorage_withTransactionContext @@ -2592,6 +2594,12 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.execIRStmtWithInternals_log3_of_eval_args Compiler.Proofs.IRGeneration.execIRStmtWithInternals_log4_of_eval_args Compiler.Proofs.IRGeneration.execIRInternalFunctionWithInternals_hides_caller_only_locals + Compiler.Proofs.IRGeneration.execIRStmt_for_init_noncontinue + Compiler.Proofs.IRGeneration.execIRStmt_for_cond_none + Compiler.Proofs.IRGeneration.execIRStmt_for_init_cond_zero + Compiler.Proofs.IRGeneration.execIRStmt_for_body_noncontinue + Compiler.Proofs.IRGeneration.execIRStmt_for_post_noncontinue + Compiler.Proofs.IRGeneration.execIRStmt_for_one_continue Compiler.Proofs.IRGeneration.execIRStmt_stop_succ Compiler.Proofs.IRGeneration.execIRStmt_stop_one_add Compiler.Proofs.IRGeneration.execIRStmt_stop_one_add_add @@ -2828,6 +2836,7 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.SourceSemantics.exists_writeUnindexedEventScratch_of_length_zero Compiler.Proofs.IRGeneration.SourceSemantics.exists_eventScratchMemoryAfterEmit?_of_supported_length Compiler.Proofs.IRGeneration.SourceSemantics.UInt256_size_eq_UINT256_MODULUS + Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_congr -- Compiler.Proofs.IRGeneration.SourceSemantics.evalExpr_literal -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.evalExpr_param -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.evalExpr_localVar -- private @@ -2968,6 +2977,7 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.SourceSemantics.expr_sizeOf_pos -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.stmt_sizeOf_lt_ite_then -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.stmt_sizeOf_lt_ite_else -- private + -- Compiler.Proofs.IRGeneration.SourceSemantics.stmt_sizeOf_lt_forEach_body -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.stmt_sizeOf_lt_cons -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.execStmtListWithHelpers_eq_execStmtList_of_helperSurfaceClosed_inner -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.execStmtWithHelpers_eq_execStmt_of_helperSurfaceClosed_aux -- private @@ -5462,4 +5472,4 @@ end Verity.AxiomAudit Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args ] --- Total: 5167 theorems/lemmas (3587 public, 1580 private, 0 sorry'd) +-- Total: 5177 theorems/lemmas (3596 public, 1581 private, 0 sorry'd) From 221d3691cc66830c9b4c3ae622930456a034f933 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Mon, 25 May 2026 06:09:15 +0200 Subject: [PATCH 06/27] Add forEach source loop helper lemmas --- .../Proofs/IRGeneration/SourceSemantics.lean | 102 ++++++++++++++++++ artifacts/interpreter_feature_matrix.json | 2 +- docs/INTERPRETER_FEATURE_MATRIX.md | 4 +- 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/Compiler/Proofs/IRGeneration/SourceSemantics.lean b/Compiler/Proofs/IRGeneration/SourceSemantics.lean index 770f2cea3..ac7f2eaa8 100644 --- a/Compiler/Proofs/IRGeneration/SourceSemantics.lean +++ b/Compiler/Proofs/IRGeneration/SourceSemantics.lean @@ -563,6 +563,108 @@ def execForEachLoop | .return value next => .return value next | .revert => .revert +@[simp] theorem execForEachLoop_zero + (varName : String) + (runBody : RuntimeState → StmtResult) + (state : RuntimeState) + (index : Nat) : + execForEachLoop varName runBody state index 0 = .continue state := rfl + +theorem execForEachLoop_succ + (varName : String) + (runBody : RuntimeState → StmtResult) + (state : RuntimeState) + (index remaining : Nat) : + execForEachLoop varName runBody state index (remaining + 1) = + let loopState := + { state with bindings := bindValue state.bindings varName (wordNormalize index) } + match runBody loopState with + | .continue next => execForEachLoop varName runBody next (index + 1) remaining + | .stop next => .stop next + | .return value next => .return value next + | .revert => .revert := rfl + +@[simp] theorem lookupBinding?_bindValue_same + (bindings : List (String × Nat)) + (name : String) + (value : Nat) : + lookupBinding? (bindValue bindings name value) name = some value := by + simp [lookupBinding?, bindValue] + +@[simp] theorem lookupValue_bindValue_same + (bindings : List (String × Nat)) + (name : String) + (value : Nat) : + lookupValue (bindValue bindings name value) name = value := by + simp [lookupValue, bindValue] + +@[simp] theorem execForEachLoop_boundState_lookupBinding? + (varName : String) + (state : RuntimeState) + (index : Nat) : + lookupBinding? + (bindValue state.bindings varName (wordNormalize index)) + varName = + some (wordNormalize index) := by + simp + +@[simp] theorem execForEachLoop_boundState_lookupValue + (varName : String) + (state : RuntimeState) + (index : Nat) : + lookupValue + (bindValue state.bindings varName (wordNormalize index)) + varName = + wordNormalize index := by + simp + +theorem execForEachLoop_zero_continue_state + {varName : String} + {runBody : RuntimeState → StmtResult} + {state final : RuntimeState} + {index : Nat} + (hloop : execForEachLoop varName runBody state index 0 = .continue final) : + final = state := by + simpa [execForEachLoop] using hloop.symm + +theorem execForEachLoop_succ_continue_iff + {varName : String} + {runBody : RuntimeState → StmtResult} + {state final : RuntimeState} + {index remaining : Nat} : + execForEachLoop varName runBody state index (remaining + 1) = .continue final ↔ + ∃ next, + runBody + { state with + bindings := bindValue state.bindings varName (wordNormalize index) } = + .continue next ∧ + execForEachLoop varName runBody next (index + 1) remaining = + .continue final := by + simp only [execForEachLoop] + cases hbody : + runBody + { state with + bindings := bindValue state.bindings varName (wordNormalize index) } <;> + simp [hbody] + +theorem execForEachLoop_succ_continue + {varName : String} + {runBody : RuntimeState → StmtResult} + {state next final : RuntimeState} + {index remaining : Nat} + (hbody : + runBody + { state with + bindings := bindValue state.bindings varName (wordNormalize index) } = + .continue next) + (hloop : + execForEachLoop varName runBody next (index + 1) remaining = + .continue final) : + execForEachLoop varName runBody state index (remaining + 1) = + .continue final := by + rw [execForEachLoop_succ] + simpa only [hbody] using hloop + theorem execForEachLoop_congr {varName : String} {runBodyA runBodyB : RuntimeState → StmtResult} diff --git a/artifacts/interpreter_feature_matrix.json b/artifacts/interpreter_feature_matrix.json index 816b29f4a..b706e6f99 100644 --- a/artifacts/interpreter_feature_matrix.json +++ b/artifacts/interpreter_feature_matrix.json @@ -535,7 +535,7 @@ "SpecInterpreter_basic": "unsupported", "SpecInterpreter_fuel": "supported", "IRInterpreter": "supported", - "proof_status": "proved" + "proof_status": "partial" }, { "feature": "emit", diff --git a/docs/INTERPRETER_FEATURE_MATRIX.md b/docs/INTERPRETER_FEATURE_MATRIX.md index 62747fe19..c4d278bab 100644 --- a/docs/INTERPRETER_FEATURE_MATRIX.md +++ b/docs/INTERPRETER_FEATURE_MATRIX.md @@ -100,7 +100,7 @@ Legend: **ok** = supported, **0** = returns 0 (not modeled), **del** = delegated | Return (storage words) | `Stmt.returnStorageWords` | ok | ok | -- | proved | | Stop | `Stmt.stop` | ok | ok | ok | proved | | If/else | `Stmt.ite` | ok | ok | ok | proved | -| For-each loop | `Stmt.forEach` | **rev** | ok | ok | proved | +| For-each loop | `Stmt.forEach` | **rev** | ok | ok | partial | | Event emission | `Stmt.emit` | ok | ok | -- | proved | | Internal call (stmt) | `Stmt.internalCall` | **rev** | ok | -- | proved | | Internal call assign | `Stmt.internalCallAssign` | **rev** | ok | -- | proved | @@ -188,7 +188,7 @@ Legend: **ok** = native evaluation. | Category | Proved | Assumed | Partial | Not Modeled | |---|---|---|---|---| | Expression features | 24 | 1 (`externalCall`) | 6 | 6 (`keccak256`, `call`, `staticcall`, `delegatecall`, `arrayElementDynamicWord`, `paramDynamicHeadWord`) | -| Statement features | 25 | 0 | 1 (`mstore`) | 6 (`calldatacopy`, `returndataCopy`, `revertReturndata`, `rawLog`, `externalCallBind`, `ecm`) | +| Statement features | 24 | 0 | 2 (`forEach`, `mstore`) | 6 (`calldatacopy`, `returndataCopy`, `revertReturndata`, `rawLog`, `externalCallBind`, `ecm`) | | Builtins (agreement) | 36 | 0 | 0 | 0 (delegated) | Proof-boundary features split across two buckets. Partially modeled features currently include runtime introspection (`blockNumber`, `contractAddress`, `chainid`) and single-word linear-memory forms (`mload`, `mstore`, `returndataOptionalBoolAt`). `selfBalance` is also partially modeled: it is compiler-supported and source-executable, but not yet included in the generic proof interpreter fragment. Fully not-modeled features currently include `keccak256`, low-level call / returndata plumbing (`call`, `staticcall`, `delegatecall`, `calldatacopy`, `returndataCopy`, `revertReturndata`), event emission (`rawLog`), and external call modules (`externalCallBind`, `ecm`). Dynamic struct-array head-word decoding (`arrayElementDynamicWord`) and direct dynamic-struct parameter head-word decoding (`paramDynamicHeadWord`) are also not modeled by proof interpreters yet. These features are still compiler-supported and are validated by differential testing (70,000+ test vectors against actual EVM execution). From 89c0317e0015358b4cdb1e6d46bd837141625b9f Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Mon, 25 May 2026 09:59:52 +0200 Subject: [PATCH 07/27] Add reusable Yul for-loop execution lemmas --- .../Proofs/IRGeneration/IRInterpreter.lean | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index afdcf8a0e..01a8376db 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -1057,6 +1057,130 @@ theorem execIRStmt_for_one_continue simp only [execIRStmt, hinit, hcond] simp [hcondNZ, hbody, hpost] +/-- Singleton-list form of `execIRStmt_for_init_cond_zero`. + +This is the shape needed when a compiled source statement is represented by a +single Yul `for` statement inside `execIRStmts`. -/ +theorem execIRStmts_single_for_init_cond_zero + (fuel : Nat) (state sInit : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some 0) : + execIRStmts (Nat.succ (Nat.succ fuel)) state [.for_ init cond post body] = + .continue sInit := by + simp only [execIRStmts] + rw [execIRStmt_for_init_cond_zero fuel state sInit init post body cond hinit hcond] + +/-- Head/tail form of `execIRStmt_for_init_cond_zero`. + +After the `for` condition evaluates to zero, statement-list execution continues +with the tail from the state produced by the init block. -/ +theorem execIRStmts_cons_for_init_cond_zero + (fuel : Nat) (state sInit : IRState) + (init post body tail : List YulStmt) (cond : YulExpr) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some 0) : + execIRStmts (Nat.succ (Nat.succ fuel)) state + (.for_ init cond post body :: tail) = + execIRStmts (Nat.succ fuel) sInit tail := by + simp only [execIRStmts] + rw [execIRStmt_for_init_cond_zero fuel state sInit init post body cond hinit hcond] + +/-- Singleton-list form of one successful `for` iteration. + +The init block, first condition check, body, and post block are discharged, and +the remaining execution is exactly the recursive empty-init loop. -/ +theorem execIRStmts_single_for_one_continue + (fuel : Nat) (state sInit sBody sPost : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (condValue : Nat) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some condValue) + (hcondNZ : condValue ≠ 0) + (hbody : execIRStmts fuel sInit body = .continue sBody) + (hpost : execIRStmts fuel sBody post = .continue sPost) : + execIRStmts (Nat.succ (Nat.succ fuel)) state [.for_ init cond post body] = + match execIRStmt fuel sPost (.for_ [] cond post body) with + | .continue sLoop => .continue sLoop + | .return value sLoop => .return value sLoop + | .stop sLoop => .stop sLoop + | .revert sLoop => .revert sLoop := by + simp only [execIRStmts] + rw [execIRStmt_for_one_continue fuel state sInit sBody sPost init post body cond + condValue hinit hcond hcondNZ hbody hpost] + +/-- Head/tail form of one successful `for` iteration. + +This threads the tail through the recursive empty-init loop produced after the +first successful body/post execution. -/ +theorem execIRStmts_cons_for_one_continue + (fuel : Nat) (state sInit sBody sPost : IRState) + (init post body tail : List YulStmt) (cond : YulExpr) + (condValue : Nat) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some condValue) + (hcondNZ : condValue ≠ 0) + (hbody : execIRStmts fuel sInit body = .continue sBody) + (hpost : execIRStmts fuel sBody post = .continue sPost) : + execIRStmts (Nat.succ (Nat.succ fuel)) state + (.for_ init cond post body :: tail) = + match execIRStmt fuel sPost (.for_ [] cond post body) with + | .continue sLoop => execIRStmts (Nat.succ fuel) sLoop tail + | .return value sLoop => .return value sLoop + | .stop sLoop => .stop sLoop + | .revert sLoop => .revert sLoop := by + simp only [execIRStmts] + rw [execIRStmt_for_one_continue fuel state sInit sBody sPost init post body cond + condValue hinit hcond hcondNZ hbody hpost] + +/-- Convenient continuation-only corollary of `execIRStmts_cons_for_one_continue`. + +Use this when the recursive empty-init loop is known to finish with `.continue`; +the list tail then resumes from that state. -/ +theorem execIRStmts_cons_for_one_continue_of_loop_continue + (fuel : Nat) (state sInit sBody sPost sLoop : IRState) + (init post body tail : List YulStmt) (cond : YulExpr) + (condValue : Nat) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : evalIRExpr sInit cond = some condValue) + (hcondNZ : condValue ≠ 0) + (hbody : execIRStmts fuel sInit body = .continue sBody) + (hpost : execIRStmts fuel sBody post = .continue sPost) + (hloop : execIRStmt fuel sPost (.for_ [] cond post body) = .continue sLoop) : + execIRStmts (Nat.succ (Nat.succ fuel)) state + (.for_ init cond post body :: tail) = + execIRStmts (Nat.succ fuel) sLoop tail := by + rw [execIRStmts_cons_for_one_continue fuel state sInit sBody sPost init post body + tail cond condValue hinit hcond hcondNZ hbody hpost, hloop] + +/-- forEach-shaped zero-condition lemma. + +The statement syntax is specialized to the compiler's current forEach loop +layout: init statements, `lt(idx, count)`, `idx := add(idx, 1)`, and a leading +assignment of the element variable before the compiled body. The semantic facts +remain explicit hypotheses so callers can provide them from their local loop +invariants. -/ +theorem execIRStmt_forEach_shape_init_cond_zero + (fuel : Nat) (state sInit : IRState) + (init bodyIR : List YulStmt) + (idxName countName varName : String) + (hinit : execIRStmts fuel state init = .continue sInit) + (hcond : + evalIRExpr sInit + (.call "lt" [.ident idxName, .ident countName]) = some 0) : + execIRStmt (Nat.succ fuel) state + (.for_ + init + (.call "lt" [.ident idxName, .ident countName]) + [.assign idxName (.call "add" [.ident idxName, .lit 1])] + (.assign varName (.ident idxName) :: bodyIR)) = + .continue sInit := by + exact execIRStmt_for_init_cond_zero fuel state sInit init + [.assign idxName (.call "add" [.ident idxName, .lit 1])] + (.assign varName (.ident idxName) :: bodyIR) + (.call "lt" [.ident idxName, .ident countName]) + hinit hcond + @[simp] theorem execIRStmt_stop_succ (fuel : Nat) (state : IRState) : execIRStmt (Nat.succ fuel) state (YulStmt.expr (YulExpr.call "stop" [])) = .stop state := by From cba7f1a68abc2c4471e49367d495cc26e87cbfa4 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Mon, 25 May 2026 10:42:17 +0200 Subject: [PATCH 08/27] Add forEach zero-init execution lemma --- Compiler/Proofs/IRGeneration/IRInterpreter.lean | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index 01a8376db..9a6e5d131 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -1181,6 +1181,21 @@ theorem execIRStmt_forEach_shape_init_cond_zero (.call "lt" [.ident idxName, .ident countName]) hinit hcond +/-- The three initializer statements emitted for a zero-literal `forEach` +execute to the expected cached-counter state once enough fuel is available. -/ +theorem execIRStmts_forEach_init_literal_zero + (fuel : Nat) (state : IRState) + (idxName countName varName : String) + (hfuel : 4 ≤ fuel) : + execIRStmts fuel state + [ YulStmt.let_ idxName (YulExpr.lit 0) + , YulStmt.let_ countName (YulExpr.lit 0) + , YulStmt.let_ varName (YulExpr.lit 0) ] = + .continue (((state.setVar idxName 0).setVar countName 0).setVar varName 0) := by + rcases Nat.exists_eq_add_of_le hfuel with ⟨extra, rfl⟩ + rw [Nat.add_comm] + simp [execIRStmts, execIRStmt, evalIRExpr] + @[simp] theorem execIRStmt_stop_succ (fuel : Nat) (state : IRState) : execIRStmt (Nat.succ fuel) state (YulStmt.expr (YulExpr.call "stop" [])) = .stop state := by From 4c135e0e13e35b76b6bd19f3518526dd5d890ee1 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 04:15:46 +0200 Subject: [PATCH 09/27] Prove zero-bound forEach preservation --- .../Proofs/IRGeneration/GenericInduction.lean | 443 +++++++++++++++++- .../IRGeneration/SupportedFragment.lean | 3 +- .../Proofs/IRGeneration/SupportedSpec.lean | 18 +- 3 files changed, 446 insertions(+), 18 deletions(-) diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 2b74f3ca8..f800c5998 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -4,6 +4,7 @@ import Compiler.CompilationModel.ValidationCalls import Compiler.Proofs.IRGeneration.FunctionBody import Compiler.Proofs.IRGeneration.IRInterpreter import Compiler.Proofs.IRGeneration.SupportedSpec +import Compiler.Proofs.YulGeneration.Backends.EvmYulLeanBridgeLemmas set_option linter.unnecessarySeqFocus false set_option linter.unnecessarySimpa false @@ -231,6 +232,31 @@ inductive StmtListGenericCore (fields : List Field) : List String → List Stmt StmtListGenericCore fields (stmtNextScope scope stmt) rest → StmtListGenericCore fields scope (stmt :: rest) +private theorem compileStmtList_ok_of_stmtListGenericCore_early + {fields : List Field} + {scope inScopeNames : List String} + {stmts : List Stmt} + (hgeneric : StmtListGenericCore fields scope stmts) + (hincluded : FunctionBody.scopeNamesIncluded scope inScopeNames) : + ∃ bodyIR, + CompilationModel.compileStmtList + fields [] [] .calldata [] false inScopeNames [] stmts = Except.ok bodyIR := by + induction hgeneric generalizing inScopeNames with + | nil => exact ⟨[], rfl⟩ + | cons hstep _hrest ih => + rcases FunctionBody.compileStmt_ok_any_scope + (scope2 := inScopeNames) ⟨_, hstep.compileOk⟩ with ⟨headIR, hhead⟩ + rcases ih (inScopeNames := collectStmtNames _ ++ inScopeNames) + (by + intro name hmem + simp [stmtNextScope] at hmem + rcases hmem with h | h + · exact List.mem_append_left _ h + · exact List.mem_append_right _ (hincluded name h)) + with ⟨tailIR, htail⟩ + exact ⟨headIR ++ tailIR, + FunctionBody.compileStmtList_cons_ok_of_compileStmt_ok hhead htail⟩ + /-- Weaker source-side reuse witness for the future helper-rich induction path: only helper-surface-closed heads must come with the existing helper-free generic step proof. Helper-surface-positive heads can instead be discharged by a @@ -1010,6 +1036,52 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS hthenLegacy (.if_ _ elseIR [] helseLegacy .nil))) .nil + | forEach varName count body => + cases count with + | literal n => + cases n with + | zero => + have hbodySurface : + stmtListTouchesUnsupportedContractSurface body = false := by + simpa [stmtTouchesUnsupportedContractSurface] using hsurface + simp only [CompilationModel.compileStmt, bind, Except.bind] at hcompile + cases hbody : + CompilationModel.compileStmtList fields events errors .calldata [] false + (varName :: inScopeNames) [] body with + | error e => simp [CompilationModel.compileExpr, pure, Except.pure, hbody] at hcompile + | ok loopBodyIR => + simp [CompilationModel.compileExpr, hbody] at hcompile + cases hcompile + let forUsedNames := + varName :: (inScopeNames ++ collectExprNames (Expr.literal 0) ++ collectStmtListNames body) + let idxName := pickFreshName "__forEach_idx" forUsedNames + let countName := pickFreshName "__forEach_count" (idxName :: forUsedNames) + let initStmts := [ + YulStmt.let_ idxName (YulExpr.lit 0), + YulStmt.let_ countName (YulExpr.lit 0), + YulStmt.let_ varName (YulExpr.lit 0) + ] + let condExpr := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName] + let postStmts := [YulStmt.assign idxName (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + let bodyWithBind := YulStmt.assign varName (YulExpr.ident idxName) :: loopBodyIR + simpa [forUsedNames, idxName, countName, initStmts, condExpr, postStmts, + bodyWithBind] using + (LegacyCompatibleExternalStmtList.for_ initStmts condExpr postStmts bodyWithBind [] + (LegacyCompatibleExternalStmtList.let_ idxName (YulExpr.lit 0) _ + (LegacyCompatibleExternalStmtList.let_ countName (YulExpr.lit 0) _ + (LegacyCompatibleExternalStmtList.let_ varName (YulExpr.lit 0) _ + LegacyCompatibleExternalStmtList.nil))) + (LegacyCompatibleExternalStmtList.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1]) _ + LegacyCompatibleExternalStmtList.nil) + (LegacyCompatibleExternalStmtList.assign varName (YulExpr.ident idxName) loopBodyIR <| + legacyCompatibleExternalStmtList_of_compileStmtList_ok_on_supportedContractSurface + hnoPacked hbodySurface hbody) + LegacyCompatibleExternalStmtList.nil) + | succ n => + simp [stmtTouchesUnsupportedContractSurface] at hsurface + | _ => + simp [stmtTouchesUnsupportedContractSurface] at hsurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hsurface termination_by sizeOf stmt @@ -3127,6 +3199,10 @@ inductive StmtListScopeDiscipline (fieldNames : List String) : List String → L StmtListScopeDiscipline fieldNames scope elseBranch → StmtListScopeDiscipline fieldNames (stmtNextScope scope (.ite cond thenBranch elseBranch)) rest → StmtListScopeDiscipline fieldNames scope (.ite cond thenBranch elseBranch :: rest) + | forEachLiteralZero {scope : List String} {varName : String} {body rest : List Stmt} : + StmtListScopeDiscipline fieldNames (varName :: scope) body → + StmtListScopeDiscipline fieldNames (stmtNextScope scope (.forEach varName (.literal 0) body)) rest → + StmtListScopeDiscipline fieldNames scope (.forEach varName (.literal 0) body :: rest) /-- Syntax-side witness for the current generic statement fragment, before the scope obligations are discharged from identifier validation. -/ @@ -3183,6 +3259,10 @@ inductive StmtListScopeCore (fieldNames : List String) : List Stmt → Prop wher StmtListScopeCore fieldNames elseBranch → StmtListScopeCore fieldNames rest → StmtListScopeCore fieldNames (.ite cond thenBranch elseBranch :: rest) + | forEachLiteralZero {varName : String} {body rest : List Stmt} : + StmtListScopeCore fieldNames body → + StmtListScopeCore fieldNames rest → + StmtListScopeCore fieldNames (.forEach varName (.literal 0) body :: rest) private theorem exprCompileCore_of_exprTouchesUnsupportedContractSurface_eq_false {expr : Expr} @@ -3469,6 +3549,28 @@ private theorem stmtListScopeCore_of_unsupportedContractSurface_eq_false fields scope thenBranch thenIR hstmtSurface.1.2 hthenCompile) (stmtListScopeCore_of_unsupportedContractSurface_eq_false fields scope elseBranch elseIR hstmtSurface.2 helseCompile) ihRest + | forEach varName count body => + cases count with + | literal n => + cases n with + | zero => + have hbodySurface : + stmtListTouchesUnsupportedContractSurface body = false := by + simpa [stmtTouchesUnsupportedContractSurface] using hstmtSurface + simp only [CompilationModel.compileStmt, bind, Except.bind] at hhead + cases hbody : + CompilationModel.compileStmtList fields [] [] .calldata [] false + (varName :: scope) [] body with + | error e => simp [CompilationModel.compileExpr, pure, Except.pure, hbody] at hhead + | ok loopBodyIR => + exact .forEachLiteralZero + (stmtListScopeCore_of_unsupportedContractSurface_eq_false + fields (varName :: scope) body loopBodyIR hbodySurface hbody) + ihRest + | succ n => + simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface + | _ => + simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface termination_by sizeOf stmts @@ -3572,6 +3674,28 @@ theorem stmtListScopeCore_prefix_of_compileStmtList_ok_of_stmtListTouchesUnsuppo (stmtListScopeCore_of_unsupportedContractSurface_eq_false fields scope elseBranch elseIR hstmtSurface.2 helseCompile) (ih hrestSurface htail) + | forEach varName count body => + cases count with + | literal n => + cases n with + | zero => + have hbodySurface : + stmtListTouchesUnsupportedContractSurface body = false := by + simpa [stmtTouchesUnsupportedContractSurface] using hstmtSurface + simp only [CompilationModel.compileStmt, bind, Except.bind] at hhead + cases hbody : + CompilationModel.compileStmtList fields [] [] .calldata [] false + (varName :: scope) [] body with + | error e => simp [CompilationModel.compileExpr, pure, Except.pure, hbody] at hhead + | ok loopBodyIR => + exact StmtListScopeCore.forEachLiteralZero + (stmtListScopeCore_of_unsupportedContractSurface_eq_false + fields (varName :: scope) body loopBodyIR hbodySurface hbody) + (ih hrestSurface htail) + | succ n => + simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface + | _ => + simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface | setMapping _ _ _ | setMappingWord _ _ _ _ | setMappingPackedWord _ _ _ _ _ | setMapping2 _ _ _ _ | setMapping2Word _ _ _ _ _ | setMappingUint _ _ _ | setMappingChain _ _ _ @@ -3579,7 +3703,7 @@ theorem stmtListScopeCore_prefix_of_compileStmtList_ok_of_stmtListTouchesUnsuppo | storageArrayPush _ _ | storageArrayPop _ | setStorageArrayElement _ _ _ | requireError _ _ _ | revertError _ _ | returnValues _ | returnArray _ | returnBytes _ | returnStorageWords _ | calldatacopy _ _ _ - | returndataCopy _ _ _ | revertReturndata | forEach _ _ _ + | returndataCopy _ _ _ | revertReturndata | emit _ _ | internalCall _ _ | internalCallAssign _ _ _ | rawLog _ _ _ | externalCallBind _ _ _ | tryExternalCallBind _ _ _ _ | ecm _ _ | unsafeBlock _ _ | matchAdt _ _ _ => @@ -4312,7 +4436,42 @@ private theorem stmtListScopeDiscipline_of_validateScopedStmtListIdentifiers (by intro other hmem exact mem_stmtNextScope_of_mem_scope (hlocalsInScope other hmem))) - + | forEachLiteralZero hbodyCore hrestCore ihBody ihRest => + rcases validateScopedStmtListIdentifiers_cons_ok_inv hvalidate with + ⟨nextLocalScope, hstmt, hrestValidate⟩ + have hstmt' := hstmt + unfold validateScopedStmtIdentifiers at hstmt' + revert hstmt' + simp only [bind, Except.bind, pure, Except.pure] + intro hstmt' + rcases hCountVal : + validateScopedExprIdentifiers context params paramScope dynamicParams localScope + constructorArgCount (Expr.literal 0) with _ | _ + · rw [hCountVal] at hstmt'; simp at hstmt' + · rw [hCountVal] at hstmt' + rcases hBodyVal : + validateScopedStmtListIdentifiers context params paramScope dynamicParams + (_ :: localScope) constructorArgCount _ with _ | _ + · rw [hBodyVal] at hstmt'; simp at hstmt' + · rw [hBodyVal] at hstmt'; simp at hstmt'; cases hstmt' + exact StmtListScopeDiscipline.forEachLiteralZero + (ihBody hBodyVal + (by + intro other hmem + simp [hparamsInScope other hmem]) + (by + intro other hmem + simp at hmem + rcases hmem with h | h + · simp [h] + · simp [hlocalsInScope other h])) + (ihRest hrestValidate + (by + intro other hmem + exact mem_stmtNextScope_of_mem_scope (hparamsInScope other hmem)) + (by + intro other hmem + exact mem_stmtNextScope_of_mem_scope (hlocalsInScope other hmem))) theorem stmtListScopeDiscipline_of_validateFunctionIdentifierReferences_prefix {spec : FunctionSpec} {fieldNames : List String} @@ -4580,6 +4739,33 @@ private theorem scopeNamesPresent_foldl_stmtNextScope_of_validateScopedStmtListI intro name hname exact mem_stmtNextScope_of_mem_scope (hlocalsInScope name hname)) other hmem + | forEachLiteralZero hbodyCore hrestCore ihBody ihRest => + rcases validateScopedStmtListIdentifiers_cons_ok_inv hvalidate with + ⟨nextLocalScope, hstmt, hrestValidate⟩ + have hstmt' := hstmt + unfold validateScopedStmtIdentifiers at hstmt' + revert hstmt' + simp only [bind, Except.bind, pure, Except.pure] + intro hstmt' + rcases hCountVal : + validateScopedExprIdentifiers context params paramScope dynamicParams localScope + constructorArgCount (Expr.literal 0) with _ | _ + · rw [hCountVal] at hstmt'; simp at hstmt' + · rw [hCountVal] at hstmt' + rcases hBodyVal : + validateScopedStmtListIdentifiers context params paramScope dynamicParams + (_ :: localScope) constructorArgCount _ with _ | _ + · rw [hBodyVal] at hstmt'; simp at hstmt' + · rw [hBodyVal] at hstmt'; simp at hstmt'; cases hstmt' + intro other hmem + exact ihRest hrestValidate + (by + intro name hname + exact mem_stmtNextScope_of_mem_scope (hparamsInScope name hname)) + (by + intro name hname + exact mem_stmtNextScope_of_mem_scope (hlocalsInScope name hname)) + other hmem private theorem exprBoundNamesInScope_setStorage_of_validateFunctionIdentifierReferences {spec : FunctionSpec} @@ -4858,6 +5044,31 @@ private theorem stmtListScopeDiscipline_scope_names · left; left; right; right; exact hbind · left; right; right; exact hassign · right; exact hfield + | @forEachLiteralZero scope varName body rest _ _ ihBody ihRest => + intro other hmem + simp only [List.foldl] at hmem + have htail := ihRest other hmem + simp [stmtNextScope, collectStmtNames, collectExprNames, + collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames] at htail + rcases htail with hvar | hbodyName | hscope | hbindRest | hassignRest | hfield + · simp [collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames, hvar] + · + have hmemFoldl := stmtListNames_subset_foldl_stmtNextScope + (scope := varName :: scope) hbodyName + have hbodyResult := ihBody other hmemFoldl + simp [collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames] at hbodyResult ⊢ + tauto + · simp [collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames, hscope] + · simp [collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames, hbindRest] + · simp [collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames, hassignRest] + · simp [collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames, hfield] theorem compiledStmtStep_letVar {fields : List Field} @@ -11905,7 +12116,7 @@ private theorem stmtListTouchesUnsupportedContractSurface_append | nil => simp [stmtListTouchesUnsupportedContractSurface] | cons stmt rest ih => - simp [stmtListTouchesUnsupportedContractSurface, ih, Bool.or_assoc] + cases stmt <;> simp [stmtListTouchesUnsupportedContractSurface, ih, Bool.or_assoc] private theorem stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites_append {«prefix» «suffix» : List Stmt} : @@ -11918,6 +12129,26 @@ private theorem stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites_app | cons stmt rest ih => simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, ih, Bool.or_assoc] +private theorem stmtTouchesUnsupportedContractSurfaceExceptMappingWrites_eq_false_of_contractSurface + {stmt : Stmt} + (hsurface : stmtTouchesUnsupportedContractSurface stmt = false) : + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites stmt = false := by + cases stmt <;> simp [stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurface] at hsurface ⊢ + all_goals assumption + +private theorem stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites_eq_false_of_contractSurface + {stmts : List Stmt} + (hsurface : stmtListTouchesUnsupportedContractSurface stmts = false) : + stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites stmts = false := by + induction stmts with + | nil => simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites] + | cons stmt rest ih => + have hsplit := Bool.or_eq_false_iff.mp hsurface + simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites_eq_false_of_contractSurface hsplit.1, + ih hsplit.2] + private theorem stmtListCompileCore_of_requireLiteralGuardFamilyClauses {scope : List String} (clauses : List Verity.Core.Free.RequireLiteralGuardFamilyClause) : @@ -12476,6 +12707,177 @@ private theorem stmtListGenericCore_of_supportedStmtList_tstoreSingle_of_surface (hcoreValue := hcoreValue) (hinScopeValue := hinScopeValue) +private theorem compiledStmtStep_forEach_literal_zero + {fields : List Field} + {scope : List String} + {varName : String} + {body : List Stmt} + (hbodyNames : ∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) + (hbodyGeneric : StmtListGenericCore fields (varName :: scope) body) : + ∃ compiledIR, + CompiledStmtStep fields scope (Stmt.forEach varName (Expr.literal 0) body) compiledIR := by + rcases compileStmtList_ok_of_stmtListGenericCore_early + (fields := fields) + (scope := varName :: scope) + (inScopeNames := varName :: scope) + hbodyGeneric + FunctionBody.scopeNamesIncluded_refl with + ⟨bodyIR, hbodyCompile⟩ + let forUsedNames := varName :: (scope ++ collectExprNames (Expr.literal 0) ++ collectStmtListNames body) + let idxName := pickFreshName "__forEach_idx" forUsedNames + let countName := pickFreshName "__forEach_count" (idxName :: forUsedNames) + let initStmts := [ + YulStmt.let_ idxName (YulExpr.lit 0), + YulStmt.let_ countName (YulExpr.lit 0), + YulStmt.let_ varName (YulExpr.lit 0) + ] + let condExpr := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName] + let postStmts := [YulStmt.assign idxName (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + let bodyWithBind := YulStmt.assign varName (YulExpr.ident idxName) :: bodyIR + let compiledIR := [YulStmt.for_ initStmts condExpr postStmts bodyWithBind] + refine ⟨compiledIR, ?_⟩ + refine + { compileOk := ?_ + preserves := ?_ } + · dsimp [compiledIR, initStmts, condExpr, postStmts, bodyWithBind, idxName, countName, + forUsedNames] + simp [CompilationModel.compileStmt, CompilationModel.compileExpr, hbodyCompile] + · intro runtime state extraFuel hexact hscope hbounded hruntime hslack + let stateIdx := state.setVar idxName 0 + let stateCount := stateIdx.setVar countName 0 + let stateLoop := stateCount.setVar varName 0 + let runtimeLoop : SourceSemantics.RuntimeState := + { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } + have hsource : + SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 0) body) = + .continue runtimeLoop := by + change + SourceSemantics.execForEachLoop varName + (fun loopState => SourceSemantics.execStmtList fields loopState body) + runtimeLoop 0 0 = .continue runtimeLoop + rfl + have hidxFreshUsed : idxName ∉ forUsedNames := by + simpa [idxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" forUsedNames + have hcountFreshUsed : countName ∉ idxName :: forUsedNames := by + simpa [countName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" (idxName :: forUsedNames) + have hidx_ne_var : idxName ≠ varName := by + intro h + apply hidxFreshUsed + simp [forUsedNames, h] + have hcount_ne_var : countName ≠ varName := by + intro h + apply hcountFreshUsed + simp [forUsedNames, h] + have hcount_ne_idx : countName ≠ idxName := by + intro h + apply hcountFreshUsed + simp [h] + have hidx_not_scope : idxName ∉ scope := by + intro hmem + apply hidxFreshUsed + simp [forUsedNames, hmem] + have hcount_not_scope : countName ∉ scope := by + intro hmem + apply hcountFreshUsed + simp [forUsedNames, hmem] + have hfuelInit : 4 ≤ extraFuel := by + have hmin : 4 ≤ sizeOf compiledIR - compiledIR.length := by + dsimp [compiledIR, initStmts, condExpr, postStmts, bodyWithBind] + simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, + YulStmt.for_.sizeOf_spec, YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, + YulExpr.call.sizeOf_spec, YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] + omega + omega + have hinit : + execIRStmts extraFuel state initStmts = .continue stateLoop := by + simpa [initStmts, stateIdx, stateCount, stateLoop] using + execIRStmts_forEach_init_literal_zero + (fuel := extraFuel) (state := state) (idxName := idxName) + (countName := countName) (varName := varName) (hfuel := hfuelInit) + have hcond : evalIRExpr stateLoop condExpr = some 0 := by + have hidx_after : stateLoop.getVar idxName = some 0 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 (by exact hidx_ne_var)] + rw [FunctionBody.getVar_setVar_ne _ countName idxName 0 (by exact hcount_ne_idx.symm)] + simp + have hcount_after : stateLoop.getVar countName = some 0 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 (by exact hcount_ne_var)] + simp + simp [condExpr, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after, hcount_after] + have hir : + execIRStmts (compiledIR.length + extraFuel + 1) state compiledIR = + .continue stateLoop := by + dsimp [compiledIR] + have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega + rw [← hfuelEq] + exact + (execIRStmts_single_for_init_cond_zero + (fuel := extraFuel) + (state := state) + (sInit := stateLoop) + (init := initStmts) + (post := postStmts) + (body := bodyWithBind) + (cond := condExpr) + hinit + hcond) + have hruntimeIdx : FunctionBody.runtimeStateMatchesIR fields runtime stateIdx := + FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime + have hruntimeCount : FunctionBody.runtimeStateMatchesIR fields runtime stateCount := + FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntimeIdx + have hruntimeLoop : FunctionBody.runtimeStateMatchesIR fields runtimeLoop stateLoop := by + simpa [runtimeLoop, stateLoop] using + FunctionBody.runtimeStateMatchesIR_setVar_bindValue + (fields := fields) (runtime := runtime) (state := stateCount) + hruntimeCount varName 0 + have hexactIdx : + FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateIdx := + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope + have hexactCount : + FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateCount := + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexactIdx hcount_not_scope + have hexactBase : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) runtimeLoop.bindings stateLoop := by + simpa [runtimeLoop, stateLoop] using + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue + (boundName := varName) (value := 0) hexactCount + have hNextScopeIncl : + FunctionBody.scopeNamesIncluded + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) + (varName :: scope) := by + intro name hmem + simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem + rcases hmem with hvar | hbody | hscopeMem + · simp [hvar] + · exact hbodyNames name hbody + · simp [hscopeMem] + have hexactLoop : + FunctionBody.bindingsExactlyMatchIRVarsOnScope + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) + runtimeLoop.bindings stateLoop := + FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactBase hNextScopeIncl + have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := + FunctionBody.bindingsBounded_bindValue hbounded varName 0 + (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) + have hscopeBase : + FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by + simpa [runtimeLoop] using + FunctionBody.scopeNamesPresent_cons_bindValue + (boundName := varName) (value := 0) hscope + have hscopeLoop : + FunctionBody.scopeNamesPresent + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) + runtimeLoop.bindings := + FunctionBody.scopeNamesPresent_of_included hscopeBase hNextScopeIncl + refine ⟨.continue runtimeLoop, .continue stateLoop, hsource, hir, ?_⟩ + simp [stmtStepMatchesIRExec] + exact ⟨hruntimeLoop, hexactLoop, hboundedLoop, hscopeLoop⟩ + /-- Extra Tier 2 assumptions needed to turn the singleton mapping-write constructors in `SupportedStmtList` into real compiled-step proofs. These are @@ -14055,9 +14457,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface exact False.elim (false_of_supportedStmtList_setMapping2WordSingle_surface hsurface) | setStructMember2Single hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hslot hmembers hmember => exact False.elim (false_of_supportedStmtList_setStructMember2Single_surface hsurface) - | forEachLiteralBounded _ _ _ => - simp [stmtListTouchesUnsupportedContractSurface, - stmtTouchesUnsupportedContractSurface] at hsurface + | forEachLiteralBounded hbodyNames _ ih => + rcases compiledStmtStep_forEach_literal_zero hbodyNames (ih (by + simpa [stmtListTouchesUnsupportedContractSurface, + stmtTouchesUnsupportedContractSurface] using hsurface)) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => simp [stmtListTouchesUnsupportedContractSurface] at hsurface apply stmtListGenericCore_append @@ -14186,10 +14591,15 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites hvalue hscopeValue hslot hmembers hmember with ⟨hm, hws, hss⟩ exact stmtListGenericCore_singleton_setStructMember2Single_of_slotSafety hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hm hmembers hmember hws hss - | forEachLiteralBounded _ _ _ => - simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurface] at hsurface + | forEachLiteralBounded hbodyNames _ ih => + rcases compiledStmtStep_forEach_literal_zero hbodyNames + (ih (by + exact stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites_eq_false_of_contractSurface + (by simpa [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurface] using hsurface))) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause ih hsurface @@ -14507,10 +14917,15 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites_ subst hwordOffsetEq exact stmtListGenericCore_singleton_setStructMember2Single_of_slotSafety hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hm hmembers hmember hws hss - | forEachLiteralBounded _ _ _ => - simp [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurface] at hsurface + | forEachLiteralBounded hbodyNames hbody ih => + rcases compiledStmtStep_forEach_literal_zero hbodyNames + (stmtListGenericCore_of_supportedStmtList_of_surface + hnoConflict hbody (by + simpa [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurface] using hsurface)) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause hsupportedRest ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause diff --git a/Compiler/Proofs/IRGeneration/SupportedFragment.lean b/Compiler/Proofs/IRGeneration/SupportedFragment.lean index 6cd341be8..f8c247973 100644 --- a/Compiler/Proofs/IRGeneration/SupportedFragment.lean +++ b/Compiler/Proofs/IRGeneration/SupportedFragment.lean @@ -343,11 +343,10 @@ inductive SupportedStmtList (fields : List Field) : List String → List Stmt | forEachLiteralBounded {scope : List String} {varName : String} - {n : Nat} {body : List Stmt} : (∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) → SupportedStmtList fields (varName :: scope) body → - SupportedStmtList fields scope [Stmt.forEach varName (.literal n) body] + SupportedStmtList fields scope [Stmt.forEach varName (.literal 0) body] | requireClause {scope : List String} (clause : RequireLiteralGuardFamilyClause) diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index 416fd94d4..a585b2a32 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -1557,10 +1557,13 @@ def stmtTouchesUnsupportedContractSurface (stmt : Stmt) : Bool := | .storageArrayPush _ _ | .storageArrayPop _ | .setStorageArrayElement _ _ _ | .requireError _ _ _ | .revertError _ _ | .returnValues _ | .returnArray _ | .returnBytes _ | .returnStorageWords _ | .calldatacopy _ _ _ - | .returndataCopy _ _ _ | .revertReturndata | .forEach _ _ _ + | .returndataCopy _ _ _ | .revertReturndata | .emit _ _ | .internalCall _ _ | .internalCallAssign _ _ _ | .rawLog _ _ _ | .externalCallBind _ _ _ | .ecm _ _ | .tryExternalCallBind _ _ _ _ | .unsafeBlock _ _ | .matchAdt _ _ _ => true + | .forEach _ (.literal 0) body => + stmtListTouchesUnsupportedContractSurface body + | .forEach _ _ _ => true def stmtTouchesUnsupportedContractSurfaceWithEvents (events : List EventDef) (stmt : Stmt) : Bool := @@ -4375,9 +4378,20 @@ theorem stmtTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed | storageArrayPop _ | setStorageArrayElement _ _ _ | requireError _ _ _ | revertError _ _ | returnValues _ | returnArray _ | returnBytes _ | returnStorageWords _ | calldatacopy _ _ _ | returndataCopy _ _ _ - | revertReturndata | forEach _ _ _ | emit _ _ | internalCall _ _ + | revertReturndata | emit _ _ | internalCall _ _ | internalCallAssign _ _ _ | rawLog _ _ _ | externalCallBind _ _ _ | ecm _ _ => cases hsurface + | forEach varName count body => + cases count with + | literal n => + cases n with + | zero => + simp only [stmtTouchesUnsupportedContractSurface] at hsurface + simpa [stmtTouchesUnsupportedHelperSurface, exprTouchesUnsupportedHelperSurface] using + stmtListTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed + hsurface + | succ n => simp [stmtTouchesUnsupportedContractSurface] at hsurface + | _ => simp [stmtTouchesUnsupportedContractSurface] at hsurface termination_by sizeOf stmt theorem stmtListTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed From 28579d6e0c46251a02751228d5767e2d476fd24b Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 04:43:41 +0200 Subject: [PATCH 10/27] Split zero-bound forEach proof --- .../Proofs/IRGeneration/GenericInduction.lean | 458 ++++++++++++------ 1 file changed, 309 insertions(+), 149 deletions(-) diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 34b61c011..172635aed 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -12707,6 +12707,297 @@ private theorem stmtListGenericCore_of_supportedStmtList_tstoreSingle_of_surface (hcoreValue := hcoreValue) (hinScopeValue := hinScopeValue) +private def forEachZeroUsedNames (scope : List String) (varName : String) (body : List Stmt) : + List String := + varName :: (scope ++ collectExprNames (Expr.literal 0) ++ collectStmtListNames body) + +private def forEachZeroIdxName (scope : List String) (varName : String) (body : List Stmt) : + String := + pickFreshName "__forEach_idx" (forEachZeroUsedNames scope varName body) + +private def forEachZeroCountName (scope : List String) (varName : String) (body : List Stmt) : + String := + pickFreshName "__forEach_count" + (forEachZeroIdxName scope varName body :: forEachZeroUsedNames scope varName body) + +private def forEachZeroInitStmts (scope : List String) (varName : String) (body : List Stmt) : + List YulStmt := + [ + YulStmt.let_ (forEachZeroIdxName scope varName body) (YulExpr.lit 0), + YulStmt.let_ (forEachZeroCountName scope varName body) (YulExpr.lit 0), + YulStmt.let_ varName (YulExpr.lit 0) + ] + +private def forEachZeroCondExpr (scope : List String) (varName : String) (body : List Stmt) : + YulExpr := + YulExpr.call "lt" + [YulExpr.ident (forEachZeroIdxName scope varName body), + YulExpr.ident (forEachZeroCountName scope varName body)] + +private def forEachZeroPostStmts (scope : List String) (varName : String) (body : List Stmt) : + List YulStmt := + [YulStmt.assign (forEachZeroIdxName scope varName body) + (YulExpr.call "add" [YulExpr.ident (forEachZeroIdxName scope varName body), YulExpr.lit 1])] + +private def forEachZeroBodyWithBind + (scope : List String) (varName : String) (body : List Stmt) (bodyIR : List YulStmt) : + List YulStmt := + YulStmt.assign varName (YulExpr.ident (forEachZeroIdxName scope varName body)) :: bodyIR + +private def forEachZeroCompiledIR + (scope : List String) (varName : String) (body : List Stmt) (bodyIR : List YulStmt) : + List YulStmt := + [YulStmt.for_ (forEachZeroInitStmts scope varName body) + (forEachZeroCondExpr scope varName body) + (forEachZeroPostStmts scope varName body) + (forEachZeroBodyWithBind scope varName body bodyIR)] + +private def forEachZeroRuntimeLoop + (runtime : SourceSemantics.RuntimeState) (varName : String) : + SourceSemantics.RuntimeState := + { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } + +private theorem sourceExec_forEach_literal_zero + {fields : List Field} + {runtime : SourceSemantics.RuntimeState} + {varName : String} + {body : List Stmt} : + SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 0) body) = + .continue (forEachZeroRuntimeLoop runtime varName) := by + change + SourceSemantics.execForEachLoop varName + (fun loopState => SourceSemantics.execStmtList fields loopState body) + (forEachZeroRuntimeLoop runtime varName) 0 0 = + .continue (forEachZeroRuntimeLoop runtime varName) + rfl + +private theorem forEachZero_fresh_facts + {scope : List String} + {varName : String} + {body : List Stmt} : + let idxName := forEachZeroIdxName scope varName body + let countName := forEachZeroCountName scope varName body + idxName ≠ varName ∧ countName ≠ varName ∧ countName ≠ idxName ∧ + idxName ∉ scope ∧ countName ∉ scope := by + intro idxName countName + have hidxFreshUsed : + idxName ∉ forEachZeroUsedNames scope varName body := by + simpa [idxName, forEachZeroIdxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" + (forEachZeroUsedNames scope varName body) + have hcountFreshUsed : + countName ∉ idxName :: forEachZeroUsedNames scope varName body := by + simpa [countName, forEachZeroCountName, idxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" + (idxName :: forEachZeroUsedNames scope varName body) + constructor + · intro h; exact hidxFreshUsed (by simp [forEachZeroUsedNames, h]) + constructor + · intro h; exact hcountFreshUsed (by simp [forEachZeroUsedNames, h]) + constructor + · intro h; exact hcountFreshUsed (by simp [h]) + constructor + · intro hmem; exact hidxFreshUsed (by simp [forEachZeroUsedNames, hmem]) + · intro hmem; exact hcountFreshUsed (by simp [forEachZeroUsedNames, hmem]) + +private theorem evalIRExpr_forEachZeroCond_after_init + {scope : List String} + {varName : String} + {body : List Stmt} + {state : IRState} + (hidx_ne_var : forEachZeroIdxName scope varName body ≠ varName) + (hcount_ne_var : forEachZeroCountName scope varName body ≠ varName) + (hcount_ne_idx : + forEachZeroCountName scope varName body ≠ forEachZeroIdxName scope varName body) : + evalIRExpr (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0) + (forEachZeroCondExpr scope varName body) = some 0 := by + let idxName := forEachZeroIdxName scope varName body + let countName := forEachZeroCountName scope varName body + let stateIdx := state.setVar idxName 0 + let stateCount := stateIdx.setVar countName 0 + let stateLoop := stateCount.setVar varName 0 + have hidx_after : stateLoop.getVar idxName = some 0 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 hidx_ne_var] + rw [FunctionBody.getVar_setVar_ne _ countName idxName 0 hcount_ne_idx.symm] + simp + have hcount_after : stateLoop.getVar countName = some 0 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + change evalIRExpr stateLoop (forEachZeroCondExpr scope varName body) = some 0 + simp [forEachZeroCondExpr, idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after, hcount_after] + +private theorem forEachZero_initFuel_of_slack + {scope : List String} + {varName : String} + {body : List Stmt} + {bodyIR : List YulStmt} + {extraFuel : Nat} + (hslack : sizeOf (forEachZeroCompiledIR scope varName body bodyIR) - + (forEachZeroCompiledIR scope varName body bodyIR).length ≤ extraFuel) : + 4 ≤ extraFuel := by + have hmin : 4 ≤ + sizeOf (forEachZeroCompiledIR scope varName body bodyIR) - + (forEachZeroCompiledIR scope varName body bodyIR).length := by + dsimp [forEachZeroCompiledIR, forEachZeroInitStmts, forEachZeroCondExpr, + forEachZeroPostStmts, forEachZeroBodyWithBind] + simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, + YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, + YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] + omega + omega + +private theorem execIRStmts_forEach_literal_zero_compiled + {scope : List String} + {varName : String} + {body : List Stmt} + {bodyIR : List YulStmt} + {state : IRState} + {extraFuel : Nat} + (hslack : sizeOf (forEachZeroCompiledIR scope varName body bodyIR) - + (forEachZeroCompiledIR scope varName body bodyIR).length ≤ extraFuel) + (hidx_ne_var : forEachZeroIdxName scope varName body ≠ varName) + (hcount_ne_var : forEachZeroCountName scope varName body ≠ varName) + (hcount_ne_idx : + forEachZeroCountName scope varName body ≠ forEachZeroIdxName scope varName body) : + execIRStmts ((forEachZeroCompiledIR scope varName body bodyIR).length + extraFuel + 1) state + (forEachZeroCompiledIR scope varName body bodyIR) = + .continue (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0) := by + let idxName := forEachZeroIdxName scope varName body + let countName := forEachZeroCountName scope varName body + let stateIdx := state.setVar idxName 0 + let stateCount := stateIdx.setVar countName 0 + let stateLoop := stateCount.setVar varName 0 + have hfuelInit : 4 ≤ extraFuel := forEachZero_initFuel_of_slack hslack + have hinit : execIRStmts extraFuel state + (forEachZeroInitStmts scope varName body) = .continue stateLoop := by + simpa [forEachZeroInitStmts, stateIdx, stateCount, stateLoop, idxName, countName] using + execIRStmts_forEach_init_literal_zero + (fuel := extraFuel) (state := state) (idxName := idxName) + (countName := countName) (varName := varName) (hfuel := hfuelInit) + have hcond : evalIRExpr stateLoop (forEachZeroCondExpr scope varName body) = some 0 := by + simpa [stateLoop, stateCount, stateIdx, idxName, countName] using + evalIRExpr_forEachZeroCond_after_init + (scope := scope) (varName := varName) (body := body) (state := state) + hidx_ne_var hcount_ne_var hcount_ne_idx + dsimp [forEachZeroCompiledIR] + have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega + rw [← hfuelEq] + exact execIRStmts_single_for_init_cond_zero + (fuel := extraFuel) (state := state) (sInit := stateLoop) + (init := forEachZeroInitStmts scope varName body) + (post := forEachZeroPostStmts scope varName body) + (body := forEachZeroBodyWithBind scope varName body bodyIR) + (cond := forEachZeroCondExpr scope varName body) hinit hcond + +private theorem forEachZero_nextScopeIncluded + {scope : List String} + {varName : String} + {body : List Stmt} + (hbodyNames : ∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) : + FunctionBody.scopeNamesIncluded + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) + (varName :: scope) := by + intro name hmem + simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem + rcases hmem with hvar | hbody | hscopeMem + · simp [hvar] + · exact hbodyNames name hbody + · simp [hscopeMem] + +private theorem runtimeStateMatchesIR_forEachZeroLoop + {fields : List Field} + {scope : List String} + {varName : String} + {body : List Stmt} + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : + FunctionBody.runtimeStateMatchesIR fields (forEachZeroRuntimeLoop runtime varName) + (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0) := by + simpa [forEachZeroRuntimeLoop] using + FunctionBody.runtimeStateMatchesIR_setVar_bindValue + (fields := fields) + (runtime := runtime) + (state := (state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0) + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime)) + varName 0 + +private theorem bindingsExactly_forEachZeroBase + {scope : List String} + {varName : String} + {body : List Stmt} + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + (hidx_not_scope : forEachZeroIdxName scope varName body ∉ scope) + (hcount_not_scope : forEachZeroCountName scope varName body ∉ scope) + (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings state) : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + (forEachZeroRuntimeLoop runtime varName).bindings + (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0) := by + have hexactCount : + FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings + ((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0) := + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope) + hcount_not_scope + simpa [forEachZeroRuntimeLoop] using + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue + (boundName := varName) (value := 0) hexactCount + +private theorem stmtStepMatches_forEach_literal_zero_final + {fields : List Field} + {scope : List String} + {varName : String} + {body : List Stmt} + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + (hbodyNames : ∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) + (hidx_not_scope : forEachZeroIdxName scope varName body ∉ scope) + (hcount_not_scope : forEachZeroCountName scope varName body ∉ scope) + (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings state) + (hscope : FunctionBody.scopeNamesPresent scope runtime.bindings) + (hbounded : FunctionBody.bindingsBounded runtime.bindings) + (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : + stmtStepMatchesIRExec fields + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) + (.continue (forEachZeroRuntimeLoop runtime varName)) + (.continue (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0)) := by + let runtimeLoop := forEachZeroRuntimeLoop runtime varName + have hexactBase : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeLoop.bindings + (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0) := + bindingsExactly_forEachZeroBase hidx_not_scope hcount_not_scope hexact + have hNextScopeIncl := forEachZero_nextScopeIncluded + (scope := scope) (varName := varName) (body := body) hbodyNames + have hscopeBase : FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by + simpa [runtimeLoop, forEachZeroRuntimeLoop] using + FunctionBody.scopeNamesPresent_cons_bindValue + (boundName := varName) (value := 0) hscope + have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := + FunctionBody.bindingsBounded_bindValue hbounded varName 0 + (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) + simp [stmtStepMatchesIRExec] + exact ⟨runtimeStateMatchesIR_forEachZeroLoop + (fields := fields) (scope := scope) (varName := varName) (body := body) + (runtime := runtime) (state := state) hruntime, + FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactBase hNextScopeIncl, + hboundedLoop, + FunctionBody.scopeNamesPresent_of_included hscopeBase hNextScopeIncl⟩ + private theorem compiledStmtStep_forEach_literal_zero {fields : List Field} {scope : List String} @@ -12723,160 +13014,29 @@ private theorem compiledStmtStep_forEach_literal_zero hbodyGeneric FunctionBody.scopeNamesIncluded_refl with ⟨bodyIR, hbodyCompile⟩ - let forUsedNames := varName :: (scope ++ collectExprNames (Expr.literal 0) ++ collectStmtListNames body) - let idxName := pickFreshName "__forEach_idx" forUsedNames - let countName := pickFreshName "__forEach_count" (idxName :: forUsedNames) - let initStmts := [ - YulStmt.let_ idxName (YulExpr.lit 0), - YulStmt.let_ countName (YulExpr.lit 0), - YulStmt.let_ varName (YulExpr.lit 0) - ] - let condExpr := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName] - let postStmts := [YulStmt.assign idxName (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] - let bodyWithBind := YulStmt.assign varName (YulExpr.ident idxName) :: bodyIR - let compiledIR := [YulStmt.for_ initStmts condExpr postStmts bodyWithBind] - refine ⟨compiledIR, ?_⟩ + refine ⟨forEachZeroCompiledIR scope varName body bodyIR, ?_⟩ refine { compileOk := ?_ preserves := ?_ } - · dsimp [compiledIR, initStmts, condExpr, postStmts, bodyWithBind, idxName, countName, - forUsedNames] + · dsimp [forEachZeroCompiledIR, forEachZeroInitStmts, forEachZeroCondExpr, + forEachZeroPostStmts, forEachZeroBodyWithBind, forEachZeroIdxName, + forEachZeroCountName, forEachZeroUsedNames] simp [CompilationModel.compileStmt, CompilationModel.compileExpr, hbodyCompile] · intro runtime state extraFuel hexact hscope hbounded hruntime hslack - let stateIdx := state.setVar idxName 0 - let stateCount := stateIdx.setVar countName 0 - let stateLoop := stateCount.setVar varName 0 - let runtimeLoop : SourceSemantics.RuntimeState := - { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } - have hsource : - SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 0) body) = - .continue runtimeLoop := by - change - SourceSemantics.execForEachLoop varName - (fun loopState => SourceSemantics.execStmtList fields loopState body) - runtimeLoop 0 0 = .continue runtimeLoop - rfl - have hidxFreshUsed : idxName ∉ forUsedNames := by - simpa [idxName] using - CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" forUsedNames - have hcountFreshUsed : countName ∉ idxName :: forUsedNames := by - simpa [countName] using - CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" (idxName :: forUsedNames) - have hidx_ne_var : idxName ≠ varName := by - intro h - apply hidxFreshUsed - simp [forUsedNames, h] - have hcount_ne_var : countName ≠ varName := by - intro h - apply hcountFreshUsed - simp [forUsedNames, h] - have hcount_ne_idx : countName ≠ idxName := by - intro h - apply hcountFreshUsed - simp [h] - have hidx_not_scope : idxName ∉ scope := by - intro hmem - apply hidxFreshUsed - simp [forUsedNames, hmem] - have hcount_not_scope : countName ∉ scope := by - intro hmem - apply hcountFreshUsed - simp [forUsedNames, hmem] - have hfuelInit : 4 ≤ extraFuel := by - have hmin : 4 ≤ sizeOf compiledIR - compiledIR.length := by - dsimp [compiledIR, initStmts, condExpr, postStmts, bodyWithBind] - simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, - YulStmt.for_.sizeOf_spec, YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, - YulExpr.call.sizeOf_spec, YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] - omega - omega - have hinit : - execIRStmts extraFuel state initStmts = .continue stateLoop := by - simpa [initStmts, stateIdx, stateCount, stateLoop] using - execIRStmts_forEach_init_literal_zero - (fuel := extraFuel) (state := state) (idxName := idxName) - (countName := countName) (varName := varName) (hfuel := hfuelInit) - have hcond : evalIRExpr stateLoop condExpr = some 0 := by - have hidx_after : stateLoop.getVar idxName = some 0 := by - dsimp [stateLoop, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 (by exact hidx_ne_var)] - rw [FunctionBody.getVar_setVar_ne _ countName idxName 0 (by exact hcount_ne_idx.symm)] - simp - have hcount_after : stateLoop.getVar countName = some 0 := by - dsimp [stateLoop, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 0 (by exact hcount_ne_var)] - simp - simp [condExpr, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_after, hcount_after] - have hir : - execIRStmts (compiledIR.length + extraFuel + 1) state compiledIR = - .continue stateLoop := by - dsimp [compiledIR] - have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega - rw [← hfuelEq] - exact - (execIRStmts_single_for_init_cond_zero - (fuel := extraFuel) - (state := state) - (sInit := stateLoop) - (init := initStmts) - (post := postStmts) - (body := bodyWithBind) - (cond := condExpr) - hinit - hcond) - have hruntimeIdx : FunctionBody.runtimeStateMatchesIR fields runtime stateIdx := - FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime - have hruntimeCount : FunctionBody.runtimeStateMatchesIR fields runtime stateCount := - FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntimeIdx - have hruntimeLoop : FunctionBody.runtimeStateMatchesIR fields runtimeLoop stateLoop := by - simpa [runtimeLoop, stateLoop] using - FunctionBody.runtimeStateMatchesIR_setVar_bindValue - (fields := fields) (runtime := runtime) (state := stateCount) - hruntimeCount varName 0 - have hexactIdx : - FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateIdx := - FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope - have hexactCount : - FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateCount := - FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexactIdx hcount_not_scope - have hexactBase : - FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) runtimeLoop.bindings stateLoop := by - simpa [runtimeLoop, stateLoop] using - FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue - (boundName := varName) (value := 0) hexactCount - have hNextScopeIncl : - FunctionBody.scopeNamesIncluded - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) - (varName :: scope) := by - intro name hmem - simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem - rcases hmem with hvar | hbody | hscopeMem - · simp [hvar] - · exact hbodyNames name hbody - · simp [hscopeMem] - have hexactLoop : - FunctionBody.bindingsExactlyMatchIRVarsOnScope - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) - runtimeLoop.bindings stateLoop := - FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactBase hNextScopeIncl - have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := - FunctionBody.bindingsBounded_bindValue hbounded varName 0 - (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) - have hscopeBase : - FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by - simpa [runtimeLoop] using - FunctionBody.scopeNamesPresent_cons_bindValue - (boundName := varName) (value := 0) hscope - have hscopeLoop : - FunctionBody.scopeNamesPresent - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 0) body)) - runtimeLoop.bindings := - FunctionBody.scopeNamesPresent_of_included hscopeBase hNextScopeIncl - refine ⟨.continue runtimeLoop, .continue stateLoop, hsource, hir, ?_⟩ - simp [stmtStepMatchesIRExec] - exact ⟨hruntimeLoop, hexactLoop, hboundedLoop, hscopeLoop⟩ + rcases forEachZero_fresh_facts (scope := scope) (varName := varName) (body := body) with + ⟨hidx_ne_var, hcount_ne_var, hcount_ne_idx, hidx_not_scope, hcount_not_scope⟩ + refine ⟨.continue (forEachZeroRuntimeLoop runtime varName), + .continue (((state.setVar (forEachZeroIdxName scope varName body) 0).setVar + (forEachZeroCountName scope varName body) 0).setVar varName 0), + sourceExec_forEach_literal_zero, ?_, ?_⟩ + · exact execIRStmts_forEach_literal_zero_compiled + (scope := scope) (varName := varName) (body := body) (bodyIR := bodyIR) + (state := state) (extraFuel := extraFuel) hslack + hidx_ne_var hcount_ne_var hcount_ne_idx + · exact stmtStepMatches_forEach_literal_zero_final + (fields := fields) (scope := scope) (varName := varName) (body := body) + (runtime := runtime) (state := state) hbodyNames hidx_not_scope + hcount_not_scope hexact hscope hbounded hruntime /-- Extra Tier 2 assumptions needed to turn the singleton mapping-write From d62993c757c7f7d8b0dc138485f0811dad3f70a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 May 2026 03:45:02 +0100 Subject: [PATCH 11/27] chore: auto-refresh derived artifacts --- PrintAxioms.lean | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/PrintAxioms.lean b/PrintAxioms.lean index 90e880723..e2828a8ad 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -2429,6 +2429,15 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_setStorageAddrSingleSlot_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_mstoreSingle_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_tstoreSingle_of_surface -- private + -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_zero -- private + -- Compiler.Proofs.IRGeneration.forEachZero_fresh_facts -- private + -- Compiler.Proofs.IRGeneration.evalIRExpr_forEachZeroCond_after_init -- private + -- Compiler.Proofs.IRGeneration.forEachZero_initFuel_of_slack -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_zero_compiled -- private + -- Compiler.Proofs.IRGeneration.forEachZero_nextScopeIncluded -- private + -- Compiler.Proofs.IRGeneration.runtimeStateMatchesIR_forEachZeroLoop -- private + -- Compiler.Proofs.IRGeneration.bindingsExactly_forEachZeroBase -- private + -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_zero_final -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_zero -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingUintSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingChainSingle_of_slotSafety -- private @@ -5498,4 +5507,4 @@ end Verity.AxiomAudit Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args ] --- Total: 5200 theorems/lemmas (3611 public, 1589 private, 0 sorry'd) +-- Total: 5209 theorems/lemmas (3611 public, 1598 private, 0 sorry'd) From 9764203ed18627234ebd20704d9bdf19c79bd46d Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 08:28:56 +0200 Subject: [PATCH 12/27] Prove one-iteration forEach preservation --- Compiler/Proofs/EndToEnd.lean | 7 + .../Proofs/IRGeneration/GenericInduction.lean | 342 ++++++++++++++++++ .../Proofs/IRGeneration/IRInterpreter.lean | 16 + .../IRGeneration/SupportedFragment.lean | 7 + .../Proofs/IRGeneration/SupportedSpec.lean | 20 + 5 files changed, 392 insertions(+) diff --git a/Compiler/Proofs/EndToEnd.lean b/Compiler/Proofs/EndToEnd.lean index 4282948a6..93b1f6ec1 100644 --- a/Compiler/Proofs/EndToEnd.lean +++ b/Compiler/Proofs/EndToEnd.lean @@ -5294,6 +5294,9 @@ theorem supportedStmtList_safe_of_state_effect_closed | forEachLiteralBounded => simp [stmtListTouchesUnsupportedStateSurface, stmtTouchesUnsupportedStateSurface] at hState + | forEachLiteralOneEmpty => + simp [stmtListTouchesUnsupportedStateSurface, + stmtTouchesUnsupportedStateSurface] at hState | requireClause clause _ ih => simpa using Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.append @@ -5564,6 +5567,10 @@ theorem supportedStmtList_safe_of_state_except_mapping_writes_stmt_safety simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurface] at hState + | forEachLiteralOneEmpty => + simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurface] at hState | @requireClause scope clause rest _ ih => have hTailSafety : ∀ stmt ∈ rest, StmtMappingWriteSlotSafe fields stmt := by diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 172635aed..3ef9522ef 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -12752,11 +12752,54 @@ private def forEachZeroCompiledIR (forEachZeroPostStmts scope varName body) (forEachZeroBodyWithBind scope varName body bodyIR)] +private def forEachOneUsedNames (scope : List String) (varName : String) : + List String := + varName :: (scope ++ collectExprNames (Expr.literal 1) ++ collectStmtListNames []) + +private def forEachOneIdxName (scope : List String) (varName : String) : + String := + pickFreshName "__forEach_idx" (forEachOneUsedNames scope varName) + +private def forEachOneCountName (scope : List String) (varName : String) : + String := + pickFreshName "__forEach_count" + (forEachOneIdxName scope varName :: forEachOneUsedNames scope varName) + +private def forEachOneInitStmts (scope : List String) (varName : String) : + List YulStmt := + [ + YulStmt.let_ (forEachOneIdxName scope varName) (YulExpr.lit 0), + YulStmt.let_ (forEachOneCountName scope varName) (YulExpr.lit 1), + YulStmt.let_ varName (YulExpr.lit 0) + ] + +private def forEachOneCompiledIR (scope : List String) (varName : String) : + List YulStmt := + [YulStmt.for_ (forEachOneInitStmts scope varName) + (YulExpr.call "lt" + [YulExpr.ident (forEachOneIdxName scope varName), + YulExpr.ident (forEachOneCountName scope varName)]) + [YulStmt.assign (forEachOneIdxName scope varName) + (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]] + private def forEachZeroRuntimeLoop (runtime : SourceSemantics.RuntimeState) (varName : String) : SourceSemantics.RuntimeState := { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } +private theorem source_bindValue_idem + (bindings : List (String × Nat)) (name : String) (value : Nat) : + SourceSemantics.bindValue (SourceSemantics.bindValue bindings name value) name value = + SourceSemantics.bindValue bindings name value := by + simp [SourceSemantics.bindValue, List.filter_filter] + +private theorem irState_setVar_idem + (state : IRState) (name : String) (value : Nat) : + (state.setVar name value).setVar name value = state.setVar name value := by + cases state + simp [IRState.setVar, List.filter_filter] + private theorem sourceExec_forEach_literal_zero {fields : List Field} {runtime : SourceSemantics.RuntimeState} @@ -12771,6 +12814,20 @@ private theorem sourceExec_forEach_literal_zero .continue (forEachZeroRuntimeLoop runtime varName) rfl +private theorem sourceExec_forEach_literal_one_empty + {fields : List Field} + {runtime : SourceSemantics.RuntimeState} + {varName : String} : + SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 1) []) = + .continue (forEachZeroRuntimeLoop runtime varName) := by + change + SourceSemantics.execForEachLoop varName + (fun loopState => SourceSemantics.execStmtList fields loopState []) + (forEachZeroRuntimeLoop runtime varName) 0 1 = + .continue (forEachZeroRuntimeLoop runtime varName) + simp [SourceSemantics.execForEachLoop, SourceSemantics.execStmtList, + forEachZeroRuntimeLoop, SourceSemantics.wordNormalize, source_bindValue_idem] + private theorem forEachZero_fresh_facts {scope : List String} {varName : String} @@ -12800,6 +12857,34 @@ private theorem forEachZero_fresh_facts · intro hmem; exact hidxFreshUsed (by simp [forEachZeroUsedNames, hmem]) · intro hmem; exact hcountFreshUsed (by simp [forEachZeroUsedNames, hmem]) +private theorem forEachOne_fresh_facts + {scope : List String} + {varName : String} : + let idxName := forEachOneIdxName scope varName + let countName := forEachOneCountName scope varName + idxName ≠ varName ∧ countName ≠ varName ∧ countName ≠ idxName ∧ + idxName ∉ scope ∧ countName ∉ scope := by + intro idxName countName + have hidxFreshUsed : + idxName ∉ forEachOneUsedNames scope varName := by + simpa [idxName, forEachOneIdxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" + (forEachOneUsedNames scope varName) + have hcountFreshUsed : + countName ∉ idxName :: forEachOneUsedNames scope varName := by + simpa [countName, forEachOneCountName, idxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" + (idxName :: forEachOneUsedNames scope varName) + constructor + · intro h; exact hidxFreshUsed (by simp [forEachOneUsedNames, h]) + constructor + · intro h; exact hcountFreshUsed (by simp [forEachOneUsedNames, h]) + constructor + · intro h; exact hcountFreshUsed (by simp [h]) + constructor + · intro hmem; exact hidxFreshUsed (by simp [forEachOneUsedNames, hmem]) + · intro hmem; exact hcountFreshUsed (by simp [forEachOneUsedNames, hmem]) + private theorem evalIRExpr_forEachZeroCond_after_init {scope : List String} {varName : String} @@ -12895,6 +12980,127 @@ private theorem execIRStmts_forEach_literal_zero_compiled (body := forEachZeroBodyWithBind scope varName body bodyIR) (cond := forEachZeroCondExpr scope varName body) hinit hcond +private theorem execIRStmts_forEach_literal_one_empty_compiled + {scope : List String} + {varName : String} + {state : IRState} + {extraFuel : Nat} + (hslack : sizeOf (forEachOneCompiledIR scope varName) - + (forEachOneCompiledIR scope varName).length ≤ extraFuel) + (hidx_ne_var : forEachOneIdxName scope varName ≠ varName) + (hcount_ne_var : forEachOneCountName scope varName ≠ varName) + (hcount_ne_idx : + forEachOneCountName scope varName ≠ forEachOneIdxName scope varName) : + execIRStmts ((forEachOneCompiledIR scope varName).length + extraFuel + 1) state + (forEachOneCompiledIR scope varName) = + .continue ((((state.setVar (forEachOneIdxName scope varName) 0).setVar + (forEachOneCountName scope varName) 1).setVar varName 0).setVar + (forEachOneIdxName scope varName) 1) := by + let idxName := forEachOneIdxName scope varName + let countName := forEachOneCountName scope varName + let stateIdx := state.setVar idxName 0 + let stateCount := stateIdx.setVar countName 1 + let stateLoop := stateCount.setVar varName 0 + let statePost := stateLoop.setVar idxName 1 + have hfuelInit : 4 ≤ extraFuel := by + have hmin : 4 ≤ + sizeOf (forEachOneCompiledIR scope varName) - + (forEachOneCompiledIR scope varName).length := by + dsimp [forEachOneCompiledIR, forEachOneInitStmts, forEachZeroCondExpr, + forEachZeroPostStmts, forEachZeroBodyWithBind] + simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, + YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, + YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] + omega + omega + have hinit : execIRStmts extraFuel state + (forEachOneInitStmts scope varName) = .continue stateLoop := by + simpa [forEachOneInitStmts, stateIdx, stateCount, stateLoop, idxName, countName] using + execIRStmts_forEach_init_literal + (fuel := extraFuel) (state := state) (idxName := idxName) + (countName := countName) (varName := varName) (bound := 1) (hfuel := hfuelInit) + have hidx_after_init : stateLoop.getVar idxName = some 0 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 hidx_ne_var] + rw [FunctionBody.getVar_setVar_ne _ countName idxName 1 hcount_ne_idx.symm] + simp + have hcount_after_init : stateLoop.getVar countName = some 1 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + have hcondOne : + evalIRExpr stateLoop (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) = some 1 := by + simp [forEachZeroCondExpr, idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after_init, hcount_after_init] + have hbody : execIRStmts extraFuel stateLoop + ([YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]) = .continue stateLoop := by + have hidx_read : stateLoop.getVar idxName = some 0 := hidx_after_init + cases extraFuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + stateLoop.getVar (forEachOneIdxName scope varName) = some 0 := by + simpa [idxName] using hidx_read + have hloop_idem : stateLoop.setVar varName 0 = stateLoop := by + simpa [stateLoop] using irState_setVar_idem stateCount varName 0 + simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read', hloop_idem] + have hpost : execIRStmts extraFuel stateLoop + ([YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) = .continue statePost := by + have hidx_read : stateLoop.getVar idxName = some 0 := hidx_after_init + cases extraFuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + stateLoop.getVar (forEachOneIdxName scope varName) = some 0 := by + simpa [idxName] using hidx_read + dsimp [idxName, statePost] + simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_read'] + have hidx_after_post : statePost.getVar idxName = some 1 := by + exact FunctionBody.getVar_setVar_eq stateLoop idxName 1 + have hcount_after_post : statePost.getVar countName = some 1 := by + dsimp [statePost, stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ idxName countName 1 hcount_ne_idx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + have hcondZero : + evalIRExpr statePost (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) = some 0 := by + simp [forEachZeroCondExpr, idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after_post, hcount_after_post] + have hloop : + execIRStmt extraFuel statePost + (.for_ [] (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) + ([YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) + ([YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))])) = + .continue statePost := by + cases extraFuel with + | zero => omega + | succ fuel => + exact execIRStmt_for_init_cond_zero fuel statePost statePost [] + ([YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) + ([YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]) + (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) (by simp [execIRStmts]) hcondZero + dsimp [forEachOneCompiledIR] + have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega + rw [← hfuelEq] + rw [execIRStmts_single_for_one_continue + (fuel := extraFuel) (state := state) (sInit := stateLoop) (sBody := stateLoop) + (sPost := statePost) (init := forEachOneInitStmts scope varName) + (post := [YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) + (body := [YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]) + (cond := YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) (condValue := 1) + hinit hcondOne (by norm_num) hbody hpost] + simpa [hloop, statePost, idxName, countName, stateLoop, stateCount, stateIdx] + private theorem forEachZero_nextScopeIncluded {scope : List String} {varName : String} @@ -12998,6 +13204,90 @@ private theorem stmtStepMatches_forEach_literal_zero_final hboundedLoop, FunctionBody.scopeNamesPresent_of_included hscopeBase hNextScopeIncl⟩ +private theorem stmtStepMatches_forEach_literal_one_empty_final + {fields : List Field} + {scope : List String} + {varName : String} + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + (hidx_ne_var : forEachOneIdxName scope varName ≠ varName) + (hidx_not_scope : forEachOneIdxName scope varName ∉ scope) + (hcount_not_scope : forEachOneCountName scope varName ∉ scope) + (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings state) + (hscope : FunctionBody.scopeNamesPresent scope runtime.bindings) + (hbounded : FunctionBody.bindingsBounded runtime.bindings) + (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : + stmtStepMatchesIRExec fields + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 1) [])) + (.continue (forEachZeroRuntimeLoop runtime varName)) + (.continue ((((state.setVar (forEachOneIdxName scope varName) 0).setVar + (forEachOneCountName scope varName) 1).setVar varName 0).setVar + (forEachOneIdxName scope varName) 1)) := by + let idxName := forEachOneIdxName scope varName + let countName := forEachOneCountName scope varName + let runtimeLoop := forEachZeroRuntimeLoop runtime varName + let stateCount := (state.setVar idxName 0).setVar countName 1 + let stateLoop := stateCount.setVar varName 0 + let statePost := stateLoop.setVar idxName 1 + have hidx_not_next : idxName ∉ varName :: scope := by + intro hmem + simp at hmem + rcases hmem with hvar | hscopeMem + · exact hidx_ne_var hvar + · exact hidx_not_scope hscopeMem + have hexactCount : + FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateCount := by + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope) + hcount_not_scope + have hexactLoop : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeLoop.bindings stateLoop := by + simpa [runtimeLoop, forEachZeroRuntimeLoop, stateLoop] using + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue + (boundName := varName) (value := 0) hexactCount + have hexactPost : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeLoop.bindings statePost := by + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (state := stateLoop) (tempName := idxName) (value := 1) + hexactLoop hidx_not_next + have hruntimePost : + FunctionBody.runtimeStateMatchesIR fields runtimeLoop statePost := by + simpa [runtimeLoop, forEachZeroRuntimeLoop, stateLoop, stateCount, statePost] using + FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (state := stateCount.setVar varName 0) (name := idxName) (value := 1) + (FunctionBody.runtimeStateMatchesIR_setVar_bindValue + (fields := fields) + (runtime := runtime) + (state := stateCount) + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime)) + varName 0) + have hscopeLoop : FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by + simpa [runtimeLoop, forEachZeroRuntimeLoop] using + FunctionBody.scopeNamesPresent_cons_bindValue + (boundName := varName) (value := 0) hscope + have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := + FunctionBody.bindingsBounded_bindValue hbounded varName 0 + (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) + have hNextScopeIncl : + FunctionBody.scopeNamesIncluded + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 1) [])) + (varName :: scope) := by + intro name hmem + simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem + rcases hmem with hvar | hscopeMem + · simp [hvar] + · rcases hscopeMem with hbody | hscopeMem + · exact False.elim (by simpa [collectStmtListNames] using hbody) + · simp [hscopeMem] + simp [stmtStepMatchesIRExec] + exact ⟨hruntimePost, + FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactPost hNextScopeIncl, + hboundedLoop, + FunctionBody.scopeNamesPresent_of_included hscopeLoop hNextScopeIncl⟩ + private theorem compiledStmtStep_forEach_literal_zero {fields : List Field} {scope : List String} @@ -13038,6 +13328,40 @@ private theorem compiledStmtStep_forEach_literal_zero (runtime := runtime) (state := state) hbodyNames hidx_not_scope hcount_not_scope hexact hscope hbounded hruntime +private theorem compiledStmtStep_forEach_literal_one_empty + {fields : List Field} + {scope : List String} + {varName : String} : + ∃ compiledIR, + CompiledStmtStep fields scope (Stmt.forEach varName (Expr.literal 1) []) compiledIR := by + refine ⟨forEachOneCompiledIR scope varName, ?_⟩ + refine + { compileOk := ?_ + preserves := ?_ } + · dsimp [forEachOneCompiledIR, forEachOneInitStmts, forEachOneIdxName, + forEachOneCountName, forEachOneUsedNames] + simp [CompilationModel.compileStmt, CompilationModel.compileStmtList, + CompilationModel.compileExpr, + CompilationModel.uint256Modulus] + rfl + · intro runtime state extraFuel hexact hscope hbounded hruntime hslack + rcases forEachOne_fresh_facts (scope := scope) (varName := varName) with + ⟨hidx_ne_var, hcount_ne_var, hcount_ne_idx, hidx_not_scope, hcount_not_scope⟩ + refine ⟨.continue (forEachZeroRuntimeLoop runtime varName), + .continue ((((state.setVar (forEachOneIdxName scope varName) 0).setVar + (forEachOneCountName scope varName) 1).setVar varName 0).setVar + (forEachOneIdxName scope varName) 1), + sourceExec_forEach_literal_one_empty, ?_, ?_⟩ + · exact execIRStmts_forEach_literal_one_empty_compiled + (scope := scope) (varName := varName) + (state := state) (extraFuel := extraFuel) hslack + hidx_ne_var hcount_ne_var hcount_ne_idx + · exact stmtStepMatches_forEach_literal_one_empty_final + (fields := fields) (scope := scope) (varName := varName) + (runtime := runtime) (state := state) + hidx_ne_var hidx_not_scope hcount_not_scope + hexact hscope hbounded hruntime + /-- Extra Tier 2 assumptions needed to turn the singleton mapping-write constructors in `SupportedStmtList` into real compiled-step proofs. These are @@ -14623,6 +14947,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface stmtTouchesUnsupportedContractSurface] using hsurface)) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil + | forEachLiteralOneEmpty => + rename_i scope varName + rcases compiledStmtStep_forEach_literal_one_empty + (fields := fields) (scope := scope) (varName := varName) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => simp [stmtListTouchesUnsupportedContractSurface] at hsurface apply stmtListGenericCore_append @@ -14760,6 +15090,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites stmtTouchesUnsupportedContractSurface] using hsurface))) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil + | forEachLiteralOneEmpty => + rename_i scope varName + rcases compiledStmtStep_forEach_literal_one_empty + (fields := fields) (scope := scope) (varName := varName) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause ih hsurface @@ -15086,6 +15422,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites_ stmtTouchesUnsupportedContractSurface] using hsurface)) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil + | forEachLiteralOneEmpty => + rename_i scope varName + rcases compiledStmtStep_forEach_literal_one_empty + (fields := fields) (scope := scope) (varName := varName) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause hsupportedRest ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index 9a6e5d131..adbb2411f 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -1196,6 +1196,22 @@ theorem execIRStmts_forEach_init_literal_zero rw [Nat.add_comm] simp [execIRStmts, execIRStmt, evalIRExpr] +/-- The three initializer statements emitted for a literal-bound `forEach` +execute to the expected cached-counter state once enough fuel is available. -/ +theorem execIRStmts_forEach_init_literal + (fuel : Nat) (state : IRState) + (idxName countName varName : String) + (bound : Nat) + (hfuel : 4 ≤ fuel) : + execIRStmts fuel state + [ YulStmt.let_ idxName (YulExpr.lit 0) + , YulStmt.let_ countName (YulExpr.lit bound) + , YulStmt.let_ varName (YulExpr.lit 0) ] = + .continue (((state.setVar idxName 0).setVar countName bound).setVar varName 0) := by + rcases Nat.exists_eq_add_of_le hfuel with ⟨extra, rfl⟩ + rw [Nat.add_comm] + simp [execIRStmts, execIRStmt, evalIRExpr] + @[simp] theorem execIRStmt_stop_succ (fuel : Nat) (state : IRState) : execIRStmt (Nat.succ fuel) state (YulStmt.expr (YulExpr.call "stop" [])) = .stop state := by diff --git a/Compiler/Proofs/IRGeneration/SupportedFragment.lean b/Compiler/Proofs/IRGeneration/SupportedFragment.lean index f8c247973..9681c453a 100644 --- a/Compiler/Proofs/IRGeneration/SupportedFragment.lean +++ b/Compiler/Proofs/IRGeneration/SupportedFragment.lean @@ -347,6 +347,13 @@ inductive SupportedStmtList (fields : List Field) : List String → List Stmt (∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) → SupportedStmtList fields (varName :: scope) body → SupportedStmtList fields scope [Stmt.forEach varName (.literal 0) body] + /-- First positive loop-preservation case: a one-iteration loop with an empty + body. This exercises the compiled Yul `for` init/condition/body-bind/post + path without yet admitting arbitrary positive loop bodies. -/ + | forEachLiteralOneEmpty + {scope : List String} + {varName : String} : + SupportedStmtList fields scope [Stmt.forEach varName (.literal 1) []] | requireClause {scope : List String} (clause : RequireLiteralGuardFamilyClause) diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index 185d1fa6b..f234e1e61 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -3011,6 +3011,10 @@ theorem SupportedStmtList.helperSurfaceClosed simpa [stmtListTouchesUnsupportedHelperSurface, stmtTouchesUnsupportedHelperSurface, exprTouchesUnsupportedHelperSurface] using ih + | forEachLiteralOneEmpty => + simp [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedHelperSurface] | requireClause clause _ ih => simp [stmtListTouchesUnsupportedHelperSurface] constructor @@ -3193,6 +3197,9 @@ theorem SupportedStmtList.internalHelperCallNames_nil simpa [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, exprInternalHelperCallNames] using ih + | forEachLiteralOneEmpty => + simp [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, + exprInternalHelperCallNames] | requireClause clause _ ih => simp [stmtListInternalHelperCallNames] constructor @@ -5066,6 +5073,8 @@ private theorem supportedStmtList_usesArrayElement_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] using ih + | forEachLiteralOneEmpty => + simp [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] | requireClause clause _ ih => simp only [stmtListUsesArrayElement, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5183,6 +5192,9 @@ private theorem supportedStmtList_usesStorageArrayElement_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, exprUsesStorageArrayElement] using ih + | forEachLiteralOneEmpty => + simp [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprUsesStorageArrayElement] | requireClause clause _ ih => simp only [stmtListUsesStorageArrayElement, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5293,6 +5305,9 @@ private theorem supportedStmtList_usesDynamicBytesEq_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, exprUsesDynamicBytesEq] using ih + | forEachLiteralOneEmpty => + simp [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprUsesDynamicBytesEq] | requireClause clause _ ih => simp only [stmtListUsesDynamicBytesEq, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5661,6 +5676,8 @@ private theorem supportedStmtList_usesMulDiv512_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] using ih + | forEachLiteralOneEmpty => + simp [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] | requireClause clause _ ih => simp only [stmtListUsesMulDiv512, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5771,6 +5788,9 @@ private theorem supportedStmtList_usesParamDynamicHeadWord_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, exprUsesParamDynamicHeadWord] using ih + | forEachLiteralOneEmpty => + simp [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, + exprUsesParamDynamicHeadWord] | requireClause clause _ ih => simp only [stmtListUsesParamDynamicHeadWord, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => From 9851d0baa81650e083793bc2ad017515fa6ad648 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 May 2026 07:29:24 +0100 Subject: [PATCH 13/27] chore: auto-refresh derived artifacts --- PrintAxioms.lean | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/PrintAxioms.lean b/PrintAxioms.lean index e2828a8ad..5c2e6c4e9 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -2429,16 +2429,23 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_setStorageAddrSingleSlot_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_mstoreSingle_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_tstoreSingle_of_surface -- private + -- Compiler.Proofs.IRGeneration.source_bindValue_idem -- private + -- Compiler.Proofs.IRGeneration.irState_setVar_idem -- private -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_zero -- private + -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_one_empty -- private -- Compiler.Proofs.IRGeneration.forEachZero_fresh_facts -- private + -- Compiler.Proofs.IRGeneration.forEachOne_fresh_facts -- private -- Compiler.Proofs.IRGeneration.evalIRExpr_forEachZeroCond_after_init -- private -- Compiler.Proofs.IRGeneration.forEachZero_initFuel_of_slack -- private -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_zero_compiled -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_one_empty_compiled -- private -- Compiler.Proofs.IRGeneration.forEachZero_nextScopeIncluded -- private -- Compiler.Proofs.IRGeneration.runtimeStateMatchesIR_forEachZeroLoop -- private -- Compiler.Proofs.IRGeneration.bindingsExactly_forEachZeroBase -- private -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_zero_final -- private + -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_one_empty_final -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_zero -- private + -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_one_empty -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingUintSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingChainSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingSingle_of_slotSafety -- private @@ -2624,6 +2631,7 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.execIRStmts_cons_for_one_continue_of_loop_continue Compiler.Proofs.IRGeneration.execIRStmt_forEach_shape_init_cond_zero Compiler.Proofs.IRGeneration.execIRStmts_forEach_init_literal_zero + Compiler.Proofs.IRGeneration.execIRStmts_forEach_init_literal Compiler.Proofs.IRGeneration.execIRStmt_stop_succ Compiler.Proofs.IRGeneration.execIRStmt_stop_one_add Compiler.Proofs.IRGeneration.execIRStmt_stop_one_add_add @@ -5507,4 +5515,4 @@ end Verity.AxiomAudit Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args ] --- Total: 5209 theorems/lemmas (3611 public, 1598 private, 0 sorry'd) +-- Total: 5217 theorems/lemmas (3612 public, 1605 private, 0 sorry'd) From 52eb6b71c668eb4ce04ced4ff8d2c324be0caefa Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 09:09:27 +0200 Subject: [PATCH 14/27] Prove two-iteration forEach preservation --- Compiler/Proofs/EndToEnd.lean | 7 + .../Proofs/IRGeneration/GenericInduction.lean | 482 ++++++++++++++++++ .../IRGeneration/SupportedFragment.lean | 7 + .../Proofs/IRGeneration/SupportedSpec.lean | 20 + 4 files changed, 516 insertions(+) diff --git a/Compiler/Proofs/EndToEnd.lean b/Compiler/Proofs/EndToEnd.lean index 93b1f6ec1..1f7c89895 100644 --- a/Compiler/Proofs/EndToEnd.lean +++ b/Compiler/Proofs/EndToEnd.lean @@ -5297,6 +5297,9 @@ theorem supportedStmtList_safe_of_state_effect_closed | forEachLiteralOneEmpty => simp [stmtListTouchesUnsupportedStateSurface, stmtTouchesUnsupportedStateSurface] at hState + | forEachLiteralTwoEmpty => + simp [stmtListTouchesUnsupportedStateSurface, + stmtTouchesUnsupportedStateSurface] at hState | requireClause clause _ ih => simpa using Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.append @@ -5571,6 +5574,10 @@ theorem supportedStmtList_safe_of_state_except_mapping_writes_stmt_safety simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurface] at hState + | forEachLiteralTwoEmpty => + simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurface] at hState | @requireClause scope clause rest _ ih => have hTailSafety : ∀ stmt ∈ rest, StmtMappingWriteSlotSafe fields stmt := by diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 3ef9522ef..1eeb98d85 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -12783,17 +12783,59 @@ private def forEachOneCompiledIR (scope : List String) (varName : String) : (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])] [YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]] +private def forEachTwoUsedNames (scope : List String) (varName : String) : + List String := + varName :: (scope ++ collectExprNames (Expr.literal 2) ++ collectStmtListNames []) + +private def forEachTwoIdxName (scope : List String) (varName : String) : + String := + pickFreshName "__forEach_idx" (forEachTwoUsedNames scope varName) + +private def forEachTwoCountName (scope : List String) (varName : String) : + String := + pickFreshName "__forEach_count" + (forEachTwoIdxName scope varName :: forEachTwoUsedNames scope varName) + +private def forEachTwoInitStmts (scope : List String) (varName : String) : + List YulStmt := + [ + YulStmt.let_ (forEachTwoIdxName scope varName) (YulExpr.lit 0), + YulStmt.let_ (forEachTwoCountName scope varName) (YulExpr.lit 2), + YulStmt.let_ varName (YulExpr.lit 0) + ] + +private def forEachTwoCompiledIR (scope : List String) (varName : String) : + List YulStmt := + [YulStmt.for_ (forEachTwoInitStmts scope varName) + (YulExpr.call "lt" + [YulExpr.ident (forEachTwoIdxName scope varName), + YulExpr.ident (forEachTwoCountName scope varName)]) + [YulStmt.assign (forEachTwoIdxName scope varName) + (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]] + private def forEachZeroRuntimeLoop (runtime : SourceSemantics.RuntimeState) (varName : String) : SourceSemantics.RuntimeState := { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } +private def forEachLiteralRuntimeLoop + (runtime : SourceSemantics.RuntimeState) (varName : String) (value : Nat) : + SourceSemantics.RuntimeState := + { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName value } + private theorem source_bindValue_idem (bindings : List (String × Nat)) (name : String) (value : Nat) : SourceSemantics.bindValue (SourceSemantics.bindValue bindings name value) name value = SourceSemantics.bindValue bindings name value := by simp [SourceSemantics.bindValue, List.filter_filter] +private theorem source_bindValue_overwrite + (bindings : List (String × Nat)) (name : String) (first second : Nat) : + SourceSemantics.bindValue (SourceSemantics.bindValue bindings name first) name second = + SourceSemantics.bindValue bindings name second := by + simp [SourceSemantics.bindValue, List.filter_filter] + private theorem irState_setVar_idem (state : IRState) (name : String) (value : Nat) : (state.setVar name value).setVar name value = state.setVar name value := by @@ -12828,6 +12870,21 @@ private theorem sourceExec_forEach_literal_one_empty simp [SourceSemantics.execForEachLoop, SourceSemantics.execStmtList, forEachZeroRuntimeLoop, SourceSemantics.wordNormalize, source_bindValue_idem] +private theorem sourceExec_forEach_literal_two_empty + {fields : List Field} + {runtime : SourceSemantics.RuntimeState} + {varName : String} : + SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 2) []) = + .continue (forEachLiteralRuntimeLoop runtime varName 1) := by + change + SourceSemantics.execForEachLoop varName + (fun loopState => SourceSemantics.execStmtList fields loopState []) + (forEachZeroRuntimeLoop runtime varName) 0 2 = + .continue (forEachLiteralRuntimeLoop runtime varName 1) + simp [SourceSemantics.execForEachLoop, SourceSemantics.execStmtList, + forEachZeroRuntimeLoop, forEachLiteralRuntimeLoop, SourceSemantics.wordNormalize, + source_bindValue_idem, source_bindValue_overwrite] + private theorem forEachZero_fresh_facts {scope : List String} {varName : String} @@ -12885,6 +12942,34 @@ private theorem forEachOne_fresh_facts · intro hmem; exact hidxFreshUsed (by simp [forEachOneUsedNames, hmem]) · intro hmem; exact hcountFreshUsed (by simp [forEachOneUsedNames, hmem]) +private theorem forEachTwo_fresh_facts + {scope : List String} + {varName : String} : + let idxName := forEachTwoIdxName scope varName + let countName := forEachTwoCountName scope varName + idxName ≠ varName ∧ countName ≠ varName ∧ countName ≠ idxName ∧ + idxName ∉ scope ∧ countName ∉ scope := by + intro idxName countName + have hidxFreshUsed : + idxName ∉ forEachTwoUsedNames scope varName := by + simpa [idxName, forEachTwoIdxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" + (forEachTwoUsedNames scope varName) + have hcountFreshUsed : + countName ∉ idxName :: forEachTwoUsedNames scope varName := by + simpa [countName, forEachTwoCountName, idxName] using + CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" + (idxName :: forEachTwoUsedNames scope varName) + constructor + · intro h; exact hidxFreshUsed (by simp [forEachTwoUsedNames, h]) + constructor + · intro h; exact hcountFreshUsed (by simp [forEachTwoUsedNames, h]) + constructor + · intro h; exact hcountFreshUsed (by simp [h]) + constructor + · intro hmem; exact hidxFreshUsed (by simp [forEachTwoUsedNames, hmem]) + · intro hmem; exact hcountFreshUsed (by simp [forEachTwoUsedNames, hmem]) + private theorem evalIRExpr_forEachZeroCond_after_init {scope : List String} {varName : String} @@ -13101,6 +13186,232 @@ private theorem execIRStmts_forEach_literal_one_empty_compiled hinit hcondOne (by norm_num) hbody hpost] simpa [hloop, statePost, idxName, countName, stateLoop, stateCount, stateIdx] +private theorem execIRStmts_forEach_literal_two_empty_compiled + {scope : List String} + {varName : String} + {state : IRState} + {extraFuel : Nat} + (hslack : sizeOf (forEachTwoCompiledIR scope varName) - + (forEachTwoCompiledIR scope varName).length ≤ extraFuel) + (hidx_ne_var : forEachTwoIdxName scope varName ≠ varName) + (hcount_ne_var : forEachTwoCountName scope varName ≠ varName) + (hcount_ne_idx : + forEachTwoCountName scope varName ≠ forEachTwoIdxName scope varName) : + execIRStmts ((forEachTwoCompiledIR scope varName).length + extraFuel + 1) state + (forEachTwoCompiledIR scope varName) = + .continue ((((((state.setVar (forEachTwoIdxName scope varName) 0).setVar + (forEachTwoCountName scope varName) 2).setVar varName 0).setVar + (forEachTwoIdxName scope varName) 1).setVar varName 1).setVar + (forEachTwoIdxName scope varName) 2) := by + let idxName := forEachTwoIdxName scope varName + let countName := forEachTwoCountName scope varName + let stateIdx := state.setVar idxName 0 + let stateCount := stateIdx.setVar countName 2 + let stateLoop0 := stateCount.setVar varName 0 + let statePost0 := stateLoop0.setVar idxName 1 + let stateBody1 := statePost0.setVar varName 1 + let statePost1 := stateBody1.setVar idxName 2 + have hfuelInit : 4 ≤ extraFuel := by + have hmin : 4 ≤ + sizeOf (forEachTwoCompiledIR scope varName) - + (forEachTwoCompiledIR scope varName).length := by + dsimp [forEachTwoCompiledIR, forEachTwoInitStmts] + simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, + YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, + YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] + omega + omega + have hinit : execIRStmts extraFuel state + (forEachTwoInitStmts scope varName) = .continue stateLoop0 := by + simpa [forEachTwoInitStmts, stateIdx, stateCount, stateLoop0, idxName, countName] using + execIRStmts_forEach_init_literal + (fuel := extraFuel) (state := state) (idxName := idxName) + (countName := countName) (varName := varName) (bound := 2) (hfuel := hfuelInit) + have hidx_after_init : stateLoop0.getVar idxName = some 0 := by + dsimp [stateLoop0, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 hidx_ne_var] + rw [FunctionBody.getVar_setVar_ne _ countName idxName 2 hcount_ne_idx.symm] + simp + have hcount_after_init : stateLoop0.getVar countName = some 2 := by + dsimp [stateLoop0, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + have hcond0 : + evalIRExpr stateLoop0 (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) = some 1 := by + simp [idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after_init, hcount_after_init] + have hbody0 : execIRStmts extraFuel stateLoop0 + ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) = .continue stateLoop0 := by + cases extraFuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + stateLoop0.getVar (forEachTwoIdxName scope varName) = some 0 := by + simpa [idxName] using hidx_after_init + have hloop_idem : stateLoop0.setVar varName 0 = stateLoop0 := by + simpa [stateLoop0] using irState_setVar_idem stateCount varName 0 + simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read', hloop_idem] + have hpost0 : execIRStmts extraFuel stateLoop0 + ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) = .continue statePost0 := by + cases extraFuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + stateLoop0.getVar (forEachTwoIdxName scope varName) = some 0 := by + simpa [idxName] using hidx_after_init + dsimp [idxName, statePost0] + simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_read'] + have hidx_after_post0 : statePost0.getVar idxName = some 1 := by + exact FunctionBody.getVar_setVar_eq stateLoop0 idxName 1 + have hcount_after_post0 : statePost0.getVar countName = some 2 := by + dsimp [statePost0, stateLoop0, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ idxName countName 1 hcount_ne_idx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + have hcond1 : + evalIRExpr statePost0 (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) = some 1 := by + simp [idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after_post0, hcount_after_post0, Compiler.Constants.evmModulus, + Verity.Core.UINT256_MODULUS] + have hbody1 : execIRStmts extraFuel statePost0 + ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) = .continue stateBody1 := by + cases extraFuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + statePost0.getVar (forEachTwoIdxName scope varName) = some 1 := by + simpa [idxName] using hidx_after_post0 + dsimp [stateBody1] + simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read'] + have hidx_after_body1 : stateBody1.getVar idxName = some 1 := by + dsimp [stateBody1, statePost0] + rw [FunctionBody.getVar_setVar_ne _ varName idxName 1 hidx_ne_var] + simp + have hcount_after_body1 : stateBody1.getVar countName = some 2 := by + dsimp [stateBody1] + rw [FunctionBody.getVar_setVar_ne _ varName countName 1 hcount_ne_var] + exact hcount_after_post0 + have hpost1 : execIRStmts extraFuel stateBody1 + ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) = .continue statePost1 := by + cases extraFuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + stateBody1.getVar (forEachTwoIdxName scope varName) = some 1 := by + simpa [idxName] using hidx_after_body1 + dsimp [idxName, statePost1] + simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_read', Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS] + have hidx_after_post1 : statePost1.getVar idxName = some 2 := by + exact FunctionBody.getVar_setVar_eq stateBody1 idxName 2 + have hcount_after_post1 : statePost1.getVar countName = some 2 := by + dsimp [statePost1, stateBody1, statePost0, stateLoop0, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ idxName countName 2 hcount_ne_idx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 1 hcount_ne_var] + rw [FunctionBody.getVar_setVar_ne _ idxName countName 1 hcount_ne_idx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + have hcond2 : + evalIRExpr statePost1 (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) = some 0 := by + simp [idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_after_post1, hcount_after_post1] + have hloop1 : + execIRStmt extraFuel statePost0 + (.for_ [] (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) + ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) + ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))])) = + .continue statePost1 := by + cases extraFuel with + | zero => omega + | succ fuel => + have hbody1Fuel : execIRStmts fuel statePost0 + ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) = + .continue stateBody1 := by + cases fuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + statePost0.getVar (forEachTwoIdxName scope varName) = some 1 := by + simpa [idxName] using hidx_after_post0 + dsimp [stateBody1] + simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read'] + have hpost1Fuel : execIRStmts fuel stateBody1 + ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) = + .continue statePost1 := by + cases fuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read' : + stateBody1.getVar (forEachTwoIdxName scope varName) = some 1 := by + simpa [idxName] using hidx_after_body1 + dsimp [idxName, statePost1] + simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_read', Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS] + have htail : + execIRStmt fuel statePost1 + (.for_ [] (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) + ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) + ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))])) = + .continue statePost1 := by + cases fuel with + | zero => omega + | succ fuel => + exact execIRStmt_for_init_cond_zero fuel statePost1 statePost1 [] + ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) + ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) + (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) + (by simp [execIRStmts]) hcond2 + rw [execIRStmt_for_one_continue + (fuel := fuel) (state := statePost0) (sInit := statePost0) + (sBody := stateBody1) (sPost := statePost1) + (init := []) (post := [YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) + (body := [YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) + (cond := YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) + (condValue := 1)] + · exact htail + · simp [execIRStmts] + · exact hcond1 + · norm_num + · exact hbody1Fuel + · exact hpost1Fuel + dsimp [forEachTwoCompiledIR] + have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega + rw [← hfuelEq] + rw [execIRStmts_single_for_one_continue + (fuel := extraFuel) (state := state) (sInit := stateLoop0) (sBody := stateLoop0) + (sPost := statePost0) (init := forEachTwoInitStmts scope varName) + (post := [YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) + (body := [YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) + (cond := YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) (condValue := 1) + hinit hcond0 (by norm_num) hbody0 hpost0] + simpa [hloop1, statePost1, stateBody1, statePost0, idxName, countName, stateLoop0, + stateCount, stateIdx] + private theorem forEachZero_nextScopeIncluded {scope : List String} {varName : String} @@ -13288,6 +13599,124 @@ private theorem stmtStepMatches_forEach_literal_one_empty_final hboundedLoop, FunctionBody.scopeNamesPresent_of_included hscopeLoop hNextScopeIncl⟩ +private theorem stmtStepMatches_forEach_literal_two_empty_final + {fields : List Field} + {scope : List String} + {varName : String} + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + (hidx_ne_var : forEachTwoIdxName scope varName ≠ varName) + (hidx_not_scope : forEachTwoIdxName scope varName ∉ scope) + (hcount_not_scope : forEachTwoCountName scope varName ∉ scope) + (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings state) + (hscope : FunctionBody.scopeNamesPresent scope runtime.bindings) + (hbounded : FunctionBody.bindingsBounded runtime.bindings) + (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : + stmtStepMatchesIRExec fields + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 2) [])) + (.continue (forEachLiteralRuntimeLoop runtime varName 1)) + (.continue ((((((state.setVar (forEachTwoIdxName scope varName) 0).setVar + (forEachTwoCountName scope varName) 2).setVar varName 0).setVar + (forEachTwoIdxName scope varName) 1).setVar varName 1).setVar + (forEachTwoIdxName scope varName) 2)) := by + let idxName := forEachTwoIdxName scope varName + let countName := forEachTwoCountName scope varName + let runtimeLoop := forEachLiteralRuntimeLoop runtime varName 1 + let stateCount := (state.setVar idxName 0).setVar countName 2 + let stateLoop0 := stateCount.setVar varName 0 + let statePost0 := stateLoop0.setVar idxName 1 + let stateBody1 := statePost0.setVar varName 1 + let statePost1 := stateBody1.setVar idxName 2 + have hidx_not_next : idxName ∉ varName :: scope := by + intro hmem + simp at hmem + rcases hmem with hvar | hscopeMem + · exact hidx_ne_var hvar + · exact hidx_not_scope hscopeMem + have hexactCount : + FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateCount := by + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope) + hcount_not_scope + have hexactLoop0 : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + (forEachZeroRuntimeLoop runtime varName).bindings stateLoop0 := by + simpa [forEachZeroRuntimeLoop, stateLoop0] using + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue + (boundName := varName) (value := 0) hexactCount + have hexactPost0 : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + (forEachZeroRuntimeLoop runtime varName).bindings statePost0 := by + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (state := stateLoop0) (tempName := idxName) (value := 1) + hexactLoop0 hidx_not_next + have hexactPost0Scope : + FunctionBody.bindingsExactlyMatchIRVarsOnScope scope + (forEachZeroRuntimeLoop runtime varName).bindings statePost0 := by + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactPost0 + (by intro name hmem; simp [hmem]) + have hexactBody1 : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeLoop.bindings stateBody1 := by + simpa [runtimeLoop, forEachLiteralRuntimeLoop, forEachZeroRuntimeLoop, stateBody1, + source_bindValue_overwrite] using + FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue + (boundName := varName) (value := 1) + (scope := scope) + (bindings := (forEachZeroRuntimeLoop runtime varName).bindings) + (state := statePost0) + hexactPost0Scope + have hexactPost1 : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeLoop.bindings statePost1 := by + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (state := stateBody1) (tempName := idxName) (value := 2) + hexactBody1 hidx_not_next + have hruntimePost1 : + FunctionBody.runtimeStateMatchesIR fields runtimeLoop statePost1 := by + simpa [runtimeLoop, forEachLiteralRuntimeLoop, stateLoop0, stateCount, statePost0, + stateBody1, statePost1] using + FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (state := ((stateCount.setVar varName 0).setVar idxName 1).setVar varName 1) + (name := idxName) (value := 2) + (FunctionBody.runtimeStateMatchesIR_setVar_bindValue + (fields := fields) + (runtime := runtime) + (state := (stateCount.setVar varName 0).setVar idxName 1) + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (state := stateCount.setVar varName 0) (name := idxName) (value := 1) + (FunctionBody.runtimeStateMatchesIR_setVar_bindValue + (fields := fields) + (runtime := runtime) + (state := stateCount) + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime)) + varName 0)) + varName 1) + have hscopeLoop : FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by + simpa [runtimeLoop, forEachLiteralRuntimeLoop] using + FunctionBody.scopeNamesPresent_cons_bindValue + (boundName := varName) (value := 1) hscope + have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := + FunctionBody.bindingsBounded_bindValue hbounded varName 1 + (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) + have hNextScopeIncl : + FunctionBody.scopeNamesIncluded + (stmtNextScope scope (Stmt.forEach varName (Expr.literal 2) [])) + (varName :: scope) := by + intro name hmem + simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem + rcases hmem with hvar | hscopeMem + · simp [hvar] + · rcases hscopeMem with hbody | hscopeMem + · exact False.elim (by simpa [collectStmtListNames] using hbody) + · simp [hscopeMem] + simp [stmtStepMatchesIRExec] + exact ⟨hruntimePost1, + FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactPost1 hNextScopeIncl, + hboundedLoop, + FunctionBody.scopeNamesPresent_of_included hscopeLoop hNextScopeIncl⟩ + private theorem compiledStmtStep_forEach_literal_zero {fields : List Field} {scope : List String} @@ -13362,6 +13791,41 @@ private theorem compiledStmtStep_forEach_literal_one_empty hidx_ne_var hidx_not_scope hcount_not_scope hexact hscope hbounded hruntime +private theorem compiledStmtStep_forEach_literal_two_empty + {fields : List Field} + {scope : List String} + {varName : String} : + ∃ compiledIR, + CompiledStmtStep fields scope (Stmt.forEach varName (Expr.literal 2) []) compiledIR := by + refine ⟨forEachTwoCompiledIR scope varName, ?_⟩ + refine + { compileOk := ?_ + preserves := ?_ } + · dsimp [forEachTwoCompiledIR, forEachTwoInitStmts, forEachTwoIdxName, + forEachTwoCountName, forEachTwoUsedNames] + simp [CompilationModel.compileStmt, CompilationModel.compileStmtList, + CompilationModel.compileExpr, + CompilationModel.uint256Modulus] + rfl + · intro runtime state extraFuel hexact hscope hbounded hruntime hslack + rcases forEachTwo_fresh_facts (scope := scope) (varName := varName) with + ⟨hidx_ne_var, hcount_ne_var, hcount_ne_idx, hidx_not_scope, hcount_not_scope⟩ + refine ⟨.continue (forEachLiteralRuntimeLoop runtime varName 1), + .continue ((((((state.setVar (forEachTwoIdxName scope varName) 0).setVar + (forEachTwoCountName scope varName) 2).setVar varName 0).setVar + (forEachTwoIdxName scope varName) 1).setVar varName 1).setVar + (forEachTwoIdxName scope varName) 2), + sourceExec_forEach_literal_two_empty, ?_, ?_⟩ + · exact execIRStmts_forEach_literal_two_empty_compiled + (scope := scope) (varName := varName) + (state := state) (extraFuel := extraFuel) hslack + hidx_ne_var hcount_ne_var hcount_ne_idx + · exact stmtStepMatches_forEach_literal_two_empty_final + (fields := fields) (scope := scope) (varName := varName) + (runtime := runtime) (state := state) + hidx_ne_var hidx_not_scope hcount_not_scope + hexact hscope hbounded hruntime + /-- Extra Tier 2 assumptions needed to turn the singleton mapping-write constructors in `SupportedStmtList` into real compiled-step proofs. These are @@ -14953,6 +15417,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface (fields := fields) (scope := scope) (varName := varName) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil + | forEachLiteralTwoEmpty => + rename_i scope varName + rcases compiledStmtStep_forEach_literal_two_empty + (fields := fields) (scope := scope) (varName := varName) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => simp [stmtListTouchesUnsupportedContractSurface] at hsurface apply stmtListGenericCore_append @@ -15096,6 +15566,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites (fields := fields) (scope := scope) (varName := varName) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil + | forEachLiteralTwoEmpty => + rename_i scope varName + rcases compiledStmtStep_forEach_literal_two_empty + (fields := fields) (scope := scope) (varName := varName) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause ih hsurface @@ -15428,6 +15904,12 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites_ (fields := fields) (scope := scope) (varName := varName) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil + | forEachLiteralTwoEmpty => + rename_i scope varName + rcases compiledStmtStep_forEach_literal_two_empty + (fields := fields) (scope := scope) (varName := varName) with + ⟨compiledIR, hstep⟩ + exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause hsupportedRest ih => exact stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites clause diff --git a/Compiler/Proofs/IRGeneration/SupportedFragment.lean b/Compiler/Proofs/IRGeneration/SupportedFragment.lean index 9681c453a..01dc69590 100644 --- a/Compiler/Proofs/IRGeneration/SupportedFragment.lean +++ b/Compiler/Proofs/IRGeneration/SupportedFragment.lean @@ -354,6 +354,13 @@ inductive SupportedStmtList (fields : List Field) : List String → List Stmt {scope : List String} {varName : String} : SupportedStmtList fields scope [Stmt.forEach varName (.literal 1) []] + /-- Second positive loop-preservation case: a two-iteration loop with an + empty body. This proves one recursive empty-init `for` step after the first + post, which is the recurrence shape needed for larger literal bounds. -/ + | forEachLiteralTwoEmpty + {scope : List String} + {varName : String} : + SupportedStmtList fields scope [Stmt.forEach varName (.literal 2) []] | requireClause {scope : List String} (clause : RequireLiteralGuardFamilyClause) diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index f234e1e61..812488938 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -3015,6 +3015,10 @@ theorem SupportedStmtList.helperSurfaceClosed simp [stmtListTouchesUnsupportedHelperSurface, stmtTouchesUnsupportedHelperSurface, exprTouchesUnsupportedHelperSurface] + | forEachLiteralTwoEmpty => + simp [stmtListTouchesUnsupportedHelperSurface, + stmtTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedHelperSurface] | requireClause clause _ ih => simp [stmtListTouchesUnsupportedHelperSurface] constructor @@ -3200,6 +3204,9 @@ theorem SupportedStmtList.internalHelperCallNames_nil | forEachLiteralOneEmpty => simp [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, exprInternalHelperCallNames] + | forEachLiteralTwoEmpty => + simp [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, + exprInternalHelperCallNames] | requireClause clause _ ih => simp [stmtListInternalHelperCallNames] constructor @@ -5075,6 +5082,8 @@ private theorem supportedStmtList_usesArrayElement_false exprUsesArrayElement] using ih | forEachLiteralOneEmpty => simp [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] + | forEachLiteralTwoEmpty => + simp [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] | requireClause clause _ ih => simp only [stmtListUsesArrayElement, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5195,6 +5204,9 @@ private theorem supportedStmtList_usesStorageArrayElement_false | forEachLiteralOneEmpty => simp [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, exprUsesStorageArrayElement] + | forEachLiteralTwoEmpty => + simp [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, + exprUsesStorageArrayElement] | requireClause clause _ ih => simp only [stmtListUsesStorageArrayElement, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5308,6 +5320,9 @@ private theorem supportedStmtList_usesDynamicBytesEq_false | forEachLiteralOneEmpty => simp [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, exprUsesDynamicBytesEq] + | forEachLiteralTwoEmpty => + simp [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, + exprUsesDynamicBytesEq] | requireClause clause _ ih => simp only [stmtListUsesDynamicBytesEq, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5678,6 +5693,8 @@ private theorem supportedStmtList_usesMulDiv512_false exprUsesMulDiv512] using ih | forEachLiteralOneEmpty => simp [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] + | forEachLiteralTwoEmpty => + simp [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] | requireClause clause _ ih => simp only [stmtListUsesMulDiv512, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => @@ -5791,6 +5808,9 @@ private theorem supportedStmtList_usesParamDynamicHeadWord_false | forEachLiteralOneEmpty => simp [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, exprUsesParamDynamicHeadWord] + | forEachLiteralTwoEmpty => + simp [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, + exprUsesParamDynamicHeadWord] | requireClause clause _ ih => simp only [stmtListUsesParamDynamicHeadWord, Bool.or_eq_false_iff, Bool.false_or] exact ⟨by cases clause with | mk family n m p q message => From 824562c52c54dee653023413cf9baea939eca7bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 May 2026 08:11:01 +0100 Subject: [PATCH 15/27] chore: auto-refresh derived artifacts --- PrintAxioms.lean | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PrintAxioms.lean b/PrintAxioms.lean index 5c2e6c4e9..e6dce6b5a 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -2430,22 +2430,28 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_mstoreSingle_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_tstoreSingle_of_surface -- private -- Compiler.Proofs.IRGeneration.source_bindValue_idem -- private + -- Compiler.Proofs.IRGeneration.source_bindValue_overwrite -- private -- Compiler.Proofs.IRGeneration.irState_setVar_idem -- private -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_zero -- private -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_one_empty -- private + -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_two_empty -- private -- Compiler.Proofs.IRGeneration.forEachZero_fresh_facts -- private -- Compiler.Proofs.IRGeneration.forEachOne_fresh_facts -- private + -- Compiler.Proofs.IRGeneration.forEachTwo_fresh_facts -- private -- Compiler.Proofs.IRGeneration.evalIRExpr_forEachZeroCond_after_init -- private -- Compiler.Proofs.IRGeneration.forEachZero_initFuel_of_slack -- private -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_zero_compiled -- private -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_one_empty_compiled -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_two_empty_compiled -- private -- Compiler.Proofs.IRGeneration.forEachZero_nextScopeIncluded -- private -- Compiler.Proofs.IRGeneration.runtimeStateMatchesIR_forEachZeroLoop -- private -- Compiler.Proofs.IRGeneration.bindingsExactly_forEachZeroBase -- private -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_zero_final -- private -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_one_empty_final -- private + -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_two_empty_final -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_zero -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_one_empty -- private + -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_two_empty -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingUintSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingChainSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingSingle_of_slotSafety -- private @@ -5515,4 +5521,4 @@ end Verity.AxiomAudit Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args ] --- Total: 5217 theorems/lemmas (3612 public, 1605 private, 0 sorry'd) +-- Total: 5223 theorems/lemmas (3612 public, 1611 private, 0 sorry'd) From 63f547388998a2cc255d94cc182a0490b521f524 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 09:12:52 +0200 Subject: [PATCH 16/27] Allowlist staged forEach proof shapes --- scripts/check_proof_length.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/check_proof_length.py b/scripts/check_proof_length.py index 537814807..f74e07128 100644 --- a/scripts/check_proof_length.py +++ b/scripts/check_proof_length.py @@ -80,6 +80,14 @@ "compiledStmtStep_require", "compiledStmtStep_return", "compiledStmtStep_ite", + # Staged forEach proof witnesses for PR #1935. These are deliberately + # explicit loop-shape proofs for bounds 1 and 2; the next proof step is to + # replace them with a Nat-indexed recurrence instead of mechanically + # splitting the same init/cond/body/post state facts into tiny local lemmas. + "execIRStmts_forEach_literal_one_empty_compiled", + "execIRStmts_forEach_literal_two_empty_compiled", + "stmtStepMatches_forEach_literal_one_empty_final", + "stmtStepMatches_forEach_literal_two_empty_final", # --- Storage write compiled step proofs --- "compiledStmtStep_setStorage_singleSlot", "compiledStmtStep_setStorage_aliasSlots", From d64cd781580f92280df7839e718f99c47158235b Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 11:35:30 +0200 Subject: [PATCH 17/27] Prove generic empty forEach loops --- Compiler/Proofs/EndToEnd.lean | 11 +- .../Proofs/IRGeneration/GenericInduction.lean | 1405 ++++++++--------- .../Proofs/IRGeneration/IRInterpreter.lean | 101 ++ .../Proofs/IRGeneration/SourceSemantics.lean | 41 + .../IRGeneration/SupportedFragment.lean | 19 +- .../Proofs/IRGeneration/SupportedSpec.lean | 66 +- PrintAxioms.lean | 15 +- scripts/check_proof_length.py | 12 +- 8 files changed, 884 insertions(+), 786 deletions(-) diff --git a/Compiler/Proofs/EndToEnd.lean b/Compiler/Proofs/EndToEnd.lean index 1f7c89895..b1e524209 100644 --- a/Compiler/Proofs/EndToEnd.lean +++ b/Compiler/Proofs/EndToEnd.lean @@ -5294,10 +5294,7 @@ theorem supportedStmtList_safe_of_state_effect_closed | forEachLiteralBounded => simp [stmtListTouchesUnsupportedStateSurface, stmtTouchesUnsupportedStateSurface] at hState - | forEachLiteralOneEmpty => - simp [stmtListTouchesUnsupportedStateSurface, - stmtTouchesUnsupportedStateSurface] at hState - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListTouchesUnsupportedStateSurface, stmtTouchesUnsupportedStateSurface] at hState | requireClause clause _ ih => @@ -5570,11 +5567,7 @@ theorem supportedStmtList_safe_of_state_except_mapping_writes_stmt_safety simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurface] at hState - | forEachLiteralOneEmpty => - simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, - stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, - stmtTouchesUnsupportedStateSurface] at hState - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, stmtTouchesUnsupportedStateSurface] at hState diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 1eeb98d85..84dc2b026 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -1043,7 +1043,14 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS | zero => have hbodySurface : stmtListTouchesUnsupportedContractSurface body = false := by - simpa [stmtTouchesUnsupportedContractSurface] using hsurface + cases body with + | nil => + simp [stmtListTouchesUnsupportedContractSurface] + | cons stmt rest => + simp only [stmtTouchesUnsupportedContractSurface, + stmtListTouchesUnsupportedContractSurface, + Bool.or_eq_false_iff] at hsurface + exact Bool.or_eq_false_iff.mpr hsurface simp only [CompilationModel.compileStmt, bind, Except.bind] at hcompile cases hbody : CompilationModel.compileStmtList fields events errors .calldata [] false @@ -1079,7 +1086,42 @@ theorem legacyCompatibleExternalStmtList_of_compileStmt_ok_on_supportedContractS hnoPacked hbodySurface hbody) LegacyCompatibleExternalStmtList.nil) | succ n => - simp [stmtTouchesUnsupportedContractSurface] at hsurface + cases body with + | nil => + simp only [CompilationModel.compileStmt, bind, Except.bind] at hcompile + simp [CompilationModel.compileExpr, CompilationModel.compileStmtList] at hcompile + cases hcompile + let forUsedNames := + varName :: (inScopeNames ++ + collectExprNames (Expr.literal (n + 1)) ++ collectStmtListNames []) + let idxName := pickFreshName "__forEach_idx" forUsedNames + let countName := pickFreshName "__forEach_count" (idxName :: forUsedNames) + let initStmts := [ + YulStmt.let_ idxName (YulExpr.lit 0), + YulStmt.let_ countName + (YulExpr.lit ((n + 1) % CompilationModel.uint256Modulus)), + YulStmt.let_ varName (YulExpr.lit 0) + ] + let condExpr := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName] + let postStmts := [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + let bodyWithBind := [YulStmt.assign varName (YulExpr.ident idxName)] + simpa [forUsedNames, idxName, countName, initStmts, condExpr, + postStmts, bodyWithBind] using + (LegacyCompatibleExternalStmtList.for_ initStmts condExpr postStmts bodyWithBind [] + (LegacyCompatibleExternalStmtList.let_ idxName (YulExpr.lit 0) _ + (LegacyCompatibleExternalStmtList.let_ countName + (YulExpr.lit ((n + 1) % CompilationModel.uint256Modulus)) _ + (LegacyCompatibleExternalStmtList.let_ varName (YulExpr.lit 0) _ + LegacyCompatibleExternalStmtList.nil))) + (LegacyCompatibleExternalStmtList.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1]) _ + LegacyCompatibleExternalStmtList.nil) + (LegacyCompatibleExternalStmtList.assign varName (YulExpr.ident idxName) [] + LegacyCompatibleExternalStmtList.nil) + LegacyCompatibleExternalStmtList.nil) + | cons _ _ => + simp [stmtTouchesUnsupportedContractSurface] at hsurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hsurface | _ => @@ -3203,6 +3245,9 @@ inductive StmtListScopeDiscipline (fieldNames : List String) : List String → L StmtListScopeDiscipline fieldNames (varName :: scope) body → StmtListScopeDiscipline fieldNames (stmtNextScope scope (.forEach varName (.literal 0) body)) rest → StmtListScopeDiscipline fieldNames scope (.forEach varName (.literal 0) body :: rest) + | forEachLiteralEmpty {scope : List String} {varName : String} {n : Nat} {rest : List Stmt} : + StmtListScopeDiscipline fieldNames (stmtNextScope scope (.forEach varName (.literal n) [])) rest → + StmtListScopeDiscipline fieldNames scope (.forEach varName (.literal n) [] :: rest) /-- Syntax-side witness for the current generic statement fragment, before the scope obligations are discharged from identifier validation. -/ @@ -3263,6 +3308,9 @@ inductive StmtListScopeCore (fieldNames : List String) : List Stmt → Prop wher StmtListScopeCore fieldNames body → StmtListScopeCore fieldNames rest → StmtListScopeCore fieldNames (.forEach varName (.literal 0) body :: rest) + | forEachLiteralEmpty {varName : String} {n : Nat} {rest : List Stmt} : + StmtListScopeCore fieldNames rest → + StmtListScopeCore fieldNames (.forEach varName (.literal n) [] :: rest) private theorem exprCompileCore_of_exprTouchesUnsupportedContractSurface_eq_false {expr : Expr} @@ -3556,7 +3604,14 @@ private theorem stmtListScopeCore_of_unsupportedContractSurface_eq_false | zero => have hbodySurface : stmtListTouchesUnsupportedContractSurface body = false := by - simpa [stmtTouchesUnsupportedContractSurface] using hstmtSurface + cases body with + | nil => + simp [stmtListTouchesUnsupportedContractSurface] + | cons stmt rest => + simp only [stmtTouchesUnsupportedContractSurface, + stmtListTouchesUnsupportedContractSurface, + Bool.or_eq_false_iff] at hstmtSurface + exact Bool.or_eq_false_iff.mpr hstmtSurface simp only [CompilationModel.compileStmt, bind, Except.bind] at hhead cases hbody : CompilationModel.compileStmtList fields [] [] .calldata [] false @@ -3568,7 +3623,11 @@ private theorem stmtListScopeCore_of_unsupportedContractSurface_eq_false fields (varName :: scope) body loopBodyIR hbodySurface hbody) ihRest | succ n => - simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface + cases body with + | nil => + exact .forEachLiteralEmpty ihRest + | cons _ _ => + simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface @@ -3681,7 +3740,14 @@ theorem stmtListScopeCore_prefix_of_compileStmtList_ok_of_stmtListTouchesUnsuppo | zero => have hbodySurface : stmtListTouchesUnsupportedContractSurface body = false := by - simpa [stmtTouchesUnsupportedContractSurface] using hstmtSurface + cases body with + | nil => + simp [stmtListTouchesUnsupportedContractSurface] + | cons stmt rest => + simp only [stmtTouchesUnsupportedContractSurface, + stmtListTouchesUnsupportedContractSurface, + Bool.or_eq_false_iff] at hstmtSurface + exact Bool.or_eq_false_iff.mpr hstmtSurface simp only [CompilationModel.compileStmt, bind, Except.bind] at hhead cases hbody : CompilationModel.compileStmtList fields [] [] .calldata [] false @@ -3693,7 +3759,11 @@ theorem stmtListScopeCore_prefix_of_compileStmtList_ok_of_stmtListTouchesUnsuppo fields (varName :: scope) body loopBodyIR hbodySurface hbody) (ih hrestSurface htail) | succ n => - simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface + cases body with + | nil => + exact StmtListScopeCore.forEachLiteralEmpty (ih hrestSurface htail) + | cons _ _ => + simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hstmtSurface | setMapping _ _ _ | setMappingWord _ _ _ _ | setMappingPackedWord _ _ _ _ _ @@ -4472,6 +4542,32 @@ private theorem stmtListScopeDiscipline_of_validateScopedStmtListIdentifiers (by intro other hmem exact mem_stmtNextScope_of_mem_scope (hlocalsInScope other hmem))) + | forEachLiteralEmpty hrestCore ihRest => + rcases validateScopedStmtListIdentifiers_cons_ok_inv hvalidate with + ⟨nextLocalScope, hstmt, hrestValidate⟩ + have hstmt' := hstmt + unfold validateScopedStmtIdentifiers at hstmt' + revert hstmt' + simp only [bind, Except.bind, pure, Except.pure] + intro hstmt' + rcases hCountVal : + validateScopedExprIdentifiers context params paramScope dynamicParams localScope + constructorArgCount (Expr.literal _) with _ | _ + · rw [hCountVal] at hstmt'; simp at hstmt' + · rw [hCountVal] at hstmt' + rcases hBodyVal : + validateScopedStmtListIdentifiers context params paramScope dynamicParams + (_ :: localScope) constructorArgCount [] with _ | _ + · rw [hBodyVal] at hstmt'; simp at hstmt' + · rw [hBodyVal] at hstmt'; simp at hstmt'; cases hstmt' + exact StmtListScopeDiscipline.forEachLiteralEmpty + (ihRest hrestValidate + (by + intro other hmem + exact mem_stmtNextScope_of_mem_scope (hparamsInScope other hmem)) + (by + intro other hmem + exact mem_stmtNextScope_of_mem_scope (hlocalsInScope other hmem))) theorem stmtListScopeDiscipline_of_validateFunctionIdentifierReferences_prefix {spec : FunctionSpec} {fieldNames : List String} @@ -4560,10 +4656,10 @@ private theorem scopeNamesPresent_foldl_stmtNextScope_of_validateScopedStmtListI exact ih hrestValidate (by intro name hname - exact mem_stmtNextScope_of_mem_scope (hparamsInScope name hname)) + exact mem_stmtNextScope_of_mem_scope (stmt := _) (hparamsInScope name hname)) (by intro name hname - exact mem_stmtNextScope_of_mem_scope (hlocalsInScope name hname)) + exact mem_stmtNextScope_of_mem_scope (stmt := _) (hlocalsInScope name hname)) other hmem | require hcondCore hrest ih => rcases validateScopedStmtListIdentifiers_cons_ok_inv hvalidate with @@ -4762,9 +4858,36 @@ private theorem scopeNamesPresent_foldl_stmtNextScope_of_validateScopedStmtListI (by intro name hname exact mem_stmtNextScope_of_mem_scope (hparamsInScope name hname)) + (by + intro name hname + exact mem_stmtNextScope_of_mem_scope (hlocalsInScope name hname)) + other hmem + | forEachLiteralEmpty hrestCore ihRest => + rcases validateScopedStmtListIdentifiers_cons_ok_inv hvalidate with + ⟨nextLocalScope, hstmt, hrestValidate⟩ + have hstmt' := hstmt + unfold validateScopedStmtIdentifiers at hstmt' + revert hstmt' + simp only [bind, Except.bind, pure, Except.pure] + intro hstmt' + rcases hCountVal : + validateScopedExprIdentifiers context params paramScope dynamicParams localScope + constructorArgCount (Expr.literal _) with _ | _ + · rw [hCountVal] at hstmt'; simp at hstmt' + · rw [hCountVal] at hstmt' + rcases hBodyVal : + validateScopedStmtListIdentifiers context params paramScope dynamicParams + (_ :: localScope) constructorArgCount [] with _ | _ + · rw [hBodyVal] at hstmt'; simp at hstmt' + · rw [hBodyVal] at hstmt'; simp at hstmt'; cases hstmt' + intro other hmem + exact ihRest hrestValidate (by intro name hname - exact mem_stmtNextScope_of_mem_scope (hlocalsInScope name hname)) + exact mem_stmtNextScope_of_mem_scope (stmt := _) (hparamsInScope name hname)) + (by + intro name hname + exact mem_stmtNextScope_of_mem_scope (stmt := _) (hlocalsInScope name hname)) other hmem private theorem exprBoundNamesInScope_setStorage_of_validateFunctionIdentifierReferences @@ -5069,6 +5192,14 @@ private theorem stmtListScopeDiscipline_scope_names collectStmtListAssignedNames, collectStmtAssignedNames, hassignRest] · simp [collectStmtListBindNames, collectStmtBindNames, collectStmtListAssignedNames, collectStmtAssignedNames, hfield] + | @forEachLiteralEmpty scope varName n rest _ ihRest => + intro other hmem + simp only [List.foldl] at hmem + have htail := ihRest other hmem + simp [stmtNextScope, collectStmtNames, collectExprNames, + collectStmtListNames, collectStmtListBindNames, collectStmtBindNames, + collectStmtListAssignedNames, collectStmtAssignedNames] at htail ⊢ + tauto theorem compiledStmtStep_letVar {fields : List Field} @@ -12752,78 +12883,46 @@ private def forEachZeroCompiledIR (forEachZeroPostStmts scope varName body) (forEachZeroBodyWithBind scope varName body bodyIR)] -private def forEachOneUsedNames (scope : List String) (varName : String) : - List String := - varName :: (scope ++ collectExprNames (Expr.literal 1) ++ collectStmtListNames []) +private def forEachLiteralBound (n : Nat) : Nat := + SourceSemantics.wordNormalize n -private def forEachOneIdxName (scope : List String) (varName : String) : - String := - pickFreshName "__forEach_idx" (forEachOneUsedNames scope varName) - -private def forEachOneCountName (scope : List String) (varName : String) : - String := - pickFreshName "__forEach_count" - (forEachOneIdxName scope varName :: forEachOneUsedNames scope varName) - -private def forEachOneInitStmts (scope : List String) (varName : String) : - List YulStmt := - [ - YulStmt.let_ (forEachOneIdxName scope varName) (YulExpr.lit 0), - YulStmt.let_ (forEachOneCountName scope varName) (YulExpr.lit 1), - YulStmt.let_ varName (YulExpr.lit 0) - ] - -private def forEachOneCompiledIR (scope : List String) (varName : String) : - List YulStmt := - [YulStmt.for_ (forEachOneInitStmts scope varName) - (YulExpr.call "lt" - [YulExpr.ident (forEachOneIdxName scope varName), - YulExpr.ident (forEachOneCountName scope varName)]) - [YulStmt.assign (forEachOneIdxName scope varName) - (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])] - [YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]] - -private def forEachTwoUsedNames (scope : List String) (varName : String) : +private def forEachLiteralUsedNames (scope : List String) (varName : String) (n : Nat) : List String := - varName :: (scope ++ collectExprNames (Expr.literal 2) ++ collectStmtListNames []) + varName :: (scope ++ collectExprNames (Expr.literal n) ++ collectStmtListNames []) -private def forEachTwoIdxName (scope : List String) (varName : String) : +private def forEachLiteralIdxName (scope : List String) (varName : String) (n : Nat) : String := - pickFreshName "__forEach_idx" (forEachTwoUsedNames scope varName) + pickFreshName "__forEach_idx" (forEachLiteralUsedNames scope varName n) -private def forEachTwoCountName (scope : List String) (varName : String) : +private def forEachLiteralCountName (scope : List String) (varName : String) (n : Nat) : String := pickFreshName "__forEach_count" - (forEachTwoIdxName scope varName :: forEachTwoUsedNames scope varName) + (forEachLiteralIdxName scope varName n :: forEachLiteralUsedNames scope varName n) -private def forEachTwoInitStmts (scope : List String) (varName : String) : +private def forEachLiteralInitStmts (scope : List String) (varName : String) (n : Nat) : List YulStmt := [ - YulStmt.let_ (forEachTwoIdxName scope varName) (YulExpr.lit 0), - YulStmt.let_ (forEachTwoCountName scope varName) (YulExpr.lit 2), + YulStmt.let_ (forEachLiteralIdxName scope varName n) (YulExpr.lit 0), + YulStmt.let_ (forEachLiteralCountName scope varName n) + (YulExpr.lit (forEachLiteralBound n)), YulStmt.let_ varName (YulExpr.lit 0) ] -private def forEachTwoCompiledIR (scope : List String) (varName : String) : +private def forEachLiteralCompiledIR (scope : List String) (varName : String) (n : Nat) : List YulStmt := - [YulStmt.for_ (forEachTwoInitStmts scope varName) + [YulStmt.for_ (forEachLiteralInitStmts scope varName n) (YulExpr.call "lt" - [YulExpr.ident (forEachTwoIdxName scope varName), - YulExpr.ident (forEachTwoCountName scope varName)]) - [YulStmt.assign (forEachTwoIdxName scope varName) - (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])] - [YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]] + [YulExpr.ident (forEachLiteralIdxName scope varName n), + YulExpr.ident (forEachLiteralCountName scope varName n)]) + [YulStmt.assign (forEachLiteralIdxName scope varName n) + (YulExpr.call "add" [YulExpr.ident (forEachLiteralIdxName scope varName n), YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident (forEachLiteralIdxName scope varName n))]] private def forEachZeroRuntimeLoop (runtime : SourceSemantics.RuntimeState) (varName : String) : SourceSemantics.RuntimeState := { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } -private def forEachLiteralRuntimeLoop - (runtime : SourceSemantics.RuntimeState) (varName : String) (value : Nat) : - SourceSemantics.RuntimeState := - { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName value } - private theorem source_bindValue_idem (bindings : List (String × Nat)) (name : String) (value : Nat) : SourceSemantics.bindValue (SourceSemantics.bindValue bindings name value) name value = @@ -12856,34 +12955,25 @@ private theorem sourceExec_forEach_literal_zero .continue (forEachZeroRuntimeLoop runtime varName) rfl -private theorem sourceExec_forEach_literal_one_empty - {fields : List Field} - {runtime : SourceSemantics.RuntimeState} - {varName : String} : - SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 1) []) = - .continue (forEachZeroRuntimeLoop runtime varName) := by - change - SourceSemantics.execForEachLoop varName - (fun loopState => SourceSemantics.execStmtList fields loopState []) - (forEachZeroRuntimeLoop runtime varName) 0 1 = - .continue (forEachZeroRuntimeLoop runtime varName) - simp [SourceSemantics.execForEachLoop, SourceSemantics.execStmtList, - forEachZeroRuntimeLoop, SourceSemantics.wordNormalize, source_bindValue_idem] - -private theorem sourceExec_forEach_literal_two_empty +private theorem sourceExec_forEach_literal_empty {fields : List Field} {runtime : SourceSemantics.RuntimeState} - {varName : String} : - SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal 2) []) = - .continue (forEachLiteralRuntimeLoop runtime varName 1) := by + {varName : String} + {n : Nat} : + SourceSemantics.execStmt fields runtime (Stmt.forEach varName (Expr.literal n) []) = + .continue + (SourceSemantics.execForEachEmptyLoopFinal varName + (forEachZeroRuntimeLoop runtime varName) 0 (forEachLiteralBound n)) := by change SourceSemantics.execForEachLoop varName (fun loopState => SourceSemantics.execStmtList fields loopState []) - (forEachZeroRuntimeLoop runtime varName) 0 2 = - .continue (forEachLiteralRuntimeLoop runtime varName 1) - simp [SourceSemantics.execForEachLoop, SourceSemantics.execStmtList, - forEachZeroRuntimeLoop, forEachLiteralRuntimeLoop, SourceSemantics.wordNormalize, - source_bindValue_idem, source_bindValue_overwrite] + (forEachZeroRuntimeLoop runtime varName) 0 (SourceSemantics.wordNormalize n) = + .continue + (SourceSemantics.execForEachEmptyLoopFinal varName + (forEachZeroRuntimeLoop runtime varName) 0 (forEachLiteralBound n)) + simpa [SourceSemantics.execStmtList, forEachLiteralBound] using + SourceSemantics.execForEachLoop_empty_body varName + (forEachZeroRuntimeLoop runtime varName) 0 (SourceSemantics.wordNormalize n) private theorem forEachZero_fresh_facts {scope : List String} @@ -12914,61 +13004,34 @@ private theorem forEachZero_fresh_facts · intro hmem; exact hidxFreshUsed (by simp [forEachZeroUsedNames, hmem]) · intro hmem; exact hcountFreshUsed (by simp [forEachZeroUsedNames, hmem]) -private theorem forEachOne_fresh_facts - {scope : List String} - {varName : String} : - let idxName := forEachOneIdxName scope varName - let countName := forEachOneCountName scope varName - idxName ≠ varName ∧ countName ≠ varName ∧ countName ≠ idxName ∧ - idxName ∉ scope ∧ countName ∉ scope := by - intro idxName countName - have hidxFreshUsed : - idxName ∉ forEachOneUsedNames scope varName := by - simpa [idxName, forEachOneIdxName] using - CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" - (forEachOneUsedNames scope varName) - have hcountFreshUsed : - countName ∉ idxName :: forEachOneUsedNames scope varName := by - simpa [countName, forEachOneCountName, idxName] using - CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" - (idxName :: forEachOneUsedNames scope varName) - constructor - · intro h; exact hidxFreshUsed (by simp [forEachOneUsedNames, h]) - constructor - · intro h; exact hcountFreshUsed (by simp [forEachOneUsedNames, h]) - constructor - · intro h; exact hcountFreshUsed (by simp [h]) - constructor - · intro hmem; exact hidxFreshUsed (by simp [forEachOneUsedNames, hmem]) - · intro hmem; exact hcountFreshUsed (by simp [forEachOneUsedNames, hmem]) - -private theorem forEachTwo_fresh_facts +private theorem forEachLiteral_fresh_facts {scope : List String} - {varName : String} : - let idxName := forEachTwoIdxName scope varName - let countName := forEachTwoCountName scope varName + {varName : String} + {n : Nat} : + let idxName := forEachLiteralIdxName scope varName n + let countName := forEachLiteralCountName scope varName n idxName ≠ varName ∧ countName ≠ varName ∧ countName ≠ idxName ∧ idxName ∉ scope ∧ countName ∉ scope := by intro idxName countName have hidxFreshUsed : - idxName ∉ forEachTwoUsedNames scope varName := by - simpa [idxName, forEachTwoIdxName] using + idxName ∉ forEachLiteralUsedNames scope varName n := by + simpa [idxName, forEachLiteralIdxName] using CompilationModel.pickFreshName_not_mem_usedNames "__forEach_idx" - (forEachTwoUsedNames scope varName) + (forEachLiteralUsedNames scope varName n) have hcountFreshUsed : - countName ∉ idxName :: forEachTwoUsedNames scope varName := by - simpa [countName, forEachTwoCountName, idxName] using + countName ∉ idxName :: forEachLiteralUsedNames scope varName n := by + simpa [countName, forEachLiteralCountName, idxName] using CompilationModel.pickFreshName_not_mem_usedNames "__forEach_count" - (idxName :: forEachTwoUsedNames scope varName) + (idxName :: forEachLiteralUsedNames scope varName n) constructor - · intro h; exact hidxFreshUsed (by simp [forEachTwoUsedNames, h]) + · intro h; exact hidxFreshUsed (by simp [forEachLiteralUsedNames, h]) constructor - · intro h; exact hcountFreshUsed (by simp [forEachTwoUsedNames, h]) + · intro h; exact hcountFreshUsed (by simp [forEachLiteralUsedNames, h]) constructor · intro h; exact hcountFreshUsed (by simp [h]) constructor - · intro hmem; exact hidxFreshUsed (by simp [forEachTwoUsedNames, hmem]) - · intro hmem; exact hcountFreshUsed (by simp [forEachTwoUsedNames, hmem]) + · intro hmem; exact hidxFreshUsed (by simp [forEachLiteralUsedNames, hmem]) + · intro hmem; exact hcountFreshUsed (by simp [forEachLiteralUsedNames, hmem]) private theorem evalIRExpr_forEachZeroCond_after_init {scope : List String} @@ -13021,6 +13084,321 @@ private theorem forEachZero_initFuel_of_slack omega omega +private def forEachEmptyLoopFinal + (idxName varName : String) : Nat → IRState → Nat → IRState + | _, state, 0 => state + | idx, state, Nat.succ remaining => + forEachEmptyLoopFinal idxName varName (idx + 1) + ((state.setVar varName idx).setVar idxName (idx + 1)) + remaining + +private theorem execIRStmts_forEach_empty_body_assign + {idxName varName : String} + {fuel idx : Nat} + {state : IRState} + (hidx : state.getVar idxName = some idx) + (hfuel : 2 ≤ fuel) : + execIRStmts fuel state [YulStmt.assign varName (YulExpr.ident idxName)] = + .continue (state.setVar varName idx) := by + cases fuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + simp [execIRStmts, execIRStmt, evalIRExpr, hidx] + +private theorem execIRStmts_forEach_empty_post_increment + {idxName varName : String} + {fuel idx : Nat} + {state : IRState} + (hidx : state.getVar idxName = some idx) + (hidx_ne_var : idxName ≠ varName) + (hidxNextLt : idx + 1 < Compiler.Constants.evmModulus) + (hfuel : 2 ≤ fuel) : + execIRStmts fuel (state.setVar varName idx) + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] = + .continue ((state.setVar varName idx).setVar idxName (idx + 1)) := by + cases fuel with + | zero => omega + | succ fuel => + cases fuel with + | zero => omega + | succ fuel => + have hidx_read : + (state.setVar varName idx).getVar idxName = some idx := by + rw [FunctionBody.getVar_setVar_ne _ varName idxName idx hidx_ne_var] + exact hidx + have hidxNextMod : + (idx + 1) % Compiler.Constants.evmModulus = idx + 1 := + Nat.mod_eq_of_lt hidxNextLt + simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx_read, Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS, + hidxNextMod] + +private theorem evalIRExpr_forEach_empty_cond_lt + {idxName countName : String} + {idx remaining : Nat} + {state : IRState} + (hidx : state.getVar idxName = some idx) + (hcount : state.getVar countName = some (idx + Nat.succ remaining)) + (hboundLt : idx + Nat.succ remaining < Compiler.Constants.evmModulus) : + evalIRExpr state + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) = + some 1 := by + have hidxLt : idx < Compiler.Constants.evmModulus := by omega + simp [evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx, hcount, Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS, + Nat.mod_eq_of_lt hidxLt, Nat.mod_eq_of_lt hboundLt] + +private theorem evalIRExpr_forEach_empty_cond_eq + {idxName countName : String} + {idx : Nat} + {state : IRState} + (hidx : state.getVar idxName = some idx) + (hcount : state.getVar countName = some idx) : + evalIRExpr state + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) = + some 0 := by + simp [evalIRExpr, evalIRExprs, evalIRCall, + Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, + hidx, hcount] + +private theorem execIRStmt_forEach_empty_loop_from_idx + {idxName countName varName : String} + (remaining idx fuel : Nat) + (state : IRState) + (hidx_ne_var : idxName ≠ varName) + (hcount_ne_var : countName ≠ varName) + (hcount_ne_idx : countName ≠ idxName) + (hidx : state.getVar idxName = some idx) + (hcount : state.getVar countName = some (idx + remaining)) + (hboundLt : idx + remaining < Compiler.Constants.evmModulus) + (hfuel : remaining + 2 ≤ fuel) : + execIRStmt fuel state + (.for_ [] + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident idxName)]) = + .continue (forEachEmptyLoopFinal idxName varName idx state remaining) := by + induction remaining generalizing idx fuel state with + | zero => + cases fuel with + | zero => omega + | succ fuel => + exact execIRStmt_for_init_cond_zero fuel state state [] + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident idxName)] + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + (by simp [execIRStmts]) + (evalIRExpr_forEach_empty_cond_eq hidx (by simpa using hcount)) + | succ remaining ih => + cases fuel with + | zero => omega + | succ fuel => + have hfuelBody : 2 ≤ fuel := by omega + have hbody : + execIRStmts fuel state + [YulStmt.assign varName (YulExpr.ident idxName)] = + .continue (state.setVar varName idx) := + execIRStmts_forEach_empty_body_assign hidx hfuelBody + have hpost : + execIRStmts fuel (state.setVar varName idx) + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] = + .continue ((state.setVar varName idx).setVar idxName (idx + 1)) := + execIRStmts_forEach_empty_post_increment hidx hidx_ne_var (by omega) hfuelBody + have hidxNext : + ((state.setVar varName idx).setVar idxName (idx + 1)).getVar idxName = + some (idx + 1) := + FunctionBody.getVar_setVar_eq _ idxName (idx + 1) + have hcountAfterVar : + (state.setVar varName idx).getVar countName = + some (idx + Nat.succ remaining) := by + rw [FunctionBody.getVar_setVar_ne _ varName countName idx hcount_ne_var] + exact hcount + have hcountNext : + ((state.setVar varName idx).setVar idxName (idx + 1)).getVar countName = + some ((idx + 1) + remaining) := by + rw [FunctionBody.getVar_setVar_ne _ idxName countName (idx + 1) hcount_ne_idx] + simpa [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using hcountAfterVar + have htail : + execIRStmt fuel ((state.setVar varName idx).setVar idxName (idx + 1)) + (.for_ [] + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident idxName)]) = + .continue (forEachEmptyLoopFinal idxName varName (idx + 1) + ((state.setVar varName idx).setVar idxName (idx + 1)) remaining) := + ih (idx := idx + 1) (fuel := fuel) + (state := (state.setVar varName idx).setVar idxName (idx + 1)) + hidxNext hcountNext (by omega) (by omega) + rw [execIRStmt_for_one_continue + (fuel := fuel) (state := state) (sInit := state) + (sBody := state.setVar varName idx) + (sPost := (state.setVar varName idx).setVar idxName (idx + 1)) + (init := []) + (post := [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])]) + (body := [YulStmt.assign varName (YulExpr.ident idxName)]) + (cond := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + (condValue := 1)] + · exact htail + · simp [execIRStmts] + · exact evalIRExpr_forEach_empty_cond_lt hidx hcount hboundLt + · norm_num + · exact hbody + · exact hpost + +private theorem execIRStmt_forEach_empty_loop_idx_bound + {idxName countName varName : String} + (idx bound fuel : Nat) + (state : IRState) + (hidx_ne_var : idxName ≠ varName) + (hcount_ne_var : countName ≠ varName) + (hcount_ne_idx : countName ≠ idxName) + (hidx : state.getVar idxName = some idx) + (hcount : state.getVar countName = some bound) + (hidx_le_bound : idx ≤ bound) + (hboundLt : bound < Compiler.Constants.evmModulus) + (hfuel : bound - idx + 2 ≤ fuel) : + execIRStmt fuel state + (.for_ [] + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident idxName)]) = + .continue (forEachEmptyLoopFinal idxName varName idx state (bound - idx)) := by + exact execIRStmt_forEach_empty_loop_from_idx + (idxName := idxName) (countName := countName) (varName := varName) + (remaining := bound - idx) (idx := idx) (fuel := fuel) (state := state) + hidx_ne_var hcount_ne_var hcount_ne_idx hidx + (by simpa [Nat.add_sub_of_le hidx_le_bound] using hcount) + (by + have hsum : idx + (bound - idx) = bound := Nat.add_sub_of_le hidx_le_bound + simpa [hsum] using hboundLt) + hfuel + +private theorem forEachLiteral_loopFuel_of_slack + {scope : List String} + {varName : String} + {n extraFuel : Nat} + (hslack : sizeOf (forEachLiteralCompiledIR scope varName n) - + (forEachLiteralCompiledIR scope varName n).length ≤ extraFuel) : + forEachLiteralBound n + 2 ≤ extraFuel := by + have hmin : forEachLiteralBound n + 2 ≤ + sizeOf (forEachLiteralCompiledIR scope varName n) - + (forEachLiteralCompiledIR scope varName n).length := by + dsimp [forEachLiteralCompiledIR, forEachLiteralInitStmts, forEachLiteralBound] + simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, + YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, + YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] + have hboundSize : + SourceSemantics.wordNormalize n ≤ sizeOf (SourceSemantics.wordNormalize n) := by + simp + omega + omega + +private theorem forEachLiteral_initFuel_of_slack + {scope : List String} + {varName : String} + {n extraFuel : Nat} + (hslack : sizeOf (forEachLiteralCompiledIR scope varName n) - + (forEachLiteralCompiledIR scope varName n).length ≤ extraFuel) : + 4 ≤ extraFuel := by + have hmin : 4 ≤ + sizeOf (forEachLiteralCompiledIR scope varName n) - + (forEachLiteralCompiledIR scope varName n).length := by + dsimp [forEachLiteralCompiledIR, forEachLiteralInitStmts, forEachLiteralBound] + simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, + YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, + YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] + omega + omega + +private theorem execIRStmts_forEach_literal_empty_compiled + {scope : List String} + {varName : String} + {n : Nat} + {state : IRState} + {extraFuel : Nat} + (hslack : sizeOf (forEachLiteralCompiledIR scope varName n) - + (forEachLiteralCompiledIR scope varName n).length ≤ extraFuel) + (hidx_ne_var : forEachLiteralIdxName scope varName n ≠ varName) + (hcount_ne_var : forEachLiteralCountName scope varName n ≠ varName) + (hcount_ne_idx : + forEachLiteralCountName scope varName n ≠ forEachLiteralIdxName scope varName n) : + execIRStmts ((forEachLiteralCompiledIR scope varName n).length + extraFuel + 1) state + (forEachLiteralCompiledIR scope varName n) = + .continue + (forEachEmptyLoopFinal (forEachLiteralIdxName scope varName n) varName 0 + (((state.setVar (forEachLiteralIdxName scope varName n) 0).setVar + (forEachLiteralCountName scope varName n) (forEachLiteralBound n)).setVar + varName 0) + (forEachLiteralBound n)) := by + let idxName := forEachLiteralIdxName scope varName n + let countName := forEachLiteralCountName scope varName n + let bound := forEachLiteralBound n + let stateIdx := state.setVar idxName 0 + let stateCount := stateIdx.setVar countName bound + let stateLoop := stateCount.setVar varName 0 + have hfuelInit : 4 ≤ extraFuel := forEachLiteral_initFuel_of_slack hslack + have hfuelLoop : bound + 2 ≤ extraFuel := by + simpa [bound] using forEachLiteral_loopFuel_of_slack + (scope := scope) (varName := varName) (n := n) hslack + have hinit : execIRStmts extraFuel state + (forEachLiteralInitStmts scope varName n) = .continue stateLoop := by + simpa [forEachLiteralInitStmts, stateIdx, stateCount, stateLoop, idxName, countName, + bound] using + execIRStmts_forEach_init_literal + (fuel := extraFuel) (state := state) (idxName := idxName) + (countName := countName) (varName := varName) (bound := bound) + (hfuel := hfuelInit) + have hidx_after_init : stateLoop.getVar idxName = some 0 := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 hidx_ne_var] + rw [FunctionBody.getVar_setVar_ne _ countName idxName bound hcount_ne_idx.symm] + simp + have hcount_after_init : stateLoop.getVar countName = some bound := by + dsimp [stateLoop, stateCount, stateIdx] + rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] + simp + have hboundLt : bound < Compiler.Constants.evmModulus := by + dsimp [bound, forEachLiteralBound] + exact FunctionBody.wordNormalize_lt_evmModulus n + have hloop : + execIRStmt (Nat.succ extraFuel) stateLoop + (.for_ [] + (YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])] + [YulStmt.assign varName (YulExpr.ident idxName)]) = + .continue (forEachEmptyLoopFinal idxName varName 0 stateLoop bound) := by + simpa [Nat.zero_add] using + execIRStmt_forEach_empty_loop_idx_bound + (idxName := idxName) (countName := countName) (varName := varName) + (idx := 0) (bound := bound) (fuel := Nat.succ extraFuel) (state := stateLoop) + hidx_ne_var hcount_ne_var hcount_ne_idx hidx_after_init + hcount_after_init (by omega) hboundLt (by omega) + dsimp [forEachLiteralCompiledIR] + have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega + rw [← hfuelEq] + exact execIRStmts_single_for_init_continue + (fuel := extraFuel) (state := state) (sInit := stateLoop) + (sFinal := forEachEmptyLoopFinal idxName varName 0 stateLoop bound) + (init := forEachLiteralInitStmts scope varName n) + (post := [YulStmt.assign idxName + (YulExpr.call "add" [YulExpr.ident idxName, YulExpr.lit 1])]) + (body := [YulStmt.assign varName (YulExpr.ident idxName)]) + (cond := YulExpr.call "lt" [YulExpr.ident idxName, YulExpr.ident countName]) + hinit hloop + private theorem execIRStmts_forEach_literal_zero_compiled {scope : List String} {varName : String} @@ -13065,353 +13443,6 @@ private theorem execIRStmts_forEach_literal_zero_compiled (body := forEachZeroBodyWithBind scope varName body bodyIR) (cond := forEachZeroCondExpr scope varName body) hinit hcond -private theorem execIRStmts_forEach_literal_one_empty_compiled - {scope : List String} - {varName : String} - {state : IRState} - {extraFuel : Nat} - (hslack : sizeOf (forEachOneCompiledIR scope varName) - - (forEachOneCompiledIR scope varName).length ≤ extraFuel) - (hidx_ne_var : forEachOneIdxName scope varName ≠ varName) - (hcount_ne_var : forEachOneCountName scope varName ≠ varName) - (hcount_ne_idx : - forEachOneCountName scope varName ≠ forEachOneIdxName scope varName) : - execIRStmts ((forEachOneCompiledIR scope varName).length + extraFuel + 1) state - (forEachOneCompiledIR scope varName) = - .continue ((((state.setVar (forEachOneIdxName scope varName) 0).setVar - (forEachOneCountName scope varName) 1).setVar varName 0).setVar - (forEachOneIdxName scope varName) 1) := by - let idxName := forEachOneIdxName scope varName - let countName := forEachOneCountName scope varName - let stateIdx := state.setVar idxName 0 - let stateCount := stateIdx.setVar countName 1 - let stateLoop := stateCount.setVar varName 0 - let statePost := stateLoop.setVar idxName 1 - have hfuelInit : 4 ≤ extraFuel := by - have hmin : 4 ≤ - sizeOf (forEachOneCompiledIR scope varName) - - (forEachOneCompiledIR scope varName).length := by - dsimp [forEachOneCompiledIR, forEachOneInitStmts, forEachZeroCondExpr, - forEachZeroPostStmts, forEachZeroBodyWithBind] - simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, - YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, - YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] - omega - omega - have hinit : execIRStmts extraFuel state - (forEachOneInitStmts scope varName) = .continue stateLoop := by - simpa [forEachOneInitStmts, stateIdx, stateCount, stateLoop, idxName, countName] using - execIRStmts_forEach_init_literal - (fuel := extraFuel) (state := state) (idxName := idxName) - (countName := countName) (varName := varName) (bound := 1) (hfuel := hfuelInit) - have hidx_after_init : stateLoop.getVar idxName = some 0 := by - dsimp [stateLoop, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 hidx_ne_var] - rw [FunctionBody.getVar_setVar_ne _ countName idxName 1 hcount_ne_idx.symm] - simp - have hcount_after_init : stateLoop.getVar countName = some 1 := by - dsimp [stateLoop, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] - simp - have hcondOne : - evalIRExpr stateLoop (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) = some 1 := by - simp [forEachZeroCondExpr, idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_after_init, hcount_after_init] - have hbody : execIRStmts extraFuel stateLoop - ([YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]) = .continue stateLoop := by - have hidx_read : stateLoop.getVar idxName = some 0 := hidx_after_init - cases extraFuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - stateLoop.getVar (forEachOneIdxName scope varName) = some 0 := by - simpa [idxName] using hidx_read - have hloop_idem : stateLoop.setVar varName 0 = stateLoop := by - simpa [stateLoop] using irState_setVar_idem stateCount varName 0 - simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read', hloop_idem] - have hpost : execIRStmts extraFuel stateLoop - ([YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) = .continue statePost := by - have hidx_read : stateLoop.getVar idxName = some 0 := hidx_after_init - cases extraFuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - stateLoop.getVar (forEachOneIdxName scope varName) = some 0 := by - simpa [idxName] using hidx_read - dsimp [idxName, statePost] - simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_read'] - have hidx_after_post : statePost.getVar idxName = some 1 := by - exact FunctionBody.getVar_setVar_eq stateLoop idxName 1 - have hcount_after_post : statePost.getVar countName = some 1 := by - dsimp [statePost, stateLoop, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ idxName countName 1 hcount_ne_idx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] - simp - have hcondZero : - evalIRExpr statePost (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) = some 0 := by - simp [forEachZeroCondExpr, idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_after_post, hcount_after_post] - have hloop : - execIRStmt extraFuel statePost - (.for_ [] (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) - ([YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) - ([YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))])) = - .continue statePost := by - cases extraFuel with - | zero => omega - | succ fuel => - exact execIRStmt_for_init_cond_zero fuel statePost statePost [] - ([YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) - ([YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]) - (YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) (by simp [execIRStmts]) hcondZero - dsimp [forEachOneCompiledIR] - have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega - rw [← hfuelEq] - rw [execIRStmts_single_for_one_continue - (fuel := extraFuel) (state := state) (sInit := stateLoop) (sBody := stateLoop) - (sPost := statePost) (init := forEachOneInitStmts scope varName) - (post := [YulStmt.assign (forEachOneIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.lit 1])]) - (body := [YulStmt.assign varName (YulExpr.ident (forEachOneIdxName scope varName))]) - (cond := YulExpr.call "lt" [YulExpr.ident (forEachOneIdxName scope varName), YulExpr.ident (forEachOneCountName scope varName)]) (condValue := 1) - hinit hcondOne (by norm_num) hbody hpost] - simpa [hloop, statePost, idxName, countName, stateLoop, stateCount, stateIdx] - -private theorem execIRStmts_forEach_literal_two_empty_compiled - {scope : List String} - {varName : String} - {state : IRState} - {extraFuel : Nat} - (hslack : sizeOf (forEachTwoCompiledIR scope varName) - - (forEachTwoCompiledIR scope varName).length ≤ extraFuel) - (hidx_ne_var : forEachTwoIdxName scope varName ≠ varName) - (hcount_ne_var : forEachTwoCountName scope varName ≠ varName) - (hcount_ne_idx : - forEachTwoCountName scope varName ≠ forEachTwoIdxName scope varName) : - execIRStmts ((forEachTwoCompiledIR scope varName).length + extraFuel + 1) state - (forEachTwoCompiledIR scope varName) = - .continue ((((((state.setVar (forEachTwoIdxName scope varName) 0).setVar - (forEachTwoCountName scope varName) 2).setVar varName 0).setVar - (forEachTwoIdxName scope varName) 1).setVar varName 1).setVar - (forEachTwoIdxName scope varName) 2) := by - let idxName := forEachTwoIdxName scope varName - let countName := forEachTwoCountName scope varName - let stateIdx := state.setVar idxName 0 - let stateCount := stateIdx.setVar countName 2 - let stateLoop0 := stateCount.setVar varName 0 - let statePost0 := stateLoop0.setVar idxName 1 - let stateBody1 := statePost0.setVar varName 1 - let statePost1 := stateBody1.setVar idxName 2 - have hfuelInit : 4 ≤ extraFuel := by - have hmin : 4 ≤ - sizeOf (forEachTwoCompiledIR scope varName) - - (forEachTwoCompiledIR scope varName).length := by - dsimp [forEachTwoCompiledIR, forEachTwoInitStmts] - simp only [List.cons.sizeOf_spec, List.nil.sizeOf_spec, YulStmt.for_.sizeOf_spec, - YulStmt.let_.sizeOf_spec, YulStmt.assign.sizeOf_spec, YulExpr.call.sizeOf_spec, - YulExpr.ident.sizeOf_spec, YulExpr.lit.sizeOf_spec] - omega - omega - have hinit : execIRStmts extraFuel state - (forEachTwoInitStmts scope varName) = .continue stateLoop0 := by - simpa [forEachTwoInitStmts, stateIdx, stateCount, stateLoop0, idxName, countName] using - execIRStmts_forEach_init_literal - (fuel := extraFuel) (state := state) (idxName := idxName) - (countName := countName) (varName := varName) (bound := 2) (hfuel := hfuelInit) - have hidx_after_init : stateLoop0.getVar idxName = some 0 := by - dsimp [stateLoop0, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ varName idxName 0 hidx_ne_var] - rw [FunctionBody.getVar_setVar_ne _ countName idxName 2 hcount_ne_idx.symm] - simp - have hcount_after_init : stateLoop0.getVar countName = some 2 := by - dsimp [stateLoop0, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] - simp - have hcond0 : - evalIRExpr stateLoop0 (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) = some 1 := by - simp [idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_after_init, hcount_after_init] - have hbody0 : execIRStmts extraFuel stateLoop0 - ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) = .continue stateLoop0 := by - cases extraFuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - stateLoop0.getVar (forEachTwoIdxName scope varName) = some 0 := by - simpa [idxName] using hidx_after_init - have hloop_idem : stateLoop0.setVar varName 0 = stateLoop0 := by - simpa [stateLoop0] using irState_setVar_idem stateCount varName 0 - simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read', hloop_idem] - have hpost0 : execIRStmts extraFuel stateLoop0 - ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) = .continue statePost0 := by - cases extraFuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - stateLoop0.getVar (forEachTwoIdxName scope varName) = some 0 := by - simpa [idxName] using hidx_after_init - dsimp [idxName, statePost0] - simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_read'] - have hidx_after_post0 : statePost0.getVar idxName = some 1 := by - exact FunctionBody.getVar_setVar_eq stateLoop0 idxName 1 - have hcount_after_post0 : statePost0.getVar countName = some 2 := by - dsimp [statePost0, stateLoop0, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ idxName countName 1 hcount_ne_idx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] - simp - have hcond1 : - evalIRExpr statePost0 (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) = some 1 := by - simp [idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_after_post0, hcount_after_post0, Compiler.Constants.evmModulus, - Verity.Core.UINT256_MODULUS] - have hbody1 : execIRStmts extraFuel statePost0 - ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) = .continue stateBody1 := by - cases extraFuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - statePost0.getVar (forEachTwoIdxName scope varName) = some 1 := by - simpa [idxName] using hidx_after_post0 - dsimp [stateBody1] - simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read'] - have hidx_after_body1 : stateBody1.getVar idxName = some 1 := by - dsimp [stateBody1, statePost0] - rw [FunctionBody.getVar_setVar_ne _ varName idxName 1 hidx_ne_var] - simp - have hcount_after_body1 : stateBody1.getVar countName = some 2 := by - dsimp [stateBody1] - rw [FunctionBody.getVar_setVar_ne _ varName countName 1 hcount_ne_var] - exact hcount_after_post0 - have hpost1 : execIRStmts extraFuel stateBody1 - ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) = .continue statePost1 := by - cases extraFuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - stateBody1.getVar (forEachTwoIdxName scope varName) = some 1 := by - simpa [idxName] using hidx_after_body1 - dsimp [idxName, statePost1] - simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_read', Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS] - have hidx_after_post1 : statePost1.getVar idxName = some 2 := by - exact FunctionBody.getVar_setVar_eq stateBody1 idxName 2 - have hcount_after_post1 : statePost1.getVar countName = some 2 := by - dsimp [statePost1, stateBody1, statePost0, stateLoop0, stateCount, stateIdx] - rw [FunctionBody.getVar_setVar_ne _ idxName countName 2 hcount_ne_idx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 1 hcount_ne_var] - rw [FunctionBody.getVar_setVar_ne _ idxName countName 1 hcount_ne_idx] - rw [FunctionBody.getVar_setVar_ne _ varName countName 0 hcount_ne_var] - simp - have hcond2 : - evalIRExpr statePost1 (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) = some 0 := by - simp [idxName, countName, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_after_post1, hcount_after_post1] - have hloop1 : - execIRStmt extraFuel statePost0 - (.for_ [] (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) - ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) - ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))])) = - .continue statePost1 := by - cases extraFuel with - | zero => omega - | succ fuel => - have hbody1Fuel : execIRStmts fuel statePost0 - ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) = - .continue stateBody1 := by - cases fuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - statePost0.getVar (forEachTwoIdxName scope varName) = some 1 := by - simpa [idxName] using hidx_after_post0 - dsimp [stateBody1] - simp [execIRStmts, execIRStmt, evalIRExpr, hidx_read'] - have hpost1Fuel : execIRStmts fuel stateBody1 - ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) = - .continue statePost1 := by - cases fuel with - | zero => omega - | succ fuel => - cases fuel with - | zero => omega - | succ fuel => - have hidx_read' : - stateBody1.getVar (forEachTwoIdxName scope varName) = some 1 := by - simpa [idxName] using hidx_after_body1 - dsimp [idxName, statePost1] - simp [execIRStmts, execIRStmt, evalIRExpr, evalIRExprs, evalIRCall, - Compiler.Proofs.YulGeneration.Backends.evalBuiltinCallWithEvmYulLeanContext, - hidx_read', Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS] - have htail : - execIRStmt fuel statePost1 - (.for_ [] (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) - ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) - ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))])) = - .continue statePost1 := by - cases fuel with - | zero => omega - | succ fuel => - exact execIRStmt_for_init_cond_zero fuel statePost1 statePost1 [] - ([YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) - ([YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) - (YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) - (by simp [execIRStmts]) hcond2 - rw [execIRStmt_for_one_continue - (fuel := fuel) (state := statePost0) (sInit := statePost0) - (sBody := stateBody1) (sPost := statePost1) - (init := []) (post := [YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) - (body := [YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) - (cond := YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) - (condValue := 1)] - · exact htail - · simp [execIRStmts] - · exact hcond1 - · norm_num - · exact hbody1Fuel - · exact hpost1Fuel - dsimp [forEachTwoCompiledIR] - have hfuelEq : extraFuel + 1 + 1 = 1 + extraFuel + 1 := by omega - rw [← hfuelEq] - rw [execIRStmts_single_for_one_continue - (fuel := extraFuel) (state := state) (sInit := stateLoop0) (sBody := stateLoop0) - (sPost := statePost0) (init := forEachTwoInitStmts scope varName) - (post := [YulStmt.assign (forEachTwoIdxName scope varName) (YulExpr.call "add" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.lit 1])]) - (body := [YulStmt.assign varName (YulExpr.ident (forEachTwoIdxName scope varName))]) - (cond := YulExpr.call "lt" [YulExpr.ident (forEachTwoIdxName scope varName), YulExpr.ident (forEachTwoCountName scope varName)]) (condValue := 1) - hinit hcond0 (by norm_num) hbody0 hpost0] - simpa [hloop1, statePost1, stateBody1, statePost0, idxName, countName, stateLoop0, - stateCount, stateIdx] - private theorem forEachZero_nextScopeIncluded {scope : List String} {varName : String} @@ -13515,31 +13546,109 @@ private theorem stmtStepMatches_forEach_literal_zero_final hboundedLoop, FunctionBody.scopeNamesPresent_of_included hscopeBase hNextScopeIncl⟩ -private theorem stmtStepMatches_forEach_literal_one_empty_final +private theorem forEach_empty_final_rel + {fields : List Field} + {scope : List String} + {idxName varName : String} + (idx remaining : Nat) + {runtime : SourceSemantics.RuntimeState} + {state : IRState} + (hidx_not_next : idxName ∉ varName :: scope) + (hboundLt : idx + remaining < Compiler.Constants.evmModulus) + (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) + (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtime.bindings state) + (hbounded : FunctionBody.bindingsBounded runtime.bindings) + (hscope : FunctionBody.scopeNamesPresent (varName :: scope) runtime.bindings) : + FunctionBody.runtimeStateMatchesIR fields + (SourceSemantics.execForEachEmptyLoopFinal varName runtime idx remaining) + (forEachEmptyLoopFinal idxName varName idx state remaining) ∧ + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + (SourceSemantics.execForEachEmptyLoopFinal varName runtime idx remaining).bindings + (forEachEmptyLoopFinal idxName varName idx state remaining) ∧ + FunctionBody.bindingsBounded + (SourceSemantics.execForEachEmptyLoopFinal varName runtime idx remaining).bindings ∧ + FunctionBody.scopeNamesPresent (varName :: scope) + (SourceSemantics.execForEachEmptyLoopFinal varName runtime idx remaining).bindings := by + induction remaining generalizing idx runtime state with + | zero => + simpa [SourceSemantics.execForEachEmptyLoopFinal, forEachEmptyLoopFinal] using + And.intro hruntime (And.intro hexact (And.intro hbounded hscope)) + | succ remaining ih => + have hidxLt : idx < Compiler.Constants.evmModulus := by omega + have hnorm : SourceSemantics.wordNormalize idx = idx := by + simp [SourceSemantics.wordNormalize, Compiler.Constants.evmModulus, + Verity.Core.UINT256_MODULUS, Nat.mod_eq_of_lt hidxLt] + have hmod : idx % Compiler.Constants.evmModulus = idx := + Nat.mod_eq_of_lt hidxLt + let runtimeStep : SourceSemantics.RuntimeState := + { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName idx } + let stateStep : IRState := (state.setVar varName idx).setVar idxName (idx + 1) + have hruntimeStep : + FunctionBody.runtimeStateMatchesIR fields runtimeStep stateStep := by + dsimp [runtimeStep, stateStep] + exact FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (FunctionBody.runtimeStateMatchesIR_setVar_bindValue hruntime varName idx) + have hexactBind : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeStep.bindings (state.setVar varName idx) := by + dsimp [runtimeStep] + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue + (scope := scope) (boundName := varName) (value := idx) + (FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexact + (by intro name hmem; simp [hmem])) + have hexactStep : + FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) + runtimeStep.bindings stateStep := by + dsimp [stateStep] + exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant + (state := state.setVar varName idx) (tempName := idxName) (value := idx + 1) + hexactBind hidx_not_next + have hboundedStep : + FunctionBody.bindingsBounded runtimeStep.bindings := by + dsimp [runtimeStep] + exact FunctionBody.bindingsBounded_bindValue hbounded varName idx hidxLt + have hscopeStep : + FunctionBody.scopeNamesPresent (varName :: scope) runtimeStep.bindings := by + dsimp [runtimeStep] + exact FunctionBody.scopeNamesPresent_cons_bindValue + (FunctionBody.scopeNamesPresent_of_included hscope + (by intro name hmem; simp [hmem])) + have htail := ih (idx := idx + 1) (runtime := runtimeStep) (state := stateStep) + (by omega) hruntimeStep hexactStep hboundedStep hscopeStep + simpa [SourceSemantics.execForEachEmptyLoopFinal, forEachEmptyLoopFinal, + runtimeStep, stateStep, hnorm, hmod] using htail + +private theorem stmtStepMatches_forEach_literal_empty_final {fields : List Field} {scope : List String} {varName : String} + {n : Nat} {runtime : SourceSemantics.RuntimeState} {state : IRState} - (hidx_ne_var : forEachOneIdxName scope varName ≠ varName) - (hidx_not_scope : forEachOneIdxName scope varName ∉ scope) - (hcount_not_scope : forEachOneCountName scope varName ∉ scope) + (hidx_ne_var : forEachLiteralIdxName scope varName n ≠ varName) + (hidx_not_scope : forEachLiteralIdxName scope varName n ∉ scope) + (hcount_not_scope : forEachLiteralCountName scope varName n ∉ scope) (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings state) (hscope : FunctionBody.scopeNamesPresent scope runtime.bindings) (hbounded : FunctionBody.bindingsBounded runtime.bindings) (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : stmtStepMatchesIRExec fields - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 1) [])) - (.continue (forEachZeroRuntimeLoop runtime varName)) - (.continue ((((state.setVar (forEachOneIdxName scope varName) 0).setVar - (forEachOneCountName scope varName) 1).setVar varName 0).setVar - (forEachOneIdxName scope varName) 1)) := by - let idxName := forEachOneIdxName scope varName - let countName := forEachOneCountName scope varName + (stmtNextScope scope (Stmt.forEach varName (Expr.literal n) [])) + (.continue + (SourceSemantics.execForEachEmptyLoopFinal varName + (forEachZeroRuntimeLoop runtime varName) 0 (forEachLiteralBound n))) + (.continue + (forEachEmptyLoopFinal (forEachLiteralIdxName scope varName n) varName 0 + (((state.setVar (forEachLiteralIdxName scope varName n) 0).setVar + (forEachLiteralCountName scope varName n) (forEachLiteralBound n)).setVar + varName 0) + (forEachLiteralBound n))) := by + let idxName := forEachLiteralIdxName scope varName n + let countName := forEachLiteralCountName scope varName n let runtimeLoop := forEachZeroRuntimeLoop runtime varName - let stateCount := (state.setVar idxName 0).setVar countName 1 + let stateCount := (state.setVar idxName 0).setVar countName (forEachLiteralBound n) let stateLoop := stateCount.setVar varName 0 - let statePost := stateLoop.setVar idxName 1 have hidx_not_next : idxName ∉ varName :: scope := by intro hmem simp at hmem @@ -13548,6 +13657,7 @@ private theorem stmtStepMatches_forEach_literal_one_empty_final · exact hidx_not_scope hscopeMem have hexactCount : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateCount := by + dsimp [stateCount] exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant (FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope) hcount_not_scope @@ -13557,24 +13667,13 @@ private theorem stmtStepMatches_forEach_literal_one_empty_final simpa [runtimeLoop, forEachZeroRuntimeLoop, stateLoop] using FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue (boundName := varName) (value := 0) hexactCount - have hexactPost : - FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) - runtimeLoop.bindings statePost := by - exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant - (state := stateLoop) (tempName := idxName) (value := 1) - hexactLoop hidx_not_next - have hruntimePost : - FunctionBody.runtimeStateMatchesIR fields runtimeLoop statePost := by - simpa [runtimeLoop, forEachZeroRuntimeLoop, stateLoop, stateCount, statePost] using - FunctionBody.runtimeStateMatchesIR_setVar_irrelevant - (state := stateCount.setVar varName 0) (name := idxName) (value := 1) - (FunctionBody.runtimeStateMatchesIR_setVar_bindValue - (fields := fields) - (runtime := runtime) - (state := stateCount) - (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant - (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime)) - varName 0) + have hruntimeLoop : + FunctionBody.runtimeStateMatchesIR fields runtimeLoop stateLoop := by + simpa [runtimeLoop, forEachZeroRuntimeLoop, stateLoop, stateCount] using + FunctionBody.runtimeStateMatchesIR_setVar_bindValue + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant + (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime)) + varName 0 have hscopeLoop : FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by simpa [runtimeLoop, forEachZeroRuntimeLoop] using FunctionBody.scopeNamesPresent_cons_bindValue @@ -13582,9 +13681,17 @@ private theorem stmtStepMatches_forEach_literal_one_empty_final have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := FunctionBody.bindingsBounded_bindValue hbounded varName 0 (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) + have hboundLt : 0 + forEachLiteralBound n < Compiler.Constants.evmModulus := by + simpa [forEachLiteralBound] using FunctionBody.wordNormalize_lt_evmModulus n + rcases forEach_empty_final_rel + (fields := fields) (scope := scope) (idxName := idxName) (varName := varName) + (idx := 0) (remaining := forEachLiteralBound n) + (runtime := runtimeLoop) (state := stateLoop) + hidx_not_next hboundLt hruntimeLoop hexactLoop hboundedLoop hscopeLoop with + ⟨hruntimeFinal, hexactFinal, hboundedFinal, hscopeFinal⟩ have hNextScopeIncl : FunctionBody.scopeNamesIncluded - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 1) [])) + (stmtNextScope scope (Stmt.forEach varName (Expr.literal n) [])) (varName :: scope) := by intro name hmem simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem @@ -13594,128 +13701,13 @@ private theorem stmtStepMatches_forEach_literal_one_empty_final · exact False.elim (by simpa [collectStmtListNames] using hbody) · simp [hscopeMem] simp [stmtStepMatchesIRExec] - exact ⟨hruntimePost, - FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactPost hNextScopeIncl, - hboundedLoop, - FunctionBody.scopeNamesPresent_of_included hscopeLoop hNextScopeIncl⟩ - -private theorem stmtStepMatches_forEach_literal_two_empty_final - {fields : List Field} - {scope : List String} - {varName : String} - {runtime : SourceSemantics.RuntimeState} - {state : IRState} - (hidx_ne_var : forEachTwoIdxName scope varName ≠ varName) - (hidx_not_scope : forEachTwoIdxName scope varName ∉ scope) - (hcount_not_scope : forEachTwoCountName scope varName ∉ scope) - (hexact : FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings state) - (hscope : FunctionBody.scopeNamesPresent scope runtime.bindings) - (hbounded : FunctionBody.bindingsBounded runtime.bindings) - (hruntime : FunctionBody.runtimeStateMatchesIR fields runtime state) : - stmtStepMatchesIRExec fields - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 2) [])) - (.continue (forEachLiteralRuntimeLoop runtime varName 1)) - (.continue ((((((state.setVar (forEachTwoIdxName scope varName) 0).setVar - (forEachTwoCountName scope varName) 2).setVar varName 0).setVar - (forEachTwoIdxName scope varName) 1).setVar varName 1).setVar - (forEachTwoIdxName scope varName) 2)) := by - let idxName := forEachTwoIdxName scope varName - let countName := forEachTwoCountName scope varName - let runtimeLoop := forEachLiteralRuntimeLoop runtime varName 1 - let stateCount := (state.setVar idxName 0).setVar countName 2 - let stateLoop0 := stateCount.setVar varName 0 - let statePost0 := stateLoop0.setVar idxName 1 - let stateBody1 := statePost0.setVar varName 1 - let statePost1 := stateBody1.setVar idxName 2 - have hidx_not_next : idxName ∉ varName :: scope := by - intro hmem - simp at hmem - rcases hmem with hvar | hscopeMem - · exact hidx_ne_var hvar - · exact hidx_not_scope hscopeMem - have hexactCount : - FunctionBody.bindingsExactlyMatchIRVarsOnScope scope runtime.bindings stateCount := by - exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant - (FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant hexact hidx_not_scope) - hcount_not_scope - have hexactLoop0 : - FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) - (forEachZeroRuntimeLoop runtime varName).bindings stateLoop0 := by - simpa [forEachZeroRuntimeLoop, stateLoop0] using - FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue - (boundName := varName) (value := 0) hexactCount - have hexactPost0 : - FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) - (forEachZeroRuntimeLoop runtime varName).bindings statePost0 := by - exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant - (state := stateLoop0) (tempName := idxName) (value := 1) - hexactLoop0 hidx_not_next - have hexactPost0Scope : - FunctionBody.bindingsExactlyMatchIRVarsOnScope scope - (forEachZeroRuntimeLoop runtime varName).bindings statePost0 := by - exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactPost0 - (by intro name hmem; simp [hmem]) - have hexactBody1 : - FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) - runtimeLoop.bindings stateBody1 := by - simpa [runtimeLoop, forEachLiteralRuntimeLoop, forEachZeroRuntimeLoop, stateBody1, - source_bindValue_overwrite] using - FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_bindValue - (boundName := varName) (value := 1) - (scope := scope) - (bindings := (forEachZeroRuntimeLoop runtime varName).bindings) - (state := statePost0) - hexactPost0Scope - have hexactPost1 : - FunctionBody.bindingsExactlyMatchIRVarsOnScope (varName :: scope) - runtimeLoop.bindings statePost1 := by - exact FunctionBody.bindingsExactlyMatchIRVarsOnScope_setVar_irrelevant - (state := stateBody1) (tempName := idxName) (value := 2) - hexactBody1 hidx_not_next - have hruntimePost1 : - FunctionBody.runtimeStateMatchesIR fields runtimeLoop statePost1 := by - simpa [runtimeLoop, forEachLiteralRuntimeLoop, stateLoop0, stateCount, statePost0, - stateBody1, statePost1] using - FunctionBody.runtimeStateMatchesIR_setVar_irrelevant - (state := ((stateCount.setVar varName 0).setVar idxName 1).setVar varName 1) - (name := idxName) (value := 2) - (FunctionBody.runtimeStateMatchesIR_setVar_bindValue - (fields := fields) - (runtime := runtime) - (state := (stateCount.setVar varName 0).setVar idxName 1) - (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant - (state := stateCount.setVar varName 0) (name := idxName) (value := 1) - (FunctionBody.runtimeStateMatchesIR_setVar_bindValue - (fields := fields) - (runtime := runtime) - (state := stateCount) - (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant - (FunctionBody.runtimeStateMatchesIR_setVar_irrelevant hruntime)) - varName 0)) - varName 1) - have hscopeLoop : FunctionBody.scopeNamesPresent (varName :: scope) runtimeLoop.bindings := by - simpa [runtimeLoop, forEachLiteralRuntimeLoop] using - FunctionBody.scopeNamesPresent_cons_bindValue - (boundName := varName) (value := 1) hscope - have hboundedLoop : FunctionBody.bindingsBounded runtimeLoop.bindings := - FunctionBody.bindingsBounded_bindValue hbounded varName 1 - (by norm_num [Compiler.Constants.evmModulus, Verity.Core.UINT256_MODULUS]) - have hNextScopeIncl : - FunctionBody.scopeNamesIncluded - (stmtNextScope scope (Stmt.forEach varName (Expr.literal 2) [])) - (varName :: scope) := by - intro name hmem - simp [stmtNextScope, collectStmtNames, collectExprNames] at hmem - rcases hmem with hvar | hscopeMem - · simp [hvar] - · rcases hscopeMem with hbody | hscopeMem - · exact False.elim (by simpa [collectStmtListNames] using hbody) - · simp [hscopeMem] - simp [stmtStepMatchesIRExec] - exact ⟨hruntimePost1, - FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included hexactPost1 hNextScopeIncl, - hboundedLoop, - FunctionBody.scopeNamesPresent_of_included hscopeLoop hNextScopeIncl⟩ + exact ⟨by simpa [idxName, countName, runtimeLoop, stateLoop, stateCount] using hruntimeFinal, + FunctionBody.bindingsExactlyMatchIRVarsOnScope_of_included + (by simpa [idxName, countName, runtimeLoop, stateLoop, stateCount] using hexactFinal) + hNextScopeIncl, + by simpa [runtimeLoop] using hboundedFinal, + FunctionBody.scopeNamesPresent_of_included + (by simpa [runtimeLoop] using hscopeFinal) hNextScopeIncl⟩ private theorem compiledStmtStep_forEach_literal_zero {fields : List Field} @@ -13757,71 +13749,42 @@ private theorem compiledStmtStep_forEach_literal_zero (runtime := runtime) (state := state) hbodyNames hidx_not_scope hcount_not_scope hexact hscope hbounded hruntime -private theorem compiledStmtStep_forEach_literal_one_empty +private theorem compiledStmtStep_forEach_literal_empty {fields : List Field} {scope : List String} - {varName : String} : - ∃ compiledIR, - CompiledStmtStep fields scope (Stmt.forEach varName (Expr.literal 1) []) compiledIR := by - refine ⟨forEachOneCompiledIR scope varName, ?_⟩ - refine - { compileOk := ?_ - preserves := ?_ } - · dsimp [forEachOneCompiledIR, forEachOneInitStmts, forEachOneIdxName, - forEachOneCountName, forEachOneUsedNames] - simp [CompilationModel.compileStmt, CompilationModel.compileStmtList, - CompilationModel.compileExpr, - CompilationModel.uint256Modulus] - rfl - · intro runtime state extraFuel hexact hscope hbounded hruntime hslack - rcases forEachOne_fresh_facts (scope := scope) (varName := varName) with - ⟨hidx_ne_var, hcount_ne_var, hcount_ne_idx, hidx_not_scope, hcount_not_scope⟩ - refine ⟨.continue (forEachZeroRuntimeLoop runtime varName), - .continue ((((state.setVar (forEachOneIdxName scope varName) 0).setVar - (forEachOneCountName scope varName) 1).setVar varName 0).setVar - (forEachOneIdxName scope varName) 1), - sourceExec_forEach_literal_one_empty, ?_, ?_⟩ - · exact execIRStmts_forEach_literal_one_empty_compiled - (scope := scope) (varName := varName) - (state := state) (extraFuel := extraFuel) hslack - hidx_ne_var hcount_ne_var hcount_ne_idx - · exact stmtStepMatches_forEach_literal_one_empty_final - (fields := fields) (scope := scope) (varName := varName) - (runtime := runtime) (state := state) - hidx_ne_var hidx_not_scope hcount_not_scope - hexact hscope hbounded hruntime - -private theorem compiledStmtStep_forEach_literal_two_empty - {fields : List Field} - {scope : List String} - {varName : String} : + {varName : String} + {n : Nat} : ∃ compiledIR, - CompiledStmtStep fields scope (Stmt.forEach varName (Expr.literal 2) []) compiledIR := by - refine ⟨forEachTwoCompiledIR scope varName, ?_⟩ + CompiledStmtStep fields scope (Stmt.forEach varName (Expr.literal n) []) compiledIR := by + refine ⟨forEachLiteralCompiledIR scope varName n, ?_⟩ refine { compileOk := ?_ preserves := ?_ } - · dsimp [forEachTwoCompiledIR, forEachTwoInitStmts, forEachTwoIdxName, - forEachTwoCountName, forEachTwoUsedNames] + · dsimp [forEachLiteralCompiledIR, forEachLiteralInitStmts, forEachLiteralIdxName, + forEachLiteralCountName, forEachLiteralUsedNames, forEachLiteralBound] simp [CompilationModel.compileStmt, CompilationModel.compileStmtList, CompilationModel.compileExpr, CompilationModel.uint256Modulus] rfl · intro runtime state extraFuel hexact hscope hbounded hruntime hslack - rcases forEachTwo_fresh_facts (scope := scope) (varName := varName) with + rcases forEachLiteral_fresh_facts (scope := scope) (varName := varName) (n := n) with ⟨hidx_ne_var, hcount_ne_var, hcount_ne_idx, hidx_not_scope, hcount_not_scope⟩ - refine ⟨.continue (forEachLiteralRuntimeLoop runtime varName 1), - .continue ((((((state.setVar (forEachTwoIdxName scope varName) 0).setVar - (forEachTwoCountName scope varName) 2).setVar varName 0).setVar - (forEachTwoIdxName scope varName) 1).setVar varName 1).setVar - (forEachTwoIdxName scope varName) 2), - sourceExec_forEach_literal_two_empty, ?_, ?_⟩ - · exact execIRStmts_forEach_literal_two_empty_compiled - (scope := scope) (varName := varName) + refine ⟨.continue + (SourceSemantics.execForEachEmptyLoopFinal varName + (forEachZeroRuntimeLoop runtime varName) 0 (forEachLiteralBound n)), + .continue + (forEachEmptyLoopFinal (forEachLiteralIdxName scope varName n) varName 0 + (((state.setVar (forEachLiteralIdxName scope varName n) 0).setVar + (forEachLiteralCountName scope varName n) (forEachLiteralBound n)).setVar + varName 0) + (forEachLiteralBound n)), + sourceExec_forEach_literal_empty, ?_, ?_⟩ + · exact execIRStmts_forEach_literal_empty_compiled + (scope := scope) (varName := varName) (n := n) (state := state) (extraFuel := extraFuel) hslack hidx_ne_var hcount_ne_var hcount_ne_idx - · exact stmtStepMatches_forEach_literal_two_empty_final - (fields := fields) (scope := scope) (varName := varName) + · exact stmtStepMatches_forEach_literal_empty_final + (fields := fields) (scope := scope) (varName := varName) (n := n) (runtime := runtime) (state := state) hidx_ne_var hidx_not_scope hcount_not_scope hexact hscope hbounded hruntime @@ -15331,6 +15294,39 @@ private theorem stmtListGenericCore_of_supportedStmtList_requireClause_of_surfac simp only [List.foldl, stmtNextScope_requireLiteralGuardFamilyClause clause] exact ihRest hsurface.2 +private theorem stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero + {varName : String} + {body : List Stmt} + (hsurface : + stmtListTouchesUnsupportedContractSurface [Stmt.forEach varName (.literal 0) body] = false) : + stmtListTouchesUnsupportedContractSurface body = false := by + cases body with + | nil => + simp [stmtListTouchesUnsupportedContractSurface] + | cons stmt rest => + simp only [stmtListTouchesUnsupportedContractSurface, + stmtTouchesUnsupportedContractSurface, Bool.or_false, + Bool.or_eq_false_iff] at hsurface + exact Bool.or_eq_false_iff.mpr hsurface + +private theorem stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero_exceptMappingWrites + {varName : String} + {body : List Stmt} + (hsurface : + stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites + [Stmt.forEach varName (.literal 0) body] = false) : + stmtListTouchesUnsupportedContractSurface body = false := by + cases body with + | nil => + simp [stmtListTouchesUnsupportedContractSurface] + | cons stmt rest => + simp only [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, + stmtTouchesUnsupportedContractSurface, + stmtListTouchesUnsupportedContractSurface, Bool.or_false, + Bool.or_eq_false_iff] at hsurface + exact Bool.or_eq_false_iff.mpr hsurface + theorem stmtListGenericCore_of_supportedStmtList_of_surface {fields : List Field} {scope : List String} @@ -15406,21 +15402,15 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface | setStructMember2Single hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hslot hmembers hmember => exact False.elim (false_of_supportedStmtList_setStructMember2Single_surface hsurface) | forEachLiteralBounded hbodyNames _ ih => - rcases compiledStmtStep_forEach_literal_zero hbodyNames (ih (by - simpa [stmtListTouchesUnsupportedContractSurface, - stmtTouchesUnsupportedContractSurface] using hsurface)) with - ⟨compiledIR, hstep⟩ - exact StmtListGenericCore.cons hstep StmtListGenericCore.nil - | forEachLiteralOneEmpty => - rename_i scope varName - rcases compiledStmtStep_forEach_literal_one_empty - (fields := fields) (scope := scope) (varName := varName) with + rcases compiledStmtStep_forEach_literal_zero hbodyNames + (ih (stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero + hsurface)) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty n => rename_i scope varName - rcases compiledStmtStep_forEach_literal_two_empty - (fields := fields) (scope := scope) (varName := varName) with + rcases compiledStmtStep_forEach_literal_empty + (fields := fields) (scope := scope) (varName := varName) (n := n) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => @@ -15553,23 +15543,15 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites hkey1 hscope1 hkey2 hscope2 hvalue hscopeValue hm hmembers hmember hws hss | forEachLiteralBounded hbodyNames _ ih => rcases compiledStmtStep_forEach_literal_zero hbodyNames - (ih (by - exact stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites_eq_false_of_contractSurface - (by simpa [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurface] using hsurface))) with - ⟨compiledIR, hstep⟩ - exact StmtListGenericCore.cons hstep StmtListGenericCore.nil - | forEachLiteralOneEmpty => - rename_i scope varName - rcases compiledStmtStep_forEach_literal_one_empty - (fields := fields) (scope := scope) (varName := varName) with + (ih (stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites_eq_false_of_contractSurface + (stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero_exceptMappingWrites + hsurface))) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty n => rename_i scope varName - rcases compiledStmtStep_forEach_literal_two_empty - (fields := fields) (scope := scope) (varName := varName) with + rcases compiledStmtStep_forEach_literal_empty + (fields := fields) (scope := scope) (varName := varName) (n := n) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause _ ih => @@ -15893,21 +15875,14 @@ theorem stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites_ rcases compiledStmtStep_forEach_literal_zero hbodyNames (stmtListGenericCore_of_supportedStmtList_of_surface hnoConflict hbody (by - simpa [stmtListTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurfaceExceptMappingWrites, - stmtTouchesUnsupportedContractSurface] using hsurface)) with - ⟨compiledIR, hstep⟩ - exact StmtListGenericCore.cons hstep StmtListGenericCore.nil - | forEachLiteralOneEmpty => - rename_i scope varName - rcases compiledStmtStep_forEach_literal_one_empty - (fields := fields) (scope := scope) (varName := varName) with + exact stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero_exceptMappingWrites + hsurface)) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty n => rename_i scope varName - rcases compiledStmtStep_forEach_literal_two_empty - (fields := fields) (scope := scope) (varName := varName) with + rcases compiledStmtStep_forEach_literal_empty + (fields := fields) (scope := scope) (varName := varName) (n := n) with ⟨compiledIR, hstep⟩ exact StmtListGenericCore.cons hstep StmtListGenericCore.nil | requireClause clause hsupportedRest ih => diff --git a/Compiler/Proofs/IRGeneration/IRInterpreter.lean b/Compiler/Proofs/IRGeneration/IRInterpreter.lean index adbb2411f..339e440ea 100644 --- a/Compiler/Proofs/IRGeneration/IRInterpreter.lean +++ b/Compiler/Proofs/IRGeneration/IRInterpreter.lean @@ -1016,6 +1016,14 @@ theorem execIRStmt_for_init_cond_zero .continue sInit := by simp [execIRStmt, hinit, hcond] +theorem execIRStmt_for_init_continue + (fuel : Nat) (state sInit : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (hinit : execIRStmts fuel state init = .continue sInit) : + execIRStmt (Nat.succ fuel) state (.for_ init cond post body) = + execIRStmt (Nat.succ fuel) sInit (.for_ [] cond post body) := by + simp [execIRStmt, hinit, execIRStmts] + theorem execIRStmt_for_body_noncontinue (fuel : Nat) (state sInit : IRState) (init post body : List YulStmt) (cond : YulExpr) @@ -1057,6 +1065,88 @@ theorem execIRStmt_for_one_continue simp only [execIRStmt, hinit, hcond] simp [hcondNZ, hbody, hpost] +/-- Nat-indexed recurrence for the recursive empty-init `for` form. + +After the first iteration of a compiled Yul `for`, the interpreter recurs on +`.for_ [] cond post body` with one less fuel. This theorem packages that +repeated step: callers provide the per-index condition/body/post preservation +facts for states `states k`, and the theorem threads them until the terminal +condition at `states remaining`. -/ +theorem execIRStmt_for_empty_init_recurrence + (fuel remaining : Nat) + (states : Nat → IRState) + (cond : YulExpr) + (post body : List YulStmt) + (hterminal : evalIRExpr (states remaining) cond = some 0) + (hstep : + ∀ k, k < remaining → + ∃ condValue bodyState postState, + evalIRExpr (states k) cond = some condValue ∧ + condValue ≠ 0 ∧ + execIRStmts (fuel - k - 1) (states k) body = .continue bodyState ∧ + execIRStmts (fuel - k - 1) bodyState post = .continue postState ∧ + postState = states (k + 1)) + (hfuel : remaining < fuel) : + execIRStmt fuel (states 0) (.for_ [] cond post body) = + .continue (states remaining) := by + induction remaining generalizing fuel states with + | zero => + cases fuel with + | zero => omega + | succ fuel => + exact execIRStmt_for_init_cond_zero fuel (states 0) (states 0) [] + post body cond (by simp [execIRStmts]) hterminal + | succ remaining ih => + cases fuel with + | zero => omega + | succ fuel => + rcases hstep 0 (by omega) with + ⟨condValue, bodyState, postState, hcond, hcondNZ, hbody, hpost, hpostState⟩ + have htail : + execIRStmt fuel (states 1) (.for_ [] cond post body) = + .continue (states (remaining + 1)) := by + let tailStates : Nat → IRState := fun k => states (k + 1) + have hterminalTail : + evalIRExpr (tailStates remaining) cond = some 0 := by + simpa [tailStates, Nat.add_assoc] using hterminal + have hstepTail : + ∀ k, k < remaining → + ∃ condValue bodyState postState, + evalIRExpr (tailStates k) cond = some condValue ∧ + condValue ≠ 0 ∧ + execIRStmts (fuel - k - 1) (tailStates k) body = + .continue bodyState ∧ + execIRStmts (fuel - k - 1) bodyState post = + .continue postState ∧ + postState = tailStates (k + 1) := by + intro k hk + rcases hstep (k + 1) (by omega) with + ⟨cv, bs, ps, hc, hcv, hb, hp, hps⟩ + refine ⟨cv, bs, ps, ?_, hcv, ?_, ?_, ?_⟩ + · simpa [tailStates, Nat.add_assoc] using hc + · have hfuelEq : Nat.succ fuel - (k + 1) - 1 = fuel - k - 1 := by + omega + simpa [tailStates, hfuelEq, Nat.add_assoc] using hb + · have hfuelEq : Nat.succ fuel - (k + 1) - 1 = fuel - k - 1 := by + omega + simpa [hfuelEq] using hp + · simpa [tailStates, Nat.add_assoc] using hps + have hfuelTail : remaining < fuel := by omega + simpa [tailStates] using + ih (fuel := fuel) (states := tailStates) + hterminalTail hstepTail hfuelTail + have hbody' : execIRStmts fuel (states 0) body = .continue bodyState := by + simpa using hbody + have hpost' : execIRStmts fuel bodyState post = .continue postState := by + simpa using hpost + rw [execIRStmt_for_one_continue + (fuel := fuel) (state := states 0) (sInit := states 0) + (sBody := bodyState) (sPost := postState) + (init := []) (post := post) (body := body) (cond := cond) + (condValue := condValue) + (by simp [execIRStmts]) hcond hcondNZ hbody' hpost'] + simpa [hpostState] using htail + /-- Singleton-list form of `execIRStmt_for_init_cond_zero`. This is the shape needed when a compiled source statement is represented by a @@ -1071,6 +1161,17 @@ theorem execIRStmts_single_for_init_cond_zero simp only [execIRStmts] rw [execIRStmt_for_init_cond_zero fuel state sInit init post body cond hinit hcond] +theorem execIRStmts_single_for_init_continue + (fuel : Nat) (state sInit sFinal : IRState) + (init post body : List YulStmt) (cond : YulExpr) + (hinit : execIRStmts fuel state init = .continue sInit) + (hloop : execIRStmt (Nat.succ fuel) sInit (.for_ [] cond post body) = .continue sFinal) : + execIRStmts (Nat.succ (Nat.succ fuel)) state [.for_ init cond post body] = + .continue sFinal := by + simp only [execIRStmts] + rw [execIRStmt_for_init_continue fuel state sInit init post body cond hinit] + rw [hloop] + /-- Head/tail form of `execIRStmt_for_init_cond_zero`. After the `for` condition evaluates to zero, statement-list execution continues diff --git a/Compiler/Proofs/IRGeneration/SourceSemantics.lean b/Compiler/Proofs/IRGeneration/SourceSemantics.lean index 8556bc7a1..ff69d3385 100644 --- a/Compiler/Proofs/IRGeneration/SourceSemantics.lean +++ b/Compiler/Proofs/IRGeneration/SourceSemantics.lean @@ -683,6 +683,47 @@ theorem execForEachLoop_congr { state with bindings := bindValue state.bindings varName (wordNormalize index) } <;> simp [hrun, execForEachLoop_congr hbody] +def execForEachEmptyLoopFinal + (varName : String) : RuntimeState → Nat → Nat → RuntimeState + | state, _, 0 => state + | state, index, remaining + 1 => + execForEachEmptyLoopFinal varName + { state with bindings := bindValue state.bindings varName (wordNormalize index) } + (index + 1) remaining + +theorem execForEachLoop_empty_body + (varName : String) + (state : RuntimeState) + (index remaining : Nat) : + execForEachLoop varName (fun loopState => .continue loopState) + state index remaining = + .continue (execForEachEmptyLoopFinal varName state index remaining) := by + induction remaining generalizing state index with + | zero => + rfl + | succ remaining ih => + simp [execForEachLoop, execForEachEmptyLoopFinal, ih] + +theorem execForEachLoop_empty_body_zero_bound + (varName : String) + (state : RuntimeState) + (index : Nat) : + execForEachLoop varName (fun loopState => .continue loopState) + state index 0 = + .continue state := rfl + +theorem execForEachLoop_empty_body_positive_bound + (varName : String) + (state : RuntimeState) + (index remaining : Nat) : + execForEachLoop varName (fun loopState => .continue loopState) + state index (remaining + 1) = + .continue + (execForEachEmptyLoopFinal varName + { state with bindings := bindValue state.bindings varName (wordNormalize index) } + (index + 1) remaining) := by + simp [execForEachLoop_empty_body, execForEachEmptyLoopFinal] + private def storageArraySetAt : List Verity.Core.Uint256 → Nat → Verity.Core.Uint256 → Option (List Verity.Core.Uint256) | [], _, _ => none | _ :: rest, 0, value => some (value :: rest) diff --git a/Compiler/Proofs/IRGeneration/SupportedFragment.lean b/Compiler/Proofs/IRGeneration/SupportedFragment.lean index 01dc69590..022ab9cf6 100644 --- a/Compiler/Proofs/IRGeneration/SupportedFragment.lean +++ b/Compiler/Proofs/IRGeneration/SupportedFragment.lean @@ -347,20 +347,15 @@ inductive SupportedStmtList (fields : List Field) : List String → List Stmt (∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) → SupportedStmtList fields (varName :: scope) body → SupportedStmtList fields scope [Stmt.forEach varName (.literal 0) body] - /-- First positive loop-preservation case: a one-iteration loop with an empty - body. This exercises the compiled Yul `for` init/condition/body-bind/post - path without yet admitting arbitrary positive loop bodies. -/ - | forEachLiteralOneEmpty + /-- Positive literal loops are supported only for the empty-body case. This + exercises arbitrary Yul `for` recurrence while keeping non-empty positive + loop bodies outside the fragment until their body-preservation proof is + threaded through the loop step. -/ + | forEachLiteralEmpty {scope : List String} {varName : String} : - SupportedStmtList fields scope [Stmt.forEach varName (.literal 1) []] - /-- Second positive loop-preservation case: a two-iteration loop with an - empty body. This proves one recursive empty-init `for` step after the first - post, which is the recurrence shape needed for larger literal bounds. -/ - | forEachLiteralTwoEmpty - {scope : List String} - {varName : String} : - SupportedStmtList fields scope [Stmt.forEach varName (.literal 2) []] + (n : Nat) → + SupportedStmtList fields scope [Stmt.forEach varName (.literal n) []] | requireClause {scope : List String} (clause : RequireLiteralGuardFamilyClause) diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index 812488938..6204e7186 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -1563,6 +1563,7 @@ def stmtTouchesUnsupportedContractSurface (stmt : Stmt) : Bool := | .tryExternalCallBind _ _ _ _ | .unsafeBlock _ _ | .matchAdt _ _ _ => true | .forEach _ (.literal 0) body => stmtListTouchesUnsupportedContractSurface body + | .forEach _ (.literal _) [] => false | .forEach _ _ _ => true def stmtTouchesUnsupportedContractSurfaceWithEvents @@ -3011,11 +3012,7 @@ theorem SupportedStmtList.helperSurfaceClosed simpa [stmtListTouchesUnsupportedHelperSurface, stmtTouchesUnsupportedHelperSurface, exprTouchesUnsupportedHelperSurface] using ih - | forEachLiteralOneEmpty => - simp [stmtListTouchesUnsupportedHelperSurface, - stmtTouchesUnsupportedHelperSurface, - exprTouchesUnsupportedHelperSurface] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListTouchesUnsupportedHelperSurface, stmtTouchesUnsupportedHelperSurface, exprTouchesUnsupportedHelperSurface] @@ -3201,10 +3198,7 @@ theorem SupportedStmtList.internalHelperCallNames_nil simpa [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, exprInternalHelperCallNames] using ih - | forEachLiteralOneEmpty => - simp [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, - exprInternalHelperCallNames] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListInternalHelperCallNames, stmtInternalHelperCallNames, exprInternalHelperCallNames] | requireClause clause _ ih => @@ -4400,11 +4394,32 @@ theorem stmtTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed | literal n => cases n with | zero => - simp only [stmtTouchesUnsupportedContractSurface] at hsurface - simpa [stmtTouchesUnsupportedHelperSurface, exprTouchesUnsupportedHelperSurface] using - stmtListTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed - hsurface - | succ n => simp [stmtTouchesUnsupportedContractSurface] at hsurface + cases body with + | nil => + simp [stmtTouchesUnsupportedHelperSurface, + stmtListTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedHelperSurface] + | cons stmt rest => + simp only [stmtTouchesUnsupportedContractSurface, + stmtListTouchesUnsupportedContractSurface, + Bool.or_eq_false_iff] at hsurface + simp [stmtTouchesUnsupportedHelperSurface, + stmtListTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedHelperSurface, + Bool.or_eq_false_iff] + exact ⟨ + stmtTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed + hsurface.1, + stmtListTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed + hsurface.2⟩ + | succ n => + cases body with + | nil => + simp [stmtTouchesUnsupportedHelperSurface, + stmtListTouchesUnsupportedHelperSurface, + exprTouchesUnsupportedHelperSurface] + | cons _ _ => + simp [stmtTouchesUnsupportedContractSurface] at hsurface | _ => simp [stmtTouchesUnsupportedContractSurface] at hsurface termination_by sizeOf stmt @@ -5080,9 +5095,7 @@ private theorem supportedStmtList_usesArrayElement_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] using ih - | forEachLiteralOneEmpty => - simp [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListUsesArrayElement, stmtUsesArrayElement, exprUsesArrayElement] | requireClause clause _ ih => simp only [stmtListUsesArrayElement, Bool.or_eq_false_iff, Bool.false_or] @@ -5201,10 +5214,7 @@ private theorem supportedStmtList_usesStorageArrayElement_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, exprUsesStorageArrayElement] using ih - | forEachLiteralOneEmpty => - simp [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, - exprUsesStorageArrayElement] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListUsesStorageArrayElement, stmtUsesStorageArrayElement, exprUsesStorageArrayElement] | requireClause clause _ ih => @@ -5317,10 +5327,7 @@ private theorem supportedStmtList_usesDynamicBytesEq_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, exprUsesDynamicBytesEq] using ih - | forEachLiteralOneEmpty => - simp [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, - exprUsesDynamicBytesEq] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListUsesDynamicBytesEq, stmtUsesDynamicBytesEq, exprUsesDynamicBytesEq] | requireClause clause _ ih => @@ -5691,9 +5698,7 @@ private theorem supportedStmtList_usesMulDiv512_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] using ih - | forEachLiteralOneEmpty => - simp [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListUsesMulDiv512, stmtUsesMulDiv512, exprUsesMulDiv512] | requireClause clause _ ih => simp only [stmtListUsesMulDiv512, Bool.or_eq_false_iff, Bool.false_or] @@ -5805,10 +5810,7 @@ private theorem supportedStmtList_usesParamDynamicHeadWord_false | forEachLiteralBounded _ _ ih => simpa [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, exprUsesParamDynamicHeadWord] using ih - | forEachLiteralOneEmpty => - simp [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, - exprUsesParamDynamicHeadWord] - | forEachLiteralTwoEmpty => + | forEachLiteralEmpty _ => simp [stmtListUsesParamDynamicHeadWord, stmtUsesParamDynamicHeadWord, exprUsesParamDynamicHeadWord] | requireClause clause _ ih => diff --git a/PrintAxioms.lean b/PrintAxioms.lean index e6dce6b5a..d036c7496 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -2433,25 +2433,20 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.source_bindValue_overwrite -- private -- Compiler.Proofs.IRGeneration.irState_setVar_idem -- private -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_zero -- private - -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_one_empty -- private - -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_two_empty -- private + -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_empty -- private -- Compiler.Proofs.IRGeneration.forEachZero_fresh_facts -- private - -- Compiler.Proofs.IRGeneration.forEachOne_fresh_facts -- private - -- Compiler.Proofs.IRGeneration.forEachTwo_fresh_facts -- private + -- Compiler.Proofs.IRGeneration.forEachLiteral_fresh_facts -- private -- Compiler.Proofs.IRGeneration.evalIRExpr_forEachZeroCond_after_init -- private -- Compiler.Proofs.IRGeneration.forEachZero_initFuel_of_slack -- private -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_zero_compiled -- private - -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_one_empty_compiled -- private - -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_two_empty_compiled -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_empty_compiled -- private -- Compiler.Proofs.IRGeneration.forEachZero_nextScopeIncluded -- private -- Compiler.Proofs.IRGeneration.runtimeStateMatchesIR_forEachZeroLoop -- private -- Compiler.Proofs.IRGeneration.bindingsExactly_forEachZeroBase -- private -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_zero_final -- private - -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_one_empty_final -- private - -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_two_empty_final -- private + -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_empty_final -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_zero -- private - -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_one_empty -- private - -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_two_empty -- private + -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_empty -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingUintSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingChainSingle_of_slotSafety -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_singleton_setMappingSingle_of_slotSafety -- private diff --git a/scripts/check_proof_length.py b/scripts/check_proof_length.py index f74e07128..ae1882299 100644 --- a/scripts/check_proof_length.py +++ b/scripts/check_proof_length.py @@ -80,14 +80,10 @@ "compiledStmtStep_require", "compiledStmtStep_return", "compiledStmtStep_ite", - # Staged forEach proof witnesses for PR #1935. These are deliberately - # explicit loop-shape proofs for bounds 1 and 2; the next proof step is to - # replace them with a Nat-indexed recurrence instead of mechanically - # splitting the same init/cond/body/post state facts into tiny local lemmas. - "execIRStmts_forEach_literal_one_empty_compiled", - "execIRStmts_forEach_literal_two_empty_compiled", - "stmtStepMatches_forEach_literal_one_empty_final", - "stmtStepMatches_forEach_literal_two_empty_final", + # Nat-indexed forEach empty-body proof witnesses. Positive non-empty + # literal loops intentionally remain outside the supported fragment. + "execIRStmts_forEach_literal_empty_compiled", + "stmtStepMatches_forEach_literal_empty_final", # --- Storage write compiled step proofs --- "compiledStmtStep_setStorage_singleSlot", "compiledStmtStep_setStorage_aliasSlots", From 95293f4e59ca564630778262bc99fd0e6909d891 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 May 2026 10:36:27 +0100 Subject: [PATCH 18/27] chore: auto-refresh derived artifacts --- PrintAxioms.lean | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/PrintAxioms.lean b/PrintAxioms.lean index d036c7496..015362a55 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -2438,12 +2438,21 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.forEachLiteral_fresh_facts -- private -- Compiler.Proofs.IRGeneration.evalIRExpr_forEachZeroCond_after_init -- private -- Compiler.Proofs.IRGeneration.forEachZero_initFuel_of_slack -- private - -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_zero_compiled -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_empty_body_assign -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_empty_post_increment -- private + -- Compiler.Proofs.IRGeneration.evalIRExpr_forEach_empty_cond_lt -- private + -- Compiler.Proofs.IRGeneration.evalIRExpr_forEach_empty_cond_eq -- private + -- Compiler.Proofs.IRGeneration.execIRStmt_forEach_empty_loop_from_idx -- private + -- Compiler.Proofs.IRGeneration.execIRStmt_forEach_empty_loop_idx_bound -- private + -- Compiler.Proofs.IRGeneration.forEachLiteral_loopFuel_of_slack -- private + -- Compiler.Proofs.IRGeneration.forEachLiteral_initFuel_of_slack -- private -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_empty_compiled -- private + -- Compiler.Proofs.IRGeneration.execIRStmts_forEach_literal_zero_compiled -- private -- Compiler.Proofs.IRGeneration.forEachZero_nextScopeIncluded -- private -- Compiler.Proofs.IRGeneration.runtimeStateMatchesIR_forEachZeroLoop -- private -- Compiler.Proofs.IRGeneration.bindingsExactly_forEachZeroBase -- private -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_zero_final -- private + -- Compiler.Proofs.IRGeneration.forEach_empty_final_rel -- private -- Compiler.Proofs.IRGeneration.stmtStepMatches_forEach_literal_empty_final -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_zero -- private -- Compiler.Proofs.IRGeneration.compiledStmtStep_forEach_literal_empty -- private @@ -2503,6 +2512,8 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.stmtNextScope_requireLiteralGuardFamilyClause -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_append_of_surface_exceptMappingWrites -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_requireClause_of_surface_exceptMappingWrites -- private + -- Compiler.Proofs.IRGeneration.stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero -- private + -- Compiler.Proofs.IRGeneration.stmtListTouchesUnsupportedContractSurface_body_of_singleton_forEach_zero_exceptMappingWrites -- private Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_of_surface Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_of_surface_exceptMappingWrites_stmtSafety @@ -2622,10 +2633,13 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.execIRStmt_for_init_noncontinue Compiler.Proofs.IRGeneration.execIRStmt_for_cond_none Compiler.Proofs.IRGeneration.execIRStmt_for_init_cond_zero + Compiler.Proofs.IRGeneration.execIRStmt_for_init_continue Compiler.Proofs.IRGeneration.execIRStmt_for_body_noncontinue Compiler.Proofs.IRGeneration.execIRStmt_for_post_noncontinue Compiler.Proofs.IRGeneration.execIRStmt_for_one_continue + Compiler.Proofs.IRGeneration.execIRStmt_for_empty_init_recurrence Compiler.Proofs.IRGeneration.execIRStmts_single_for_init_cond_zero + Compiler.Proofs.IRGeneration.execIRStmts_single_for_init_continue Compiler.Proofs.IRGeneration.execIRStmts_cons_for_init_cond_zero Compiler.Proofs.IRGeneration.execIRStmts_single_for_one_continue Compiler.Proofs.IRGeneration.execIRStmts_cons_for_one_continue @@ -2879,6 +2893,9 @@ end Verity.AxiomAudit Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_succ_continue_iff Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_succ_continue Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_congr + Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_empty_body + Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_empty_body_zero_bound + Compiler.Proofs.IRGeneration.SourceSemantics.execForEachLoop_empty_body_positive_bound -- Compiler.Proofs.IRGeneration.SourceSemantics.evalExpr_literal -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.evalExpr_param -- private -- Compiler.Proofs.IRGeneration.SourceSemantics.evalExpr_localVar -- private @@ -5516,4 +5533,4 @@ end Verity.AxiomAudit Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args ] --- Total: 5223 theorems/lemmas (3612 public, 1611 private, 0 sorry'd) +-- Total: 5235 theorems/lemmas (3618 public, 1617 private, 0 sorry'd) From 94424dcf0b808e134f3d1520a365e5d6030b8d8c Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 11:37:36 +0200 Subject: [PATCH 19/27] Allowlist generalized forEach proof witnesses --- scripts/check_proof_length.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/check_proof_length.py b/scripts/check_proof_length.py index ae1882299..09e489e13 100644 --- a/scripts/check_proof_length.py +++ b/scripts/check_proof_length.py @@ -81,7 +81,13 @@ "compiledStmtStep_return", "compiledStmtStep_ite", # Nat-indexed forEach empty-body proof witnesses. Positive non-empty - # literal loops intentionally remain outside the supported fragment. + # literal loops intentionally remain outside the supported fragment. The + # longer witnesses below are recurrence/state-relation proofs where + # splitting would duplicate the same loop-index, binder, and fuel facts. + "execIRStmt_forEach_empty_loop_from_idx", + "execIRStmt_for_empty_init_recurrence", + "forEach_empty_final_rel", + "stmtTouchesUnsupportedHelperSurface_eq_false_of_contractSurfaceClosed", "execIRStmts_forEach_literal_empty_compiled", "stmtStepMatches_forEach_literal_empty_final", # --- Storage write compiled step proofs --- From e46dd8ac65f826297913323d8c5928fb494e5e21 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 12:09:03 +0200 Subject: [PATCH 20/27] Document forEach proof boundary --- docs/INTERPRETER_FEATURE_MATRIX.md | 4 ++++ docs/ROADMAP.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/INTERPRETER_FEATURE_MATRIX.md b/docs/INTERPRETER_FEATURE_MATRIX.md index c4d278bab..27dad68dd 100644 --- a/docs/INTERPRETER_FEATURE_MATRIX.md +++ b/docs/INTERPRETER_FEATURE_MATRIX.md @@ -115,6 +115,10 @@ Legend: **ok** = supported, **0** = returns 0 (not modeled), **del** = delegated Legend: **ok** = supported, **rev** = reverts (not modeled), **nop** = no-op (codegen concern), **--** = not applicable, **n/m** = not modeled. +`Stmt.forEach` proof coverage is intentionally partial: zero-bound loops with +supported bodies are proved, arbitrary literal-bound empty-body loops are +proved, and positive literal loops with non-empty bodies remain future work. + ECMs include standard Verity-core modules for generic external-call mechanics, including `Compiler.Modules.Calls.bubblingValueCall`, which lowers Solidity-style `call{value: v}(data)` wrappers to Yul `call` and forwards exact diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index af4d8c929..bc47d826b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -209,7 +209,7 @@ UnlinkPool, a ZK privacy pool, was the first non-trivial contract built with Ver | Feature | Issue | CompilationModel | Core/Interpreter | |---------|-------|-------------|-----------------| | If/else branching | #179 | `Stmt.ite` | `execStmt` mutual recursion | -| ForEach loops | #179 | `Stmt.forEach` | `execStmtsFuel` + `expandForEach` desugaring | +| ForEach loops | #179 | `Stmt.forEach` | `execStmtsFuel` + `expandForEach` desugaring; proofs cover zero-bound loops with supported bodies and arbitrary literal-bound empty-body loops, while positive non-empty bodies remain future work | | Array/bytes params | #180 | `ParamType.bytes32`, `.array`, `.fixedArray`, `.bytes` | `arrayParams` in `EvalContext` | | Storage dynamic arrays | #1571 | `FieldType.dynamicArray`, `Expr.storageArrayLength` / `.storageArrayElement`, `Stmt.storageArrayPush` / `.storageArrayPop` / `.setStorageArrayElement` | compile-time/Yul lowering, source-side runtime semantics, and macro surface are in place; whole-contract proofs still pending | | Internal function calls | #181 | `Stmt.internalCall`, `Expr.internalCall`, `FunctionSpec.isInternal` | Statement + expression evaluation | From de40e2cdc86e5cf24d299f5895b724eb042d4a0d Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 13:24:48 +0200 Subject: [PATCH 21/27] Trigger proof verification rerun From e7613d422794e132807002e57f8bc9ff4069ca09 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 13:26:48 +0200 Subject: [PATCH 22/27] Clarify forEach proof boundary --- docs/INTERPRETER_FEATURE_MATRIX.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/INTERPRETER_FEATURE_MATRIX.md b/docs/INTERPRETER_FEATURE_MATRIX.md index 27dad68dd..5b104326e 100644 --- a/docs/INTERPRETER_FEATURE_MATRIX.md +++ b/docs/INTERPRETER_FEATURE_MATRIX.md @@ -118,6 +118,8 @@ Legend: **ok** = supported, **rev** = reverts (not modeled), **nop** = no-op (co `Stmt.forEach` proof coverage is intentionally partial: zero-bound loops with supported bodies are proved, arbitrary literal-bound empty-body loops are proved, and positive literal loops with non-empty bodies remain future work. +This is the current IR-generation proof boundary, not a claim about arbitrary +non-empty loop-body preservation. ECMs include standard Verity-core modules for generic external-call mechanics, including `Compiler.Modules.Calls.bubblingValueCall`, which lowers From e865066d5e70c1e86f22103ede9e32e1696a44b0 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 13:30:46 +0200 Subject: [PATCH 23/27] Clarify literal forEach fragment comment --- Compiler/Proofs/IRGeneration/SupportedFragment.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Compiler/Proofs/IRGeneration/SupportedFragment.lean b/Compiler/Proofs/IRGeneration/SupportedFragment.lean index 022ab9cf6..2393fac99 100644 --- a/Compiler/Proofs/IRGeneration/SupportedFragment.lean +++ b/Compiler/Proofs/IRGeneration/SupportedFragment.lean @@ -347,10 +347,10 @@ inductive SupportedStmtList (fields : List Field) : List String → List Stmt (∀ name, name ∈ collectStmtListNames body → name ∈ varName :: scope) → SupportedStmtList fields (varName :: scope) body → SupportedStmtList fields scope [Stmt.forEach varName (.literal 0) body] - /-- Positive literal loops are supported only for the empty-body case. This - exercises arbitrary Yul `for` recurrence while keeping non-empty positive - loop bodies outside the fragment until their body-preservation proof is - threaded through the loop step. -/ + /-- Literal loops with any bound are supported for empty bodies. This exercises + arbitrary Yul `for` recurrence while keeping non-empty positive loop bodies + outside the fragment until their body-preservation proof is threaded through + the loop step. -/ | forEachLiteralEmpty {scope : List String} {varName : String} : From 33a94210d45e0c0298d3d000e5110658c8a34c5d Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 15:06:57 +0200 Subject: [PATCH 24/27] Shorten local persistence cache keys --- scripts/ci_local_persistence.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/ci_local_persistence.sh b/scripts/ci_local_persistence.sh index b64f79700..1d95b1532 100755 --- a/scripts/ci_local_persistence.sh +++ b/scripts/ci_local_persistence.sh @@ -20,7 +20,15 @@ EOF } sanitize_key() { - printf '%s' "$1" | tr -cs 'A-Za-z0-9._-' '_' + local sanitized hash + sanitized="$(printf '%s' "$1" | tr -cs 'A-Za-z0-9._-' '_')" + if [ "${#sanitized}" -le 200 ]; then + printf '%s' "$sanitized" + return + fi + + hash="$(printf '%s' "$1" | sha256sum | awk '{print $1}')" + printf '%s-%s' "${sanitized:0:160}" "$hash" } is_dir_empty() { From e931d13a9276f2da36d43cd0f0f49ee68fd62d4a Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Tue, 26 May 2026 23:14:46 +0200 Subject: [PATCH 25/27] Address forEach proof review findings --- Compiler/Proofs/EndToEnd.lean | 53 ++++++++++++++----- .../Proofs/IRGeneration/GenericInduction.lean | 18 ------- .../Proofs/IRGeneration/SupportedSpec.lean | 1 + 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Compiler/Proofs/EndToEnd.lean b/Compiler/Proofs/EndToEnd.lean index b1e524209..e2360d0a7 100644 --- a/Compiler/Proofs/EndToEnd.lean +++ b/Compiler/Proofs/EndToEnd.lean @@ -5291,12 +5291,27 @@ theorem supportedStmtList_safe_of_state_effect_closed | setStructMember2Single => simp [stmtListTouchesUnsupportedStateSurface, stmtTouchesUnsupportedStateSurface] at hState - | forEachLiteralBounded => - simp [stmtListTouchesUnsupportedStateSurface, - stmtTouchesUnsupportedStateSurface] at hState + | @forEachLiteralBounded scope varName body _ _ _ => + cases body with + | nil => + exact Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.externalRecursiveRawLog + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.cons + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmt.forEach + _ _ _ + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExpr.literal _) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) + | cons _ _ => + simp [stmtListTouchesUnsupportedStateSurface, + stmtTouchesUnsupportedStateSurface] at hState | forEachLiteralEmpty _ => - simp [stmtListTouchesUnsupportedStateSurface, - stmtTouchesUnsupportedStateSurface] at hState + exact Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.externalRecursiveRawLog + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.cons + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmt.forEach + _ _ _ + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExpr.literal _) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) | requireClause clause _ ih => simpa using Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.append @@ -5563,14 +5578,28 @@ theorem supportedStmtList_safe_of_state_except_mapping_writes_stmt_safety (Compiler.Proofs.YulGeneration.Backends.bridgedSourceExpr_of_exprCompileCore hKey2) (Compiler.Proofs.YulGeneration.Backends.bridgedSourceExpr_of_exprCompileCore hValue) hMapping2 hMembers hFindMember rfl hZero hSlots - | forEachLiteralBounded => - simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, - stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, - stmtTouchesUnsupportedStateSurface] at hState + | @forEachLiteralBounded scope varName body _ _ _ => + cases body with + | nil => + exact Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.externalRecursiveRawLog + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.cons + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmt.forEach + _ _ _ + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExpr.literal _) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) + | cons _ _ => + simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, + stmtTouchesUnsupportedStateSurface] at hState | forEachLiteralEmpty _ => - simp [stmtListTouchesUnsupportedStateSurfaceExceptMappingWrites, - stmtTouchesUnsupportedStateSurfaceExceptMappingWrites, - stmtTouchesUnsupportedStateSurface] at hState + exact Compiler.Proofs.YulGeneration.Backends.BridgedSafeStmts.externalRecursiveRawLog + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.cons + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmt.forEach + _ _ _ + (Compiler.Proofs.YulGeneration.Backends.BridgedSourceExpr.literal _) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) + Compiler.Proofs.YulGeneration.Backends.BridgedSourceExternalRecursiveBodyWithRawLogStmts.nil) | @requireClause scope clause rest _ ih => have hTailSafety : ∀ stmt ∈ rest, StmtMappingWriteSlotSafe fields stmt := by diff --git a/Compiler/Proofs/IRGeneration/GenericInduction.lean b/Compiler/Proofs/IRGeneration/GenericInduction.lean index 84dc2b026..c9288b3ec 100644 --- a/Compiler/Proofs/IRGeneration/GenericInduction.lean +++ b/Compiler/Proofs/IRGeneration/GenericInduction.lean @@ -12923,24 +12923,6 @@ private def forEachZeroRuntimeLoop SourceSemantics.RuntimeState := { runtime with bindings := SourceSemantics.bindValue runtime.bindings varName 0 } -private theorem source_bindValue_idem - (bindings : List (String × Nat)) (name : String) (value : Nat) : - SourceSemantics.bindValue (SourceSemantics.bindValue bindings name value) name value = - SourceSemantics.bindValue bindings name value := by - simp [SourceSemantics.bindValue, List.filter_filter] - -private theorem source_bindValue_overwrite - (bindings : List (String × Nat)) (name : String) (first second : Nat) : - SourceSemantics.bindValue (SourceSemantics.bindValue bindings name first) name second = - SourceSemantics.bindValue bindings name second := by - simp [SourceSemantics.bindValue, List.filter_filter] - -private theorem irState_setVar_idem - (state : IRState) (name : String) (value : Nat) : - (state.setVar name value).setVar name value = state.setVar name value := by - cases state - simp [IRState.setVar, List.filter_filter] - private theorem sourceExec_forEach_literal_zero {fields : List Field} {runtime : SourceSemantics.RuntimeState} diff --git a/Compiler/Proofs/IRGeneration/SupportedSpec.lean b/Compiler/Proofs/IRGeneration/SupportedSpec.lean index 7e61c3a38..a22db0d5d 100644 --- a/Compiler/Proofs/IRGeneration/SupportedSpec.lean +++ b/Compiler/Proofs/IRGeneration/SupportedSpec.lean @@ -1236,6 +1236,7 @@ def stmtTouchesUnsupportedStateSurface : Stmt → Bool exprTouchesUnsupportedStateSurface cond || stmtListTouchesUnsupportedStateSurface thenBranch || stmtListTouchesUnsupportedStateSurface elseBranch + | .forEach _ (.literal _) [] => false | .forEach _ _ _ => true /-- Weaker Tier 2 state-surface gate used by the singleton storage-write bridge: From b8fd14701af12dcda5f1429c558ded42d4ce3948 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 May 2026 22:16:35 +0100 Subject: [PATCH 26/27] chore: auto-refresh derived artifacts --- PrintAxioms.lean | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/PrintAxioms.lean b/PrintAxioms.lean index 0eb7569db..59e28eba2 100644 --- a/PrintAxioms.lean +++ b/PrintAxioms.lean @@ -2430,9 +2430,6 @@ end Verity.AxiomAudit -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_setStorageAddrSingleSlot_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_mstoreSingle_of_surface -- private -- Compiler.Proofs.IRGeneration.stmtListGenericCore_of_supportedStmtList_tstoreSingle_of_surface -- private - -- Compiler.Proofs.IRGeneration.source_bindValue_idem -- private - -- Compiler.Proofs.IRGeneration.source_bindValue_overwrite -- private - -- Compiler.Proofs.IRGeneration.irState_setVar_idem -- private -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_zero -- private -- Compiler.Proofs.IRGeneration.sourceExec_forEach_literal_empty -- private -- Compiler.Proofs.IRGeneration.forEachZero_fresh_facts -- private @@ -5546,4 +5543,4 @@ end Verity.AxiomAudit Compiler.Proofs.YulGeneration.YulTransaction.ofIR_args ] --- Total: 5245 theorems/lemmas (3628 public, 1617 private, 0 sorry'd) +-- Total: 5242 theorems/lemmas (3628 public, 1614 private, 0 sorry'd) From 417abe2f2394fb78332b41fe46f6dc4158898624 Mon Sep 17 00:00:00 2001 From: "Thomas Marchand (agent)" Date: Wed, 27 May 2026 08:16:38 +0200 Subject: [PATCH 27/27] docs: clarify proven forEach fragment --- README.md | 2 +- docs-site/content/compiler.mdx | 2 +- docs-site/content/edsl-api-reference.mdx | 4 +++- docs-site/content/verification.mdx | 2 ++ docs-site/public/llms.txt | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8224f76a8..d49511036 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Verity proves that compilation preserves behavior at three stages. Each layer is **Layer 1** (EDSL to CompilationModel): the `verity_contract` macro generates both an executable Lean program and a compiler-facing model from a single definition. Per-contract bridge theorems prove they agree. -**Layer 2** (CompilationModel to IR): a generic whole-contract theorem covers the supported fragment with zero axioms. No per-contract proof effort needed. +**Layer 2** (CompilationModel to IR): a generic whole-contract theorem covers the supported fragment with zero axioms. No per-contract proof effort needed. `forEach` support is deliberately partial: zero-bound loops with supported bodies and arbitrary literal-bound empty-body loops are proved, while positive non-empty loop bodies remain outside the current theorem. **Layer 3** (IR to Yul): all statement types are proven equivalent. The dispatch bridge is an explicit theorem hypothesis, not an axiom. diff --git a/docs-site/content/compiler.mdx b/docs-site/content/compiler.mdx index 82f41cb77..7a76c27d7 100644 --- a/docs-site/content/compiler.mdx +++ b/docs-site/content/compiler.mdx @@ -165,7 +165,7 @@ constructor := some { > > **Axiomatized-primitive boundary**: The `keccak256` intrinsic also compiles, but it remains axiomatized in the current proof stack rather than fully modeled end to end. When it appears, archive `--trust-report` and add `--deny-axiomatized-primitives` if the selected contracts must stay inside the proved subset (see issue `#1411`). -**Statements** cover local variables, storage and mapping writes, `require`, `return`, memory writes, returndata copy and revert bubbling, `stop`, `ite` branching, bounded `forEach` loops, event emission, and internal calls. +**Statements** cover local variables, storage and mapping writes, `require`, `return`, memory writes, returndata copy and revert bubbling, `stop`, `ite` branching, event emission, internal calls, and a precisely scoped `forEach` fragment. The proved `forEach` cases are zero-bound loops with supported bodies and arbitrary literal-bound empty-body loops; positive literal loops with non-empty bodies remain future proof work. Low-level calls, linear-memory primitives, `rawLog`, and `keccak256` compile but remain partially modeled in the proof interpreter. Functions or constructors that use direct assembly-shaped primitives without `localObligations [...]` fail closed under `--deny-axiomatized-primitives`. diff --git a/docs-site/content/edsl-api-reference.mdx b/docs-site/content/edsl-api-reference.mdx index a390ee4e5..aaf2645ff 100644 --- a/docs-site/content/edsl-api-reference.mdx +++ b/docs-site/content/edsl-api-reference.mdx @@ -66,7 +66,9 @@ If `attempt` reverts with message `msg`, `tryCatch` runs `handler msg` on the st ### `forEach` -Bounded iteration in the compilation-model surface (`Stmt.forEach`). Accumulator-style loops use `Stmt.assignVar` inside `Stmt.forEach`; the macro path currently rejects mutating `let mut` locals from nested `forEach` bodies, so macro-authored contracts should use explicit memory scratch space for accumulators. +Bounded iteration in the compilation-model surface (`Stmt.forEach`). The current compiler proof covers zero-bound loops with supported bodies and arbitrary literal-bound empty-body loops. Positive loops with non-empty bodies compile at the surface but are not yet part of the generic IR-generation preservation theorem. + +Accumulator-style loops use `Stmt.assignVar` inside `Stmt.forEach`; the macro path currently rejects mutating `let mut` locals from nested `forEach` bodies, so macro-authored contracts should use explicit memory scratch space for accumulators. ```verity forEach "i" count (do diff --git a/docs-site/content/verification.mdx b/docs-site/content/verification.mdx index 4b8a19e4d..ca12069ba 100644 --- a/docs-site/content/verification.mdx +++ b/docs-site/content/verification.mdx @@ -47,6 +47,8 @@ Reusable proof infrastructure in `Verity/Proofs/Stdlib/`: - **Typed IR**: `Compiler/TypedIRCompilerCorrectness.lean`, 36 supported statement fragments including ABI-head tuples, bytes, fixed/dynamic arrays, and strings as word-typed inputs. - **Yul semantics + preservation**: `Compiler/Proofs/YulGeneration/`, with an EVMYulLean-bridged native backend in `Compiler/Proofs/YulGeneration/Backends/`. +The Layer 2 `forEach` boundary is intentionally narrow: the generic theorem covers `Stmt.forEach varName (.literal 0) body` when `body` is supported, and `Stmt.forEach varName (.literal n) []` for any literal natural bound `n`. It does not yet prove positive loops with non-empty bodies or nonliteral bounds. + ## See also - [Proof Techniques](/proof-techniques), guard modeling, unfolding, list-sum reasoning. diff --git a/docs-site/public/llms.txt b/docs-site/public/llms.txt index bdb40c77a..57446524d 100644 --- a/docs-site/public/llms.txt +++ b/docs-site/public/llms.txt @@ -14,7 +14,7 @@ EDSL --> CompilationModel --> IR --> Yul --> EVM bytecode Every transition inside the proof envelope is either fully verified or recorded as an explicit assumption in the per-build trust report. The Yul-to-bytecode step is delegated to `solc --strict-assembly`. -**Layer 2**: Generic whole-contract theorem for the supported fragment. 0 axioms. 0 documented Lean axioms remain (AXIOMS.md). +**Layer 2**: Generic whole-contract theorem for the supported fragment. 0 axioms. 0 documented Lean axioms remain (AXIOMS.md). `forEach` proof support is partial: zero-bound loops with supported bodies and arbitrary literal-bound empty-body loops are proved; positive non-empty loop bodies and nonliteral bounds are not yet proved. ## Quick facts