Skip to content

CallElimCorrect: lift _terminal to polymorphic {f : Bool}#1340

Merged
PROgram52bc merged 31 commits into
htd/callelim-smallstep-on-main2from
htd/polymorphic-f-callelim
Jun 8, 2026
Merged

CallElimCorrect: lift _terminal to polymorphic {f : Bool}#1340
PROgram52bc merged 31 commits into
htd/callelim-smallstep-on-main2from
htd/polymorphic-f-callelim

Conversation

@PROgram52bc

Copy link
Copy Markdown
Contributor

Summary

Lifts callElimStatementCorrect_terminal from the parent branch's f = false → false shape (PR #1306, success-only refinement) to fully polymorphic {f : Bool}: a source trace ending in failure maps to a target trace ending in failure, and a source success trace maps to a target success trace, in one statement. Downstream consumers no longer case-split on the failure flag at the call site.

This is a foundational refactor + generalization, not a feature. Reviewers should follow the proof structure: Stages 1-3 are pure extractions (success theorem unchanged), Path 1 narrows a public iff for semantic alignment, Stage 6 (split into a scaffold and a fill-in) lands the actual polymorphic-f result.

Base branch

This PR sits on top of htd/callelim-smallstep-on-main2 (PR #1306, currently open). Merge into that branch, or rebase onto main once #1306 lands.

Why this matters

The parent's success-only theorem suffices to argue that call-elimination preserves the absence of bugs, but it leaves a dual gap: it does not say anything about preserving the presence of bugs. Without the failure arm, callElim could in principle turn a buggy program into one that verifies. Lifting _terminal to {f : Bool} makes that argument compositional — the same lemma covers both directions, and any future transform layered on top of call-elimination inherits the polymorphic guarantee for free.

The failure arm also exercises corners of the contract semantics the success arm never touches: the assert-zip on call-site preconditions, havoc of out-parameters in the failure branch, and the assume-zip on call-site postconditions all have to compose correctly under hasFailure = true. Closing it flushed out a real semantic mismatch in EvalCommandContract.call_sem (Path 1 below) that was invisible from the success-only side.

Commit-by-commit walkthrough

Read the seven commits in order:

1. 193dee2 — WIP: polymorphic-f infrastructure (no live consumers)

Forward-looking scaffolding, kept as a separate commit so that the consumer-side proof changes in later commits aren't buried under unrelated infrastructure. Lands in:

  • Strata/Languages/Core/StatementSemantics.leanChange A: collapses the two conditional Hupdate premises of EvalCommandContract.call_sem into a single unconditional UpdateStates premise. The failure path no longer pins σ' = σ, which is what makes a polymorphic-f rule shape coherent.
  • Strata/Languages/Core/WF.leanWFPrePostProp.boolTyped clause added (zero migration cost, no live construction sites yet).
  • Strata/Transform/CoreTransformProps.lean and Strata/Transform/SubstProps.lean — new helpers singleCmdToStmts_poly, singletonAssumeEval_poly, H_havocs_poly, H_check_block_zip_poly, H_assumes_zip_poly, singletonAssertFailEval, singletonAssertEval_poly, plus the substantive new walker H_asserts_zip_fail (L4 flag-flip from false to true via a failing-pre witness) and EvalCallElim_glue_fail (composes L1-L6 with the L4 flip).

Build clean, sorry-free, axiom-free at this commit.

2. 580ef2d — Stage 1: extract HoldEval_bridge_at_σO

Pure refactor. Lifts the per-index δ-eval bridge for mkOld old-variable fvars at the post-havoc store σO out of callElimStatementCorrect_terminal's call arm into a private theorem with 11 explicit hypotheses. Net diff in Strata/Transform/CallElimCorrect.lean: +114 / -61. Same proof body as the inline version; the signature overhead is intentional because Stage 6 will share this helper between the success and failure arms. Sorry count and axiom set unchanged.

3. 5406b08 — Stage 2a: extract HoldSubBridge_at_σO

Pure refactor in Strata/Transform/CallElimCorrect.lean. Lifts the per-fvar bridge for createOldVarsSubst's codomain at the L6 intermediate stores σ_R1 / σO into a private theorem with 6 hypotheses; the inline block becomes a one-line call. Net diff: +82 / -29. Sorry count and axiom set unchanged.

HinputSubBridge (the larger sibling, originally targeted for Stage 4) is deferred, then ultimately made redundant by the flag-generalization approach (see "Stages 4 & 5: skipped" below).

4. cf10129 — Stage 3: extract b1/b2_var_witness_at_oldSubst

Pure refactor in Strata/Transform/CallElimCorrect.lean. Lifts the two class-(b) substitution-decomposition witnesses out of the call arm:

  • b1_var_witness_at_oldSubst: when a Map.find? lands in the createOldVarsSubst segment of oldSubst_L6, the codomain entry is a fresh-old createFvar gen and the witness var ∈ getVars w forces var = genOldIdents[i].
  • b2_var_witness_at_oldSubst: when the lookup misses createOldVarsSubst and hits callElim_inputOnlyOldSubst, the codomain is a positional inArgs element.

Both helpers take proc' and args directly so callElim_inputOnlyOldSubst can be referenced by name (needed for find?_append_none_elim unification at the consumer). 4 callsites each in _terminal (inv + post variants); Stage 6's failure arm reuses the same callsites with identical arguments. Net diff: +123 / -77.

Stages 4 & 5: skipped

Originally planned to extract HinputSubBridge and a final shared-plumbing block. The flag-generalization approach made these unnecessary — the call branch in _terminal cases on f, and the false arm reuses its existing 2200-LoC body verbatim, so we never needed shared sub-helpers between the two arms. Keeping the inline block on the false side was strictly cheaper than extracting it.

5. fdf2fc4 — Path 1: narrow EvalCommandContract.call_sem iff to non-Free preconditions

This is the semantic alignment that makes the failure arm provable. callElimCmd only emits asserts for non-Free preconditions (Free requires are assumed by the implementation, not checked at call sites). The previous call_sem iff ranged over all preconditions, which made the polymorphic-f failure arm unattainable: a witness pre with eval ≠ tt at σAO could fall in the Free segment, giving no L4 entry to flip the flag.

That mismatch was harmless at f = false (the success arm never relies on the iff direction that exposed it) but a hard blocker on the failure arm. Restricting the iff to non-Free preconditions aligned the two semantics without changing any existing proof:

  • Strata/Languages/Core/StatementSemantics.lean: call_sem rule's Hpre_def and Hpre_iff now range over getCheckExprs (preconditions.filter (·.attr ≠ .Free)).
  • Strata/Languages/Core/StatementSemanticsProps.lean: EvalCallBodyRefinesContract / EvalCommandRefinesContract / StepStmt[Star]_refines_contract / EvalStatement[s]RefinesContract gain [LawfulBEq Expression.Expr] instances (needed for the contains↔mem bridge in the new presFiltered_subset derivation that powers the rule constructor).
  • Strata/Transform/CallElimCorrect.lean: success arm's Hpre_evalTt / Hpre re-synthesis ranges over filtered preconditions; HpreFiltered no longer needs filterCheck_in_getCheckExprs — direct membership in presFiltered transports straight to the filtered contains-form. Failure-arm helper signature updated to take Hpre_def / Hpre_iff at the new filtered shape.

Build clean (490 jobs) with one expected sorry at line 1654 (failure-arm body, filled by the next commit).

6. 45db77d — Stage 6 scaffold: lift _terminal to {f : Bool}, delegate failure arm

Shape change without filling in the body. In Strata/Transform/CallElimCorrect.lean, callElimStatementCorrect_terminal's signature lifts from f = false → false to {f : Bool}, with Heval and the conclusion both at flag f. Non-call cases close polymorphically via nc_close (Heval passes through unchanged for identity statements). The call branch dispatches:

cases f with
| false => existing 2200-LoC body verbatim (f=false case)
| true  => exact callElimStatementCorrect_terminal_call_arm_fail ...

The _terminal_call_arm_fail helper is declared with full signature (~25 hypotheses from the cases Hcc destructure) and a sorry body, ready for the final commit. Splitting the signature lift from the proof close means the lifted public signature is reviewable and buildable on its own, and the failure-arm proof body lands against an already-stable interface. Sorry count: 18 → 19 (temporary).

7. be35c7f — Stage 6: close polymorphic-f failure arm + lift public signature

Replaces the sorry-stubbed callElimStatementCorrect_terminal_call_arm_fail in Strata/Transform/CallElimCorrect.lean with the full proof body (~2,148 LoC) that mirrors the success arm's L1-L3 + L5/L6 plumbing while flag-flipping at L4:

  • L1, L2, L3: identical to success (init segments produce f = false → false).
  • L4 (asserts): H_asserts_zip_fail with failing-pre witness.
  • L5 (havocs): H_havocs_poly at f := true.
  • L6 (assumes): H_assumes_zip_poly at f := true.
  • Glue: EvalCallElim_glue_fail composing false → false → false → false → true.

Failing-pre witness derivation (now unblocked by Path 1):

  1. Hpre_iff at flag = true: Classical.em on "all preconditions eval to tt" → contrapositive gives ∃ pre ∈ presFiltered, δ σAO pre ≠ some tt.
  2. Bool-totality from new WFCallSiteProp.preBoolTyped field (mirrors the boolTyped clause on WFPrePostProp): δ σAO pre = some tt ∨ ff.
  3. Combine → ∃ pre, δ σAO pre = some HasBool.ff.
  4. Transport σAO ↦ σ_old via subst_fvars_correct against Hsubst_L4.

WF additions in Strata/Languages/Core/WF.lean:

  • WFCallSiteProp.preBoolTyped (~line 1156): per-precondition bool-totality.
  • WFCallSiteSpec.preBoolTyped (~line 1207): per-call specialization.
  • WFCallSiteProp.specialize forwards the new field.

The exit arm is unchanged — call statements never produce .exiting. Sorry count: 19 → 18 (back to baseline). Build clean, no new axioms.

Polymorphic helpers — all genuinely consumed

Helper Source Callsites
EvalCallElim_glue_fail Strata/Transform/CoreTransformProps.lean 1
H_asserts_zip_fail Strata/Transform/SubstProps.lean 1
H_havocs_poly Strata/Transform/CoreTransformProps.lean 1
H_assumes_zip_poly Strata/Transform/CoreTransformProps.lean 1
WFPrePostProp.preBoolTyped Strata/Languages/Core/WF.lean 1

Helpers are deliberately single-callsite for now: the polymorphic-f infrastructure is staged to be reusable, but until a second consumer materializes the helpers stay scoped to this proof and we avoid speculative API design. No dormant scaffolding.

Soundness

  • Build: clean (490 jobs)
  • New sorries: 0 (baseline 18 in Strata/Languages/Core/WF.lean are pre-existing and unrelated to this work)
  • New admits: 0
  • New axioms: 0
  • #print axioms callElimStatementCorrect_terminal: [propext, Classical.choice, Quot.sound] — Lean stdlib only, no project-local axioms
  • Helper consumption: all 4 polymorphic helpers + preBoolTyped have ≥ 1 live callsite (verified above)
  • Path 1 conservativity: the narrowed call_sem iff matches callElimCmd's actual emission rule (asserts only for non-Free precondition components); success-arm reproof is mechanical

Commit list

be35c7f31 Stage 6: close polymorphic-f failure arm + lift public signature
fdf2fc43c Path 1: narrow EvalCommandContract.call_sem iff to non-Free preconditions
45db77d87 Stage 6 scaffold: lift _terminal to {f : Bool}, delegate failure arm
cf10129ef Stage 3: extract b1/b2_var_witness_at_oldSubst helpers
5406b08d0 Stage 2a: extract HoldSubBridge_at_σO helper
580ef2d26 Stage 1: extract HoldEval_bridge_at_σO helper
193dee2d6 WIP: polymorphic-f infrastructure (no live consumers)

Test plan

  • lake build clean across all 490 jobs
  • Zero new sorries introduced (baseline 18 in Strata/Languages/Core/WF.lean preserved)
  • Zero new admits/axioms introduced
  • #print axioms callElimStatementCorrect_terminal → Lean stdlib only ([propext, Classical.choice, Quot.sound])
  • Each polymorphic helper genuinely consumed (EvalCallElim_glue_fail, H_asserts_zip_fail, H_havocs_poly, H_assumes_zip_poly, WFPrePostProp.preBoolTyped — 1 callsite each)
  • Reviewer: spot-check Stages 1 / 2a / 3 (580ef2d, 5406b08, cf10129) are pure refactors by diffing helper bodies against the inline blocks they replaced
  • Reviewer: confirm Path 1 (fdf2fc4) preserves success-arm soundness — Hpre_evalTt re-synthesis over filtered preconditions is the only consumer-side change in the success arm
  • Reviewer: confirm Change A in 193dee2 (collapsed UpdateStates premise on call_sem) preserves the parent branch's false → false semantics
  • Downstream consumers of _terminal (none on htd/callelim-smallstep-on-main2 today; re-check after rebase if CallElimCorrect: rewrite using small-step semantics #1306 lands first)

Forward-looking scaffolding for polymorphic-f generalization of
callElimStatementCorrect. Build clean, sorry-free, axiom-free, but
none of the new helpers have callsites yet — the consumer-side wiring
requires extracting shared call-arm plumbing first (see Stage 1+ plan).

Changes:
- StatementSemantics.lean: Change A — collapsed two conditional Hupdate
  premises in EvalCommandContract.call_sem into a single unconditional
  UpdateStates premise. Failure path no longer pins σ' = σ.
- WF.lean: added boolTyped clause to WFPrePostProp giving the bool
  totality of pre/post conditions when their free vars are defined.
  Zero migration cost (no live construction sites).
- CoreTransformProps.lean: 3 polymorphic-f helpers
  (singleCmdToStmts_poly, singletonAssumeEval_poly, H_havocs_poly).
- SubstProps.lean: 4 polymorphic-f helpers (H_check_block_zip_poly,
  H_assumes_zip_poly, singletonAssertFailEval, singletonAssertEval_poly)
  plus the substantive new walker H_asserts_zip_fail (L4 flag-flip from
  false to true via failing-pre witness) and EvalCallElim_glue_fail
  (composes L1-L6 with L4 flag-flip).
- StatementSemanticsProps.lean: consumer update for Change A.

These 7 polymorphic helpers + glue_fail form the failure-arm toolkit.
They will be wired into callElimStatementCorrect_terminal_fail after
Stages 1-5 extract shared plumbing from the existing _terminal proof.
Lift the per-index δ-eval bridge for mkOld old-variable fvars at the
post-havoc store σO out of callElimStatementCorrect_terminal's call arm
into a private theorem.

Same proof body as the inline; promoted to a private theorem with 11
explicit hypotheses (Hwf2_univ, Hinitout, HσAO_reads_outs, Hevalouts,
hCallArgsLhs, HoutAlign, HoldVars_sub_{outs,lhs,callLhs}, HoldVals,
HoldValsLen). The inline block in _terminal becomes a one-line call.

Net diff: +114/-61 in CallElimCorrect.lean. The signature/docstring
overhead is intentional — Stage 6 will share this helper between the
success and failure arms once polymorphic-f wiring lands.

Build clean (490 jobs). Sorry count unchanged at 18. No new axioms.
Lift the per-fvar bridge for createOldVarsSubst's codomain at the L6
intermediate stores σ_R1/σO out of callElimStatementCorrect_terminal's
call arm into a private theorem.

Same proof body as the inline; promoted to a private theorem with 6
hypotheses (Hwfvars, three length facts, σ_R1_read_olds, HoldEval_bridge).
The inline block becomes a one-line call. δ_fvar_eq is re-derived from
Hwfvars inside the helper rather than threaded as a parameter.

Net diff: +82/-29 in CallElimCorrect.lean. Build clean (490 jobs).
Sorry count unchanged at 18. No new axioms.

HinputSubBridge (the larger sibling) is deferred — its ~25 dependencies
include several let-bound abbreviations (σ_R1, σO_eq_σAO_off_outs,
σR1_eq_σ_for_notTouched, etc.) that are tangled with the Hsubst
construction. It will be folded into Stage 4 instead.
Lift the two class-(b) substitution-decomposition witnesses out of
callElimStatementCorrect_terminal's call arm into private theorems.

b1_var_witness_at_oldSubst: when a Map.find? lands in the
createOldVarsSubst segment of oldSubst_L6, the codomain entry is a
fresh-old createFvar gen and the witness var ∈ getVars w forces
var = genOldIdents[i] for the same positional i.

b2_var_witness_at_oldSubst: when the lookup misses createOldVarsSubst
and hits callElim_inputOnlyOldSubst, the codomain is a positional
inArgs element and var appears in flatMap getVars inArgs.

Same proof bodies as the inline blocks. Both helpers take proc' and
args directly so callElim_inputOnlyOldSubst can be referenced by name
(needed for the find?_append_none_elim unification at the consumer).

Net diff: +123/-77 in CallElimCorrect.lean. Build clean (490 jobs).
Sorry count unchanged at 18. No new axioms.

Both helpers have 4 callsites in _terminal (Hinv and Hpred_disj for
inv, plus their post-side variants). Once Stage 6 lands, the failure
arm reuses the same callsites with identical arguments.
Lift callElimStatementCorrect_terminal's signature from f=false→false
to {f : Bool}, with Heval and conclusion both at flag f. Non-call cases
close via nc_close polymorphically (Heval passes through unchanged for
identity sts). Call branch dispatches:

  cases f with
  | false => existing 2200-LoC body verbatim (f=false case)
  | true  => exact callElimStatementCorrect_terminal_call_arm_fail ...

The _terminal_call_arm_fail helper is declared with full signature
(~25 hypotheses from the cases Hcc destructure) and a sorry body.
The body will be filled by a follow-up commit using the polymorphic
helpers shipped in the infra baseline (H_asserts_zip_fail, H_havocs_poly,
H_assumes_zip_poly, EvalCallElim_glue_fail) plus boolTyped extraction.

Build clean (490 jobs) at this checkpoint with one expected sorry
(line 1654). Sorry count: 18 → 19 (temporary; will revert to 18
when the helper body lands).

This commit is the Stage 6 scaffold; the next commit fills in the
failure-arm body.
…ions

Aligns the source-side failure semantics with callElimCmd's filtering.
Free preconditions ('free requires') are assumed by the implementation
but not checked at call sites (Procedure.lean §92), so they don't
contribute to caller-side failure. Previously the iff ranged over ALL
preconditions, which made the polymorphic-f failure-arm proof
unattainable: a witness 'pre with eval ≠ tt at σAO' could fall in the
Free segment, giving no L4 entry to flip the flag.

Changes:
- StatementSemantics.lean: call_sem rule's Hpre_def and Hpre_iff now
  range over `getCheckExprs (preconditions.filter (·.attr ≠ .Free))`.
- StatementSemanticsProps.lean: EvalCallBodyRefinesContract /
  EvalCommandRefinesContract / StepStmt[Star]_refines_contract /
  EvalStatement[s]RefinesContract gain `[LawfulBEq Expression.Expr]`
  instances (needed for the contains↔mem bridge in the new
  presFiltered_subset derivation that powers the rule constructor).
- CallElimCorrect.lean: success arm's Hpre_evalTt/Hpre re-synthesis
  now ranges over filtered preconditions (matches the new iff).
  HpreFiltered no longer needs filterCheck_in_getCheckExprs — direct
  membership in presFiltered transports straight to the filtered
  contains-form.
- CallElimCorrect.lean: failure-arm helper signature
  (callElimStatementCorrect_terminal_call_arm_fail) updated to take
  Hpre_def/Hpre_iff at the new filtered shape.

Build clean (490 jobs) with the one expected sorry at line 1654
(failure-arm body, to be filled by next commit). No new axioms.
Replaces the sorry-stubbed callElimStatementCorrect_terminal_call_arm_fail
with a complete proof body (~2,148 LoC) that mirrors the success arm's
L1-L3 + L5/L6 plumbing while flag-flipping at L4:

  - L1, L2, L3: identical to success (init segments produce f=false→false)
  - L4 (asserts): H_asserts_zip_fail with failing-pre witness
  - L5 (havocs): H_havocs_poly at f := true
  - L6 (assumes): H_assumes_zip_poly at f := true
  - Glue: EvalCallElim_glue_fail composing false→false→false→false→true

Failing-pre witness derivation now unblocked by Path 1 (commit fdf2fc4):
  1. Hpre_iff at flag=true: Classical.em on "all preconditions eval to tt"
     → contrapositive gives ∃ pre ∈ presFiltered, δ σAO pre ≠ some tt.
  2. Bool-totality from new WFCallSiteProp.preBoolTyped field (mirrors
     the boolTyped clause on WFPrePostProp): δ σAO pre = some tt ∨ ff.
  3. Combine → ∃ pre, δ σAO pre = some HasBool.ff.
  4. Transport σAO ↦ σ_old via subst_fvars_correct against Hsubst_L4.

WF additions:
  - WFCallSiteProp.preBoolTyped (line ~1156): per-precondition bool-totality.
  - WFCallSiteSpec.preBoolTyped (line ~1207): per-call specialization.
  - WFCallSiteProp.specialize forwards the new field.

Public signature lift: callElimStatementCorrect's terminal arm gains
{f : Bool} polymorphism — source-fail traces map to target-fail traces,
source-success traces map to target-success traces. Exit arm unchanged
(call statements never produce .exiting).

Build clean (490 jobs). Sorry count: 18 (back to baseline; the temporary
sorry from Stage 6 scaffold is now resolved). No new axioms.

Helpers EvalCallElim_glue_fail, H_asserts_zip_fail, H_havocs_poly,
H_assumes_zip_poly all genuinely consumed (not dormant scaffolding).
…es of _poly variants

Both non-poly forms now delegate to their polymorphic-f siblings at f := false,
removing two near-duplicate ~70-line proof bodies. H_assumes_zip_poly is moved
above H_assumes_zip to satisfy forward-reference order. H_asserts_zip continues
to call H_check_block_zip unchanged (now a 1-line corollary).
Generalize EvalCallElim_glue_fail to be polymorphic in the post-asserts
hasFailure flag `f` (was hard-coded to `true`). With `f` inferred from
HL4's output env, the passing arm (`f = false`) becomes the same proof
as the failing arm (`f = true`). EvalCallElim_glue is now a one-line
abbrev delegating to EvalCallElim_glue_fail; both names remain
resolvable and call sites are unchanged.

Saves 17 LoC by collapsing the two identical 5-step
EvalStatementsContractApp chains into one.
Three byte-identical δ-fvar lookup derivations in CallElimCorrect were
collapsed into a single file-scope private theorem
`delta_fvar_eq_of_wfvars`. Each call site now obtains the
∀ σ v, δ σ (fvar v) = σ v lemma via a one-line application instead of
re-deriving it from `Imperative.WellFormedSemanticEvalVar`.

Net -8 LoC. The instructions projected -14 with a `LawfulBEq` bound
on the helper, but that constraint is not synthesizable at the call
sites and is not actually needed by the proof — `HasFvar.getFvar` on
`Lambda.LExpr.fvar` reduces by the instance, not by `BEq`. Dropping
it both fixes the build and matches the existing usage pattern.

Build: 490 jobs clean. Sorry/admit count unchanged (20 in Strata/, all
pre-existing).
Hoist the per-entry decide-reduction proof bridging the boolean (!=) and
propositional (≠) filter forms over Procedure.CheckAttr.Free into a single
private theorem.  Replaces three inline copies in the fail-arm of
callElimStatementCorrect_terminal_call_arm_fail and one in the success-arm
asserts-zip step.
…n + by_cases

Refactor HexFail derivation in callElimStatementCorrect_terminal_call_arm_fail
from two stacked Classical.em rcases blocks into a single
Classical.byContradiction + intro + apply Hnot_all + by_cases pattern.

Same axiom dependency (Classical.choice, via byContradiction) — but the
structure is more idiomatic Lean 4: byContradiction gives the negated
conclusion directly, and by_cases handles the per-entry split without an
explicit absurd dance.

-6 LoC. Build clean (490 jobs). Sorry count: 0. Axioms unchanged:
[propext, Classical.choice, Quot.sound].
Drops 6 contract-refinement theorems with no external callers (audit-confirmed
via repo-wide grep, single closed dead chain):

  EvalCallBodyRefinesContract
  EvalCommandRefinesContract
  StepStmt_refines_contract           (private)
  StepStmtStar_refines_contract
  EvalStatementsRefinesContract
  EvalStatementRefinesContract

Also removes the prefatory NOTE/docstring block specific to
EvalCallBodyRefinesContract.

Helpers core_step_preserves_wfExprCongr / core_wfExprCongr_preserved are
retained — the latter is consumed by Strata/Transform/ProcBodyVerifyCorrect.

Build: lake build green (490/490). Axioms unchanged
([propext, Classical.choice, Quot.sound]) for both core_wfExprCongr_preserved
and CallElimCorrect.callElimStatementCorrect.

Net: -319 LoC.
The boolTyped field was added by the polymorphic-f branch but never
constructed anywhere. The failure arm now uses
WFCallSiteProp.preBoolTyped (in CallElimCorrect.lean) instead. Also
drop the StatementSemantics import that was added solely to elaborate
the dead field's body.
After Path 1 + Cleanup R1-5 (filter_bne_eq_filter_ne), the .contains-form
bridge filterCheck_in_getCheckExprs has zero callsites. Only the
membership-form filterCheck_mem_getCheckExprs remains in use.

-12 LoC; axioms unchanged ([propext, Classical.choice, Quot.sound]).
Both call-elim arms (success and failure) previously rebuilt the same
seven `have`-blocks (Hwfgenargs/outs/olds, Hgenargs/outs/olds, HgenApp,
Hgennd') from scratch.  Extract the combined-Nodup derivation into a
single private helper `genTrips_combined_nodup` and call it from each
arm; downstream consumers only need `Hgennd'` (already piped into
`fresh_triple_σ_facts`) plus the `Forall isTempIdent`/`isOldTempIdent`
witnesses, which remain as one-line `have`s.

Net: -28 LoC.  Build clean (490 jobs); axioms unchanged
([propext, Classical.choice, Quot.sound]); zero new sorries/admits.
Both arms of callElimStatementCorrect_terminal's call branch derived the
same four facts (Hwf2_univ, HσAO_reads_outs, HoldVars_sub_callLhs,
HoldVars_sub_outs) before invoking HoldEval_bridge_at_σO. Bundle them
into one private theorem and obtain at each call site.

Net: -20 LoC. Axioms unchanged: [propext, Classical.choice, Quot.sound].
The Hinv proofs at the four per-entry payload sites in
callElimStatementCorrect each open the recurring pattern
  (k1, k2) ∈ ((getVars expr).removeAll ((l₁ ++ l₂) ++ (l₃ ++ l₄))).zip ...
into k1 = k2 plus six leaf facts via a manual `zip_self_eq` +
`simp [List.removeAll, List.mem_filter, ...]` + `notin_append4` cascade.
Bundle that decomposition into a single `zip_removeAll4_decompose`
helper next to `notMem_of_Forall_neg` so each call site collapses to
one `obtain`.

Net: -19 LoC, axioms unchanged ([propext, Classical.choice, Quot.sound]).
Both call-arm sites in `_terminal_call_arm_fail` and `_terminal`
re-derived `proc' = proc` from `Hp`+`Hfind`+`lkup` and built the
postcondition-membership lift `proc' ↦ proc`. Factor into a single
private theorem `procEq_and_postExprs_bridge` returning the pair, and
replace the two ~15-line blocks with one-line `obtain ⟨HprocEq,
c_in_postExprs_of_proc'⟩ := ...` calls.

Net: -6 LoC (helper costs ~20 lines, sites save ~25 combined).
Axioms unchanged; lake build clean (490 jobs).
createAsserts_ok and createAssumes_ok shared a byte-identical
inductive proof body, differing only in the Statement constructor
(assert vs assume). Extract createCheckStmts_ok parameterized by the
constructor mkStmt, and rewrite both _ok lemmas as one-line
specializations (unfold + exact). Single-source the proof body;
both call sites preserve their public spec shape.

Net -3 LoC; primary win is structural (eliminate duplicated
induction proof, ~36 lines collapsed into one helper at the cost
of two thin specialization wrappers).
In subst_fvars_eval_bridge, the abs/quant/app/eq cases each unfolded a
4-block surv/sub witness pair. The ite case already used a compact
HmkLeft/HmkMid/HmkRight position-lifting style.

Apply the same style to the other four cases. Each non-ite case now
defines 1-2 small position-lift lemmas (Hmk / HmkL / HmkR) and inlines
the surv/sub call as fun-form arguments to the IH.

  abs:   24 → 11 lines
  quant: 42 → 21 lines
  app:   42 → 21 lines
  eq:    42 → 21 lines

Build clean (490 jobs); axioms unchanged (propext, Quot.sound).
LoC delta: -79.
Promote the per-fvar bridge for callElim_inputOnlyOldSubst's codomain
into a private theorem (parallel to HoldSubBridge_at_σO).  The
~140-LoC derivation in callElimStatementCorrect's call-arm success and
failure branches collapses to a single 8-line application.

Net: -84 LoC (helper +220 / per-arm savings 2 × ~152).
Collapse 48 instances of `rw [hEq]; exact h` (and `rw [← hEq]; exact h`)
into single-line term-mode transports `exact hEq ▸ h` (resp.
`exact hEq.symm ▸ h`). Pure cosmetic refactor; build clean, axioms
unchanged ([propext, Classical.choice, Quot.sound]).

Net: -49 LoC.
The HwfL term in HoldSubBridge_at_σO already had δ_fvar_eq in scope. The
wrapping show/exact tactic block was a definitional unfold of
Core.Transform.createFvar (which reduces to Lambda.LExpr.fvar () _ none),
so the term-mode application of δ_fvar_eq closes the goal directly.
Where 'apply F ?_ Y' followed by 'exact Z' in have-blocks could be
written as a direct term 'F Z Y', collapse to term mode. Skipped
sites where the apply relied on goal-driven unification of implicits
(genTrips_combined_nodup) or had multi-bullet remaining goals.
Replace 'by intro h; exact f h …' tactic blocks with the corresponding
term-mode lambda 'fun h => f h …' across several local 'have' bindings
in callElimStatementCorrect_terminal_call_arm_fail and
callElimStatementCorrect_terminal. The bodies were already pure
applications, so the tactic wrapper was pure boilerplate.

LoC: -11 lines.
Build: clean (490/490). Sorries: 0. Axioms: [propext, Classical.choice, Quot.sound].
@PROgram52bc PROgram52bc merged commit be987fd into htd/callelim-smallstep-on-main2 Jun 8, 2026
13 checks passed
@PROgram52bc PROgram52bc deleted the htd/polymorphic-f-callelim branch June 8, 2026 15:28
PROgram52bc added a commit that referenced this pull request Jun 9, 2026
## Summary

Round 3 tier-1 cleanups for the polymorphic-`f` callElim work merged in
#1340. These commits were authored on top of #1340's head before the
squash merge but were not included; this PR brings them onto the parent
branch.

Net delta: **-287 LoC** (5 files, +181/-468) across 19 small, atomic
commits. No new sorries, no new axioms, no behavioral change.

## Highlights

| Commit | Description | LoC |
|---|---|---|
| 2bb66b3 | R3-1: collapse `WFCallSiteProp` into `WFCallSiteSpec` via
def-wrapper | -64 |
| ca67ea7 | R3-2: drop f=false corollaries; rely on poly versions |
-121 |
| 680e092 | R3-12: hoist `π/φ/δ` binders into file-scope `variable`
block | -10 |
| 26bf9ae | R3-10: extract `havocVars_3layer_lift` helper | -23 |
| ed3725d | R3-8: add `Procedure.Spec.checkedPreconditions` abbrev |
-12 |
| 7b6fbb3 | R3-19: simplify HentryFail mem-zip pattern in fail arm |
-10 |
| 8e3bc3f | R3-32: flatten nested mem_append cases via rcases | -14 |

Plus 12 smaller items: bool-totality cleanup, redundant hypothesis
drops, helper extractions, doc fixes (stale "seven" counts, removed
references to deleted `WFPrePostProp.boolTyped`, fixed inverted section
header hierarchy, dropped legacy `Hwfgenst` references).

## Soundness

- **Build clean**: 490 jobs, no warnings or errors
- **Sorries**: 0 in modified files
- **Axioms**: `callElimStatementCorrect` depends only on `[propext,
Classical.choice, Quot.sound]` — Lean stdlib only
- **No behavioral change**: each commit is a refactor verified by `lake
build`

## Test plan
- [ ] CI green
- [ ] No sorry/axiom regressions vs base
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.

1 participant