Skip to content

feat: full static-call-context support in compiler correctness - #39

Merged
leonardoalt merged 4 commits into
mainfrom
fix/compiler-static-support
Jul 16, 2026
Merged

feat: full static-call-context support in compiler correctness#39
leonardoalt merged 4 commits into
mainfrom
fix/compiler-static-support

Conversation

@leonardoalt

Copy link
Copy Markdown
Member

Extends compile_correct (and the object/eval variants) to cover static (STATICCALL) frames, closing the audit's finding #2 (the theorem previously required FrameOK.perm = permitStateMutation = true, i.e. non-static frames only).

Stacked on powdr-labs/yul-semantics#37 (adds HaltKind.staticViolation so static writes match the EVM's StaticModeViolation instead of being conflated with INVALID). The lakefile pins that branch commit; update the pin to the merged SHA before merging this.

Increment 1 (this commit — landed, green)

  • Bump yul-semantics pin to the staticViolation commit.
  • Map HaltKind.staticViolation → EVM Exception .StaticModeViolation in HaltMatch/resultOf and the compile_correct_eval halt analysis.
  • Compiler builds green (8663 jobs); Checks.lean axiom guard unchanged (3 standard axioms).

Increment 2 (in progress)

  • Remove FrameOK.perm; derive permitStateMutation per-op from StateMatch and the source's static branch.
  • Add the static → StaticModeViolation halt branch for each state-modifying op (sstore/tstore/log0log4/selfdestruct, value-call/callcode, create/create2).
  • Generalize compile_correct/compile_correct_withPayload/compile_correct_eval/compileObject_correct to both static parities.
  • Update README/PLAN to drop the non-static caveat.

🤖 Generated with Claude Code

leonardoalt and others added 4 commits July 16, 2026 13:14
Bump the yul-semantics pin to the HaltKind.staticViolation commit and handle the
new halt kind in HaltMatch / resultOf (→ EVM Exception .StaticModeViolation) and
the compile_correct_eval halt-kind analysis. Keeps the compiler building and
axiom-clean against the updated source semantics; static-frame correctness proofs
follow in the next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FrameOK no longer fixes permitStateMutation = true, so the correctness
theorems now cover static (STATICCALL) frames in addition to ordinary
ones. In a static frame every state-modifying built-in the source forbids
halts with Exception .StaticModeViolation, matching the source's
HaltKind.staticViolation.

Local guarded ops (sstore, tstore, log0-log4, selfdestruct): opStep now
cases on yst.env.static. The non-static branch reuses the existing normal
step (deriving permitStateMutation = true from EnvMatch.static); the
static branch fires the target's generic StepRunning.staticModeViolation
gate via the new staticViolationStep / staticHaltStepGen helpers.

Open-world ops: externalStaticHaltStep discharges the external static-halt
branch of ahalt_sim. Value-bearing call / create / create2 halt via the
dedicated StepRunning.callStatic / createStatic / create2Static rules;
delegatecall / staticcall / gas never produce a relational halt.

CALLCODE is the one genuine source/target mismatch: the source halts a
value-bearing CALLCODE in a static frame with .staticViolation, but the
target EVM semantics deliberately has no callcodeStatic rule (a
self-transfer is a world-state no-op). FrameOK.perm is therefore weakened
to `permitStateMutation = true ∨ CALLCODE byte ∉ code` — free (Or.inl rfl)
for every ordinary frame, and only constraining a static frame to be
CALLCODE-free. This strictly generalises the previous non-static-only
theorem; ordinary-frame CALLCODE is unaffected.

Theorem statements (compile_correct, compile_correct_withPayload,
compile_correct_eval, compileObject_correct) are unchanged and still pin
axioms [propext, Classical.choice, Quot.sound].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upstream yul-semantics (db5e072) no longer rejects a value-bearing
CALLCODE in a static frame — the self-transfer is a world-state no-op, so
it now matches EIP-214 / evm-semantics (which has no callcodeStatic rule).
With the source/target mismatch gone, static CALLCODE simply proceeds like
delegatecall/staticcall and never produces a relational halt.

This removes the temporary carve-out entirely:
- Bump yul-semantics rev to db5e072d4e5067b5f4f0d9833a811e71da984f57.
- Drop the `perm` field from FrameOK (it no longer constrains
  permitStateMutation at all), and delete the `hf.perm` slot from the 36
  tuple reconstructions in OpStep.
- Route static CALLCODE through externalStaticHaltStep's never-halts group
  (contradiction from the impossible halt), alongside delegatecall/
  staticcall/gas, instead of the FrameOK byte-exclusion.

FrameOK now says nothing about permitStateMutation, so compile_correct and
friends cover ordinary and static frames uniformly with no exception. Every
built-in the source halts under static (sstore/tstore/log0-4/selfdestruct,
value-call, create/create2) maps to a matching evm-semantics
StaticModeViolation rule.

Theorem statements unchanged; axioms stay [propext, Classical.choice,
Quot.sound].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Repoint from the PR #37 branch commit to the merged yul-semantics main
(0b1a2c0), which contains HaltKind.staticViolation and the CALLCODE
static-frame correction. Build green (8663 jobs); Checks.lean axiom guard
unchanged ([propext, Classical.choice, Quot.sound]).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leonardoalt
leonardoalt marked this pull request as ready for review July 16, 2026 12:36
@leonardoalt
leonardoalt merged commit d9115f9 into main Jul 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant