Skip to content

feat(formal): real-lift R2 — structured conditionals (fuel-indexed wexec)#664

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/lucid-cray-4a22dp
Jun 27, 2026
Merged

feat(formal): real-lift R2 — structured conditionals (fuel-indexed wexec)#664
hyperpolymath merged 1 commit into
mainfrom
claude/lucid-cray-4a22dp

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Third rung of the real lift (formal/REAL-LIFT.adoc). Adds structured control to the real target IR + source core, and shows the compiler-correctness simulation still holds across a conditional. Axiom-free throughout.

What landed

Target IR (formal/RealWasm.v)

  • New instruction IfElse (thn els : list instr)lib/wasm.ml's If minus the block_type validation annotation.
  • Because instr now nests list instr, a structural wexec is rejected by Coq's guard checker ("cannot guess decreasing argument" — the recursive call descends into a branch sub-list, not the tail). So wexec becomes fuel-indexed (decreasing on a nat), exactly as REAL-LIFT.adoc anticipated for R2. Fuel keeps definitional computation, so cbn / reflexivity / the demos still evaluate.
  • Fuel breaks the old one-line wexec_seq (per-cons decrement ⇒ wexec (is1++is2) ≠ wexec is2 ∘ wexec is1). Recovered with:
    • wexec_le_S / wexec_mono — more fuel never loses a Some result;
    • wexec_app_some — additive sequencing: run is1 with f1, then is2 with f2, get is1++is2 with f1+f2 (the fuel-world replacement for wexec_seq);
    • wexec_S_cons / wexec_ifelse — one-step unfold lemmas (by reflexivity) used via rewrite so cbn doesn't over-reduce the IfElse branch run.

Source core (formal/RealCompile.v)

  • New RIf (c thn els : rexpr); eval branches on Z.eqb vc 0; compile emits compile c ++ [IfElse (compile thn) (compile els)].
  • compile_correct restated existential-in-fuel:
    exists fuel locals', wexec fuel (compile d e) locals st = Some (locals', v :: st) /\ …
    (env↔locals agreement + low-slot preservation unchanged). The RIf case opens with change (compile d (RIf …)) with (… ++ [IfElse …]) to expose the ++ while keeping inner compiles named, then discharges via wexec_app_some + wexec_le_S.
  • wexec_sound retired in this fuel refactor — it was a convenience soundness restatement, not load-bearing for compile_correct.

Verification

coqc 8.18, whole formal/ track re-audited after rebasing onto current main (incl. #648's now-wired Rows.v):

  • 18 files compile, 31 Print Assumptions reports, every one "Closed under the global context"zero axioms, no Admitted.

Docs

formal/REAL-LIFT.adoc (R2 row + nested-control section), formal/README.adoc (RealWasm/RealCompile rows + "The real lift (R0 → R2)"), docs/PROOF-NEEDS.adoc (K-1 row) updated. Conflict-resolved cleanly against #648 (Rows.v / P-11) and #658 (licence normalisation).

Next

Sub-rung R2-loops (Loop/Br + while/for, reusing the fuel device; settles the value-returning-tail question behind #601), then R-mem / R-float / R-str / … per formal/REAL-LIFT.adoc.

Refs REAL-LIFT.adoc, docs/PROOF-NEEDS.adoc (K-1 row).

🤖 Generated with Claude Code

https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr


Generated by Claude Code

…xec)

Third rung of the real lift (formal/REAL-LIFT.adoc). Adds structured
control to the real target IR and source core, and proves the
compiler-correctness simulation still holds across a conditional.

Target IR (RealWasm.v):
  - new instr `IfElse (thn els : list instr)` = lib/wasm.ml's `If` minus
    the block_type validation annotation.
  - because `instr` now nests `list instr`, a *structural* `wexec` is
    rejected by Coq's guard checker ("cannot guess decreasing argument";
    the recursive call descends into a branch sub-list, not the tail).
    So `wexec` becomes **fuel-indexed** (decreasing on a `nat`), exactly as
    REAL-LIFT.adoc anticipated for R2. Fuel keeps definitional computation,
    so `cbn`/`reflexivity`/the demos still evaluate.
  - fuel breaks the old one-line `wexec_seq` (per-cons decrement means
    `wexec (is1++is2) != wexec is2 . wexec is1`). Recovered with:
      * wexec_le_S / wexec_mono — more fuel never loses a Some result;
      * wexec_app_some — additive sequencing: f1 on is1 then f2 on is2
        gives f1+f2 on is1++is2 (the fuel-world replacement for wexec_seq);
      * wexec_S_cons / wexec_ifelse — one-step unfold lemmas (by
        reflexivity) used via rewrite to stop cbn over-reducing the
        IfElse branch run.

Source core (RealCompile.v):
  - new `RIf (c thn els : rexpr)`; eval branches on Z.eqb vc 0; compile
    emits `compile c ++ [IfElse (compile thn) (compile els)]`.
  - compile_correct restated **existential-in-fuel**:
      exists fuel locals', wexec fuel (compile d e) locals st
                           = Some (locals', v :: st) /\ ...
    (env<->locals agreement + low-slot preservation unchanged). The RIf
    case opens with `change (compile d (RIf ...)) with (... ++ [IfElse ...])`
    to expose the `++` while keeping the inner compiles named, then
    discharges via wexec_app_some + wexec_le_S.
  - wexec_sound retired in this fuel refactor — it was a convenience
    soundness restatement, not load-bearing for compile_correct.

Whole formal/ track re-audited: zero axioms, no Admitted
(`Print Assumptions` closed under the global context for every closure).

Next sub-rung R2-loops (Loop/Br + while/for, reusing the fuel device;
settles the value-returning-tail question behind #601).

Refs REAL-LIFT.adoc, docs/PROOF-NEEDS.adoc (K-1 row).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr
@hyperpolymath hyperpolymath marked this pull request as ready for review June 27, 2026 18:43
@hyperpolymath hyperpolymath merged commit bfa3c70 into main Jun 27, 2026
15 of 16 checks passed
hyperpolymath added a commit that referenced this pull request Jun 27, 2026
…ile simulation (settles #601) (#665)

Fourth rung of the real lift (`formal/REAL-LIFT.adoc`): **structured
control with backward jumps**, and a source `while` whose lowering is
proved correct — settling the **#601** value-returning-tail question
concretely. Axiom-free throughout.

> Branch note: pushed to `claude/lucid-cray-4a22dp-r2loops` rather than
`claude/lucid-cray-4a22dp` — the latter is still present at the merged
#664 tip and is now protected against direct pushes (force/ff/delete all
blocked), so a fresh branch was the only push path. Diff is against
current `main` (post-#664), so it shows **only** the R2-loops changes.

## What landed

**Target IR (`RealWasm.v`)**
- Adds the faithful `lib/wasm.ml` structured-control instructions
`Block`, `Loop`, `Br k`, `BrIf k` to `instr` (+ dead `step1` arms).
**Zero blast radius** — the proven `wexec` and
`RealCompile.compile_correct` still compile unchanged.

**R2-loops (new file `RealLoop.v`)**
- The R2 executor `wexec : … → option (locals×stack)` can't express
*"this code branched to label k"*, so it cannot run `Br`. R2-loops
introduces a **branch-aware** executor `cexec` returning an `outcome`
(`ONormal` / `OBranch k` / `OTrap`). It reuses the *same fuel device* R2
used for forward control: a `Loop` consumed by `Br 0` re-enters by
recursing with decremented fuel, so `cexec` stays a structural
`Fixpoint` — definitional, so `cbn`/`reflexivity`/demos still compute (a
real countdown `while` evaluates by `reflexivity`).
- `cexec_le_S` / `cexec_mono` (more fuel never changes a `Some`) +
`cexec_app_normal` (additive sequencing when the prefix ends `Normal`).
- A *mutual* `instr`/`list instr` `branchfree` predicate is **rejected
by Coq's guard checker** (list recursion only decreases on the spine),
so instead of a `wexec→cexec` bridge over a flag, the R2 expression
compiler is re-proved directly into `cexec`: **`cexec_compile`** (the
`cexec` analogue of `compile_correct`).
- Source statements `SSkip` / `SSeq` / `SSet` / `SWhile` with a
fuel-indexed reference `run_stmt` (+ `run_stmt_mono`,
`run_stmt_length`), and a lowering `compile_stmt` taking `while` to
`Block[Loop[<cond>; I32Eqz; BrIf 1; <body>; Br 0]]`.
- **`cexec_loop`** — the loop-simulation lemma (induction on the while's
fuel: cond-false unwinds with `OBranch 1`, caught by the `Block`;
cond-true runs the body and re-enters via `OBranch 0`).
- **`compile_stmt_correct`** — a terminating statement's lowering runs
to the same locals, leaving the value stack **empty** (`cexec … = Some
(ONormal lo' [])`). A `while` is a *statement* (unit tail); this is
**#601 settled concretely** — the expression tail would instead keep its
value on the stack.

## Verification

`coqc` 8.18, whole `formal/` track re-audited against current `main`:

- **19 files compile**, **33 `Print Assumptions` reports**, every one
*"Closed under the global context"* — **zero axioms, no `Admitted`**.

## Docs

`formal/REAL-LIFT.adoc` (R2 row + §5 hard-parts + §8 status),
`formal/README.adoc` (RealLoop row + "real lift R0 → R2-loops"),
`docs/PROOF-NEEDS.adoc` (counts 18→19 / 31→33, K-1 row). `_CoqProject` /
`justfile` / `.hypatia-ignore` wire `RealLoop.v` in.

## Next

Rung **R-mem** (linear memory: load/store, tuples/arrays/records).
`for`/`break`/`continue` and a general source-divergence model reuse
this `cexec`.

Refs `REAL-LIFT.adoc`, `docs/PROOF-NEEDS.adoc` (K-1 row), #601.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr

---
_Generated by [Claude
Code](https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr)_

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants