Skip to content

feat: structured-to-unstructured forward soundness for simple-shape programs (small-step)#1348

Draft
PROgram52bc wants to merge 35 commits into
htd/structured-to-unstructured-small-step-infrafrom
htd/structured-to-unstructured-small-step-proof
Draft

feat: structured-to-unstructured forward soundness for simple-shape programs (small-step)#1348
PROgram52bc wants to merge 35 commits into
htd/structured-to-unstructured-small-step-infrafrom
htd/structured-to-unstructured-small-step-proof

Conversation

@PROgram52bc

Copy link
Copy Markdown
Contributor

Summary

Stacked on top of #1347 (htd/structured-to-unstructured-small-step-infra). Adds structuredToUnstructured_sound (axiom-free, sorry-free) for programs satisfying Block.simpleShape (no nondeterministic ites, no loops of any kind), in Strata/Transform/StructuredToUnstructuredCorrect.lean (7,331 LoC).

Small-step variant of the proof originally landed in htd/structured-to-unstructured-simple-on-infra. The block-at-a-time EvalDetBlock relation is gone from CFGSemantics.lean; the simulation is now driven by the per-command StepCFG with three configuration shapes (.atBlock / .inBlock / .terminal) and five constructors. The proof file adapts to that shape via three run_block_* helpers (atBlock entry, inBlock prefix, terminal exit) plus an EvalCmds_to_StepCFG_chain bridge that lifts a structured EvalCmds derivation to a StepCFGStar trace through one block.

Diff vs the original block-at-a-time proof file: 181 insertions, 196 deletions (net −15 LoC). The kernel is cleaner — five focused StepCFG constructors instead of one monolithic eval_next + EvalDetBlock — and at call sites the run_block_* helpers read more directly than the prior EvalDetBlock + updateFailure incantation. As predicted by @aqjune-aws on #1196 (comment), "the size of proofs won't actually change very much" held empirically: per-lemma savings (10–14 LoC each on the four flushCmds_* lemmas + end_block_terminal) were largely absorbed by the new helpers (~85 LoC) + bridge (~30 LoC). The architectural win — .inBlock partial-residual state available for any future per-command reasoning — is a forward-looking benefit, not paid back on the existing simulation lemmas alone.

Test plan

  • lake build green (488 jobs)
  • Strata/Transform/StructuredToUnstructuredCorrect.lean is sorry/axiom-free
  • No new sorry or axiom vs the infra base
  • structuredToUnstructured_sound proved against the small-step CoreCFGStepStar (which now wraps the per-command StepCFG instead of EvalDetBlock)

@PROgram52bc PROgram52bc force-pushed the htd/structured-to-unstructured-small-step-infra branch 2 times, most recently from e672c67 to fae1183 Compare June 8, 2026 20:29
…rograms

Adds structuredToUnstructured_sound (axiom-free, sorry-free) for programs
satisfying Block.simpleShape (no nondeterministic ites, no loops of any kind),
built on top of htd/small-step-infra.

Main file: Strata/Transform/StructuredToUnstructuredCorrect.lean (7,331 LoC).

Small-step variant of the proof originally landed in
origin/htd/structured-to-unstructured-simple-on-infra. The block-at-a-time
EvalDetBlock relation is gone from CFGSemantics.lean; the simulation is
now driven by the per-command StepCFG with three configuration shapes
(.atBlock / .inBlock / .terminal) and five constructors. The proof file
adapts to that shape via three run_block_* helpers (atBlock entry, inBlock
prefix, terminal exit) plus an EvalCmds_to_StepCFG_chain bridge that lifts
a structured EvalCmds derivation to a StepCFGStar trace through one block.
Diff vs the original proof file: 181 insertions, 196 deletions.

Foundational additions on the infra branch (unchanged from the original
proof's prerequisite list, all already present in htd/small-step-infra):
- LawfulHasFvar / LawfulHasBool / LawfulHasIdent / LawfulHasIntOrder /
  LawfulHasNot instances for Core.Expression in StatementSemantics.lean
- @[expose] on DetTransferCmd.goto, ExprOrNondet.getVars, updateFailure,
  StepCFGStar, flushCmds, stmtsToBlocks, stmtsToCFGM, stmtsToCFG
- HasVarsPure typeclass and WellFormedSemanticEvalExprCongr premise threaded
  through StepCFG's conditional-goto constructors (replacing the
  EvalDetBlock-level premises from the original proof)
- synthesizedMd promoted from private to public abbrev
- flushCmds rewritten to materialize blocks for explicit transfers (so
  condGoto is emitted even when accum is empty); stmtsToBlocks block-rest
  arm uses accumEntry as the new entry (test goldens updated to match)
- DetTransferCmd.goto default md := .empty

Translator output change: synthesized-provenance metadata is suppressed
on the auxiliary condGoto blocks emitted by flushCmds; goldens in
StrataTest/Languages/Core/Examples/{Exit,Loops}.lean were updated on the
infra branch.

Sorry/axiom count: 0/0 in the proof file. Builds against the infra branch's
488 jobs; tests green (modulo the pre-existing ion-java jar issue in
Languages.Java.TestGen).
@PROgram52bc PROgram52bc force-pushed the htd/structured-to-unstructured-small-step-proof branch from 806b69f to c81249b Compare June 8, 2026 20:33
Adds three new predicates to Strata/DL/Imperative/Stmt.lean and weakens
simpleShape so that .loop is permitted when its body has simpleShape:

- Block.loopBodyNoInits      — body's initVars must be empty
- Block.loopHasNoInvariants  — invariants list must be empty
- Block.noMeasureLoops       — measure must be .none

Threads the three preconditions through the simulation theorems
(stmtsToBlocks_simulation, stmtsToBlocks_simulation_to_cont, top-level
structuredToUnstructured_sound). Adds a LoopArm namespace with framework
helpers carrying real signatures.

Build green: 489/489 jobs.
Sorries: 8, all at named lemma boundaries (2 top-level loop arms +
6 LoopArm framework helpers).
Axioms: 0.

This commit captures the skeleton from workflow wf_c2c8cd66-3b1, which
died at the auth boundary after writing the file but before launching
closure waves. Closure waves pick up from this commit.
Paused workflow wf_174290ca-040 (task wxh3di8mi) during the Implement
phase. State is intentionally incomplete but builds green.

Done:
- simpleShape strengthened to det-only loops (Stmt.lean)
- LoopArm namespace deleted; helpers being ported inline
- loop_det_decompose_h_gen monadic-chain decomposition mostly wired

Remaining (4 sorries, 0 axioms, build green):
- L4243/4244: gen-threading equalities in the decompose helper
  (`gen_kn = gen_r`; the skeleton's fictional "kNext$" gen step needs removal)
- L4968: terminal .loop arm — iteration-induction infra (decompose +
  peel_off_one_iteration + loop_iterations_det) ported to small-step
- L7342: _to_cont .loop arm — same iteration-induction gap, exit variant

Resume by relaunching the v3 workflow with resumeFromRunId so cached
Setup/Understand return instantly and Implement picks up from here.
…sT/blockT terminal+exiting) + import all Relations; 2 sorries remain
…/_to_cont + lentry_condGoto helpers (small-step); 2 .loop arm sorries remain
…FG_exiting + stmtsToBlocks_simulation_exiting
…-body,isCmd} — delete 3 unused Stmt.lean members
… one conjunction lemma fresh_inits_after_step (15 call sites)
CI builds PR #1348 as a merge into its base htd/structured-to-unstructured-small-step-infra.
The base removed the default `(md : MetaData P := .empty)` on DetTransferCmd.goto
and updated flushCmds to pass `.goto k .empty` explicitly. This proof branch
had stale copies that re-added the default and reverted flushCmds to bare
`.goto k`, which built locally (default present) but fails in the CI merge
(infra's no-default goto wins, leaving bare `.goto k` call sites ill-typed).

Align with the infra base:
- BasicBlock.lean: drop the `:= .empty` default on DetTransferCmd.goto
- StructuredToUnstructured.lean: flushCmds emits `.goto k .empty`
- StructuredToUnstructuredCorrect.lean: 2 flushCmds-lemma sites now write
  `DetTransferCmd.goto k .empty` explicitly

Both infra-owned files now match origin/htd/structured-to-unstructured-small-step-infra
exactly (zero diff). Clean full build green (489 jobs, StructuredToUnstructuredCorrect
rebuilt from scratch in 308s); 0 sorries, 0 axioms; axiom footprint
[propext, Classical.choice, Quot.sound] preserved.
…r_pre} — drop 3 unused binders + their positional args at 2 call sites
…ffix thread

The _get NoGenSuffix carry was self-referential: every derivative
(h_{then,else,rest,body}_no_gen_suffix_get) flowed only into another
recursive/sibling/helper call's _get slot, and the real freshness
consumers use the definedVars variant. Removed the binder from both
mutual lemmas (stmtsToBlocks_simulation, _to_cont), the param + return
conjunct from both arm helpers (cmd_arm_combined_lemmas,
typeDecl_arm_combined_lemmas), and the two top-level callers
(stmtsToCFG_terminal, structuredToUnstructured_sound), plus the now-dead
h_getvars_eq rebracketing helpers.
…ch-shape projections

Three build-verified consolidations in StructuredToUnstructuredCorrect.lean, net -69 LoC (8673 -> 8604), sorry-free, full build green.

- flushCmds_condGoto_{true,false}_agree -> flushCmds_condGoto_agree (b : Bool):
  shared simp/injection/subst/eval-congruence prefix proved once, then cases b
  dispatches to run_block_goto_true/false; conclusion target (if b then tl else fl).
  6 call sites pass true/false.
- lentry_condGoto_{true,false} -> lentry_condGoto (b : Bool): same pattern,
  target (if b then bl else kNext); 3 call sites.
- New ite_branch_shape helper bundles the 8 then/else projections (simpleShape /
  loopBodyNoInits / loopHasNoInvariants / noMeasureLoops) from the 4 head facts;
  replaces inline have-blocks at both .ite arms of the mutual simulation lemmas.
…heval_eq_c rewrites to ▸

Two build-verified cleanups in StructuredToUnstructuredCorrect.lean, net -16 LoC
(8604 -> 8588), sorry-free, full build green.

- New private invMapM_genStep: the GenStep obligation for the invariant-assert
  is.mapM was re-proved 4x across the .loop arm's none/some x det/nondet branches
  (twice in stmtsToBlocks_genStep, twice in stmtsToBlocks_invariant). It is a
  fully general fact (quantified over is/gen_b/gen_i/invCmds, built on mapM_genStep,
  no gen-state threading), so it unified at all 4 sites cleanly. -14 LoC.
- Collapsed the remaining `by rw [heval_eq_c]; exact h` WF-lift sites to term-mode
  `heval_eq_c ▸ h`. -2 LoC.

Deeper .loop-arm factoring (the GenInv.trans chains and lentry-freshness blocks)
was surveyed and deliberately NOT extracted: those branches share shape but thread
distinct gen-state names and carry branch-specific commands, so a parameterized
helper pays back what hoisting saves. Confirmed Delta-approx-0 for that region.
PROgram52bc and others added 9 commits June 11, 2026 16:40
…imports + vestigial attributes

Build-verified compiler-directive cleanup in StructuredToUnstructuredCorrect.lean,
-6 lines, build green, no new warnings, no build-time regression.

Kept (8 probes, each individually build-gated):
- maxRecDepth 4096 -> 1024 (still 2x the 512 default; the mutual block needs above-default but not 8x).
- Removed 2 `set_option linter.unusedVariables false` suppressions (on loop_iterations_det / _to_cont_det) — the earlier dead-binder cleanup made them vestigial; no unused-variable warning resurfaces.
- import all Strata.DL.Imperative.BasicBlock -> plain import (the `all` body-exposure was unused).
- Dropped public import Strata.DL.Imperative.CmdSemanticsProps (no referenced names).
- Dropped public import Strata.Transform.Specification (SpecificationProps re-exports the namespace transitively).
- Removed vestigial @[simp] on the StepDetCFGStar abbrev and @[expose] on the NoGenSuffix abbrev (abbrevs are already reducible).

Confirmed load-bearing and left in place (probe went RED):
- maxHeartbeats 12800000 — genuinely required; RED at both 400000 and 1600000 (whnf timeout, multiple independent hotspots), not vestigial.
- import all Strata.DL.Util.Relations — needed for ReflTransT.len exposed-body simp lemmas.
- import Strata.Transform.SpecificationProps — the `open ... Specification` depends on it.
- @[expose] on transformStmtModVars / transformBlockModVars / Block.userBlockLabels / Block.userLabelsDisjoint — in-file rfl/unfold/show proofs rely on the exposed bodies.
## Summary

- Periodic merge of `main` into `main2` to keep main2 up to date with
core improvements
- 46 new commits from main since PR #1346 (mostly repo-removal PRs:
#1339, #1351, #1343, #1329, #1334, plus code changes)
- All conflicts resolved by keeping main2's versions (sub-repos stay
local, no module migration, preserves main2-only features like
Procedure.Body sum type, transparent procs, array axiomatization)
- Fixed duplicate StrataDDM git entries that auto-merged into
`docs/api/lake-manifest.json` and `docs/verso/lake-manifest.json`

---------

Co-authored-by: Aaron Tomb <aarotomb@amazon.com>
Co-authored-by: Michael Tautschnig <mt@debian.org>
Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Co-authored-by: Juneyoung Lee <136006969+aqjune-aws@users.noreply.github.com>
Co-authored-by: keyboardDrummer-bot <keyboardDrummer-bot@users.noreply.github.com>
Co-authored-by: Mikaël Mayer <MikaelMayer@users.noreply.github.com>
Co-authored-by: thanhnguyen-aws <ntson@amazon.com>
Co-authored-by: Fabio Madge <fmadge@amazon.com>
Co-authored-by: Joe Hendrix <joehx@amazon.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: June Lee <lebjuney@amazon.com>
Co-authored-by: David Deng <daviddenghaotian@gmail.com>
Co-authored-by: David Deng <htd@amazon.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Mikael Mayer <mimayere@amazon.com>
Co-authored-by: Remy Willems <rwillems@amazon.com>
Co-authored-by: keyboardDrummer-bot <keyboarddrummer.bot@gmail.com>
Co-authored-by: Sagar Joshi <72283186+sagjoshi@users.noreply.github.com>
…nstructuredCorrect

HasNot->HasBoolOps, HasVarsPure(P.Expr)->HasFvars, HasBoolVal dropped (folded
into HasBool), HasIntOrder->HasInt+HasIntOps; op-calls HasNot.not->HasBoolOps.not,
HasVarsPure.getVars->HasFvars.getFvars, HasIntOrder.{eq,lt,zero,intTy}->HasIntOps/HasInt.
Binder ordering: HasFvars before HasInt (HasInt prereq); dropped redundant explicit
[HasVal P] where HasBool/HasBoolOps provide it (resolved HasVal instance-diamond);
added [HasOps P] per migrated hierarchy.

All binder/synthesize/invalid-binder errors cleared. REMAINING (24, NOT mechanical):
semantic changes flagged by the reconcile spec for owner input — 4-arg Config.block
(.block now takes e_parent eval snapshot), WellFormedSemanticEvalVal instance-path
mismatch from HasBool-extends-HasVal restructuring, moved SemanticEval.stmts constant.
Sorry-free preserved (0).
…for e_parent projection; add block_none inversion helpers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants