Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f1a6806
Add IR for-loop execution lemmas
Th0rgal May 24, 2026
d7036bb
Add source forEach loop semantics
Th0rgal May 24, 2026
a588e94
Align forEach lowering with source semantics
Th0rgal May 24, 2026
58de17b
Add bounded forEach fragment scaffolding
Th0rgal May 24, 2026
2103347
chore: auto-refresh derived artifacts
github-actions[bot] May 25, 2026
221d369
Add forEach source loop helper lemmas
Th0rgal May 25, 2026
89c0317
Add reusable Yul for-loop execution lemmas
Th0rgal May 25, 2026
cba7f1a
Add forEach zero-init execution lemma
Th0rgal May 25, 2026
4c135e0
Prove zero-bound forEach preservation
Th0rgal May 26, 2026
efdb0b7
Merge main into forEach proof branch
Th0rgal May 26, 2026
28579d6
Split zero-bound forEach proof
Th0rgal May 26, 2026
d62993c
chore: auto-refresh derived artifacts
github-actions[bot] May 26, 2026
9764203
Prove one-iteration forEach preservation
Th0rgal May 26, 2026
9851d0b
chore: auto-refresh derived artifacts
github-actions[bot] May 26, 2026
52eb6b7
Prove two-iteration forEach preservation
Th0rgal May 26, 2026
824562c
chore: auto-refresh derived artifacts
github-actions[bot] May 26, 2026
63f5473
Allowlist staged forEach proof shapes
Th0rgal May 26, 2026
d64cd78
Prove generic empty forEach loops
Th0rgal May 26, 2026
95293f4
chore: auto-refresh derived artifacts
github-actions[bot] May 26, 2026
94424dc
Allowlist generalized forEach proof witnesses
Th0rgal May 26, 2026
e46dd8a
Document forEach proof boundary
Th0rgal May 26, 2026
de40e2c
Trigger proof verification rerun
Th0rgal May 26, 2026
e7613d4
Clarify forEach proof boundary
Th0rgal May 26, 2026
e865066
Clarify literal forEach fragment comment
Th0rgal May 26, 2026
33a9421
Shorten local persistence cache keys
Th0rgal May 26, 2026
85493d3
Merge remote-tracking branch 'origin/main' into proof/forEach-loop-fr…
Th0rgal May 26, 2026
e931d13
Address forEach proof review findings
Th0rgal May 26, 2026
b8fd147
chore: auto-refresh derived artifacts
github-actions[bot] May 26, 2026
cc27323
Merge remote-tracking branch 'origin/main' into proof/forEach-loop-fr…
Th0rgal May 27, 2026
417abe2
docs: clarify proven forEach fragment
Th0rgal May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions Compiler/CompilationModel/Compile.lean
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
43 changes: 43 additions & 0 deletions Compiler/Proofs/EndToEnd.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5291,6 +5291,27 @@ theorem supportedStmtList_safe_of_state_effect_closed
| setStructMember2Single =>
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 _ =>
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
Expand Down Expand Up @@ -5557,6 +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 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 _ =>
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
Expand Down
5 changes: 5 additions & 0 deletions Compiler/Proofs/IRGeneration/Contract.lean
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 39 additions & 0 deletions Compiler/Proofs/IRGeneration/FunctionBody.lean
Original file line number Diff line number Diff line change
Expand Up @@ -7275,6 +7275,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 =>
Expand Down Expand Up @@ -7492,6 +7509,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)}
Expand Down
Loading
Loading