feat: adequacy interface skeleton (depends on #393)#394
Conversation
|
A personal request: please squash and remove the "claude" co-author. It's completely fine to use AI assistance, but this is not a useful marker to add to a PR, and I do not like adding advertisements to the repo. |
…oaches tried)
Comprehensive testing of `iapply step_fupdN_wand $$ Hwptp_step`:
1. Bare iapply — FAIL
2. With `imod Hwptp_step` first — FAIL
3. Explicit `(P := ...) (Q := ...)` matching exactly — FAIL
4. Explicit `(Eo := ∅) (Ei := ∅) (n := ...)` — FAIL
5. `ihave Hsfup := ... $$ Hwptp_step` succeeds, but `iapply Hsfup` then FAILS
All hit: "iapply: cannot apply Nat.repeat (fun Q => iprop(|={∅}=> ▷ |={∅}=> Q))"
Confirmed: iris-lean's IntoWand instance for step_fupdN_wand has a real
higher-order unification gap on **nested step_fupdN** (when goal has form
`Nat.repeat f n (Nat.repeat f m X)`).
Coq Iris handles this via tactic engine's defeq + HOU. iris-lean's static
typeclass approach can't unify the partially-applied Nat.repeat.
Bypass options for the future:
- iris-lean upstream fix to IntoWand
- Pure Lean term-mode chain (~50 lines explicit BIBase.trans + step_fupdN_wand)
- Ask iris-lean community for the right idiom on PR leanprover-community#394
Score unchanged: 7 sorry. Partial proof up to wptp_step + extract ∃ nt'_step
remains as ~50 lines of working IPM code committed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Hi @lihaokun, thank you for the PR! |
f747c79 to
e7c9d94
Compare
|
Thanks for the PR again! I'll start reviewing it tomorrow. |
|
@markusdemedeiros @Kaptch sorry for the noise from the Claude co-author tags — The branch has a few substantive changes on top of the original adequacy port. 0 Coq 1:1 alignment pass on the 4 meta signatures:
Small additions, flagged in code:
All I can split any of these helpers into separate PRs if you'd prefer smaller units, |
…kestpre interface
Lean 4 port of `iris/program_logic/adequacy.v`, adapted to the
`IrisGS_gen` / `wp.pre` / `WP _ @ s ; E {{ Φ }}` interface from PR leanprover-community#393.
Proven in this branch (5 lemmas + 2 theorems):
- `wp_step`, `wptp_step`, `wp_not_stuck`
- `wptp_preservation` (refl case)
- `adequate_alt`, `adequate_tp_safe`
Remaining sorries:
- `wptp_preservation` cons (skeleton + ~40 lines of working IPM; blocked
at `imod Hbody` — goal is step_fupdN, not fupd outermost)
- `wptp_postconditions`, `wptp_progress` (plain sorry, follow same pattern
as `wptp_preservation`)
- `wp_progress_gen`, `wp_strong_adequacy_gen`, `wp_adequacy_gen`,
`wp_invariance_gen` (4 meta-level theorems with `_hwp : True` placeholders;
unblocked once `InvGpreS`-style existential is finalized)
Helper `step_fupdN_compose` proves `(|=...=>^[a] P) ⊢ (P -∗ |=...=>^[b] Q) -∗
|=...=>^[a+b] Q` (`step_fupdN_wand` + `Nat.repeat_add`).
Port `step_fupdN_S_fupd` from `iris/bi/updates.v` as a private helper
(plus auxiliary `step_fupd_mono_lift` and `step_fupdN_mono`). Use it to
reshape the cons-case goal so `imod Hbody` can absorb its `|={∅,⊤}=>`
modality across the leading `step_fupdN^[k]`.
Remaining sorries:
- wptp_postconditions, wptp_progress (same NSteps induction pattern;
need per-element `wptp → from_option` conversion + wp_not_stuck splice)
- 4 meta adequacy theorems (blocked on `InvGpreS`-style infrastructure)
…omplete)
`wptp_postconditions` (with helpers `fromOptionVal` + `wp_to_postcond` +
`wptp_to_postcond`): compose `wptp_preservation` with per-element WP →
`from_option` conversion. Key trick: `show iprop(... fromOptionVal e Φ)`
top-of-proof to bridge goal's explicit `match` vs the helper's named
`@[reducible]` def — IPM tactics do not unfold reducible defs but `show`
does.
`wptp_progress`: compose `wptp_preservation` + `BigSepL2.bigSepL2_lookup_acc`
(extract WP e2 from the post-step wptp) + `wp_not_stuck`. Uses
`List.getElem?_of_mem` to obtain the index in es2 and length-bridge from
`bigSepL2_length` to obtain the corresponding Φs index.
Remaining sorries: 4 meta adequacy theorems (`wp_progress_gen`,
`wp_strong_adequacy_gen`, `wp_adequacy_gen`, `wp_invariance_gen`) — these
still have `_hwp : True` placeholders; next step is to type them properly
with `[InvGpreS GF]` and `∀ [Hinv : InvGS_gen hlc GF], ⊢ |={⊤}=> ∃ ...`
shape.
…(Group B)
All 4 meta adequacy theorems now have proper signatures:
- `[InvGpreS GF]` constraint added (matches Coq's `invGpreS Σ`)
- `_Hwp` typed as `∀ [_Hinv : InvGS_gen hlc GF] [iG : IrisGS_gen hlc Expr GF],
⊢ iprop(|={⊤}=> ...)` — Lean encoding factors the user-supplied state /
forkPost / numLatersPerStep / monotonicity as an `IrisGS_gen` typeclass
instance, rather than as separate Iris-level ∃ vars (Coq's `let _ := IrisG`
idiom cannot be expressed inside Lean's `iprop(...)` notation since `letI`
is a tactic/term-mode binder).
- `wp_progress_gen`: `wptp s es Φs` shape, returns `PrimStep.NotStuck (e2,σ2)`
- `wp_strong_adequacy_gen`: full strong adequacy with thread-split,
postcondition list, and forkPost list witness
- `wp_adequacy_gen`: simplified shape using `WP e {{ v, ⌜φ v⌝ }}`
- `wp_invariance_gen`: state-invariance shape with σ2 stateI and final
`∃ E, |={⊤,E}=> ⌜φ⌝`
Proofs still `sorry`. Build passes with 4 sorry warnings (the 4 meta
theorems).
`wp_invariance_gen`: full structural proof using `adequate_alt` + `wp_strong_adequacy_gen` (oracle) + `erasedStep_nSteps` + per-element bigSepL2 handling. Internal sorry at the `bridge` step: our Lean signature has `iG.stateInterp σ2 0 [] (t2.length - 1)` with literal `ns = 0`, while `wp_strong_adequacy_gen` yields `ns = n` from the NSteps count. Coq absorbs this via `(λ σ _ _ _, stateI σ)` in the user- constructed `irisGS_gen`; the Lean PR leanprover-community#393 interface receives iG externally, so the `ns` parameter is literal `0` in the user signature. `stateInterp_mono` only goes `ns → ns+1` (not downward), so we cannot bridge `n → 0`. Fix path: change wp_invariance_gen's `_Hwp` signature to take a `stateI : State → IProp GF` (without ns/obs/nt) and construct `iG.stateInterp σ _ _ _ = stateI σ` via a custom IrisGS_gen instance. Deferred. Other meta proofs (wp_strong_adequacy_gen, wp_progress_gen, wp_adequacy_gen): agents either still running or hit fromOptionVal/match-form ispecialize blockers; left as plain `sorry`.
…Simple After 4 parallel agents attempted Group B proofs: - Reverted wp_strong_adequacy_gen (Agent leanprover-community#1's attempt build-broke on ispecialize unification) - Reverted wp_adequacy_gen (Agent leanprover-community#3 hit match aux-def blocker) - Kept wp_invariance_gen partial proof (Agent leanprover-community#4: 1 internal bridge sorry due to ns=n vs 0 signature impedance) Added helper IrisGS_gen.ofSimple for upcoming wp_adequacy_gen / wp_invariance_gen signature refactor. Build passes; 4 sorries.
…prop) User-supplied stateI/forkPost now passed as ordinary Pi args. letI constructs IrisGS_gen via IrisGS_gen.ofSimple OUTSIDE iprop (letI inside iprop hits 'elaboration function for term_∗_ not implemented'). Agent leanprover-community#1 (wp_strong_adequacy_gen) ALSO identified that the current signature design is fundamentally broken: section-variable [iG] is external, but step_fupdN_soundness allocates a fresh Hinv internally. The two InvGS_gen instances have different LcGS/WsatGS ghost-name backing, so £-cells from the two contexts don't unify. Fix is the same pattern: take stateI/forkPost/numLaters/mono as Pi args, construct iG inside via letI. wp_progress_gen / wp_adequacy_gen / wp_strong_adequacy_gen need the same treatment — TODO. Proof body reverted to sorry; will rewrite for new signature.
…attern All 4 meta theorems now share a uniform Coq-1:1 signature shape: - User supplies stateI/forkPost (and numLaters/mono for strong) as ordinary Pi arguments. - letI constructs IrisGS_gen INSIDE the user's _Hwp (outside iprop, since letI cannot be introduced inside iprop syntax). - Helper IrisGS_gen.ofSimple (3 simpler theorems) / ofFull (wp_strong_adequacy_gen). This fixes the LcGS / WsatGS ghost-name mismatch that Agent leanprover-community#1 diagnosed: previously [iG : IrisGS_gen] was section-bound externally, so its toLcGS / toWsatGS differed from the Hinv freshly allocated by step_fupdN_soundness. Now iG is constructed FROM Hinv inside the user's _Hwp, so all £/fupd cells share the same ghost backing. Also fixes the ns=n vs 0 impedance for wp_invariance_gen (already done). All 4 proofs reverted to plain sorry pending rewrite for new signatures. Build passes.
- Add IrisGS_gen.ofFull: builds iG from full 4-arg stateI + mono (for wp_strong_adequacy_gen) - Mark IrisGS_gen.ofSimple and ofFull as @[reducible] so Lean unfolds them during unification - Update wp_progress_gen sig to use Stuckness.NotStuck (drop the s parameter — wptp_progress requires NotStuck for soundness, per Agent leanprover-community#2) Build passes; 4 sorries (all 4 meta theorem proofs). Proof attempts on wp_progress_gen reveal additional iris-lean IPM gaps that need further work (inner |={∅}=> absorption around pure props doesn't auto-unify with step_fupdN_soundness_gen's expected shape; needs fupd_pure_intro or except_0 manipulation).
Key insight: instead of porting fupd_pure_strip (which requires going to
meta level via soundness), use n := k+1 in step_fupdN_soundness_gen and
absorb wptp_progress's inner |={∅}=> via step_fupdN_S_fupd.2 + step_fupdN_le.
Proof structure:
1. apply pure_soundness → ⊢ ⌜NotStuck⌝
2. refine step_fupdN_soundness_gen (n := k+1) (m := k+1) hlc
3. intro Hinv, iintro Hcr
4. split £ (k+1) → £ k ∗ £ 1 via lc_split
5. letI iG := ofSimple Hinv (fun _ => emp) (fun _ => True) numLaters
6. apply user's _Hwp at trivial stateI/forkPost, destructure
7. apply wptp_progress to get |={⊤,∅}=> step_fupdN^[k] |={∅}=> ⌜⌝
8. apply progress_widen_bridge: lifts to |={⊤,∅}=> step_fupdN^[k+1] ⌜⌝ via
step_fupdN_le (k ≤ k+1) + step_fupdN_S_fupd.2 (strip inner |={∅}=>)
Add private theorem progress_widen_bridge (extracted to avoid whnf heartbeat
timeout that Agent leanprover-community#2 also documented).
Remaining: wp_strong_adequacy_gen, wp_adequacy_gen, wp_invariance_gen.
After wp_progress_gen proven (1/4 Group B), refined inline TODO comments on wp_strong_adequacy_gen / wp_adequacy_gen / wp_invariance_gen to document which parts of the bridge-pattern apply and which need additional structural work (NS-derivation via nested wptp_progress for strong; mask close via fupd_mask_intro_discard for invariance; bigSepL2 singleton extraction for adequacy). Build clean; 3 sorries (3 meta theorems) + significant proof scaffolding already in place from agents' work.
Same skeleton as wp_progress_gen, NOT routed through wp_strong_adequacy_gen
oracle (signature mismatch: strong's stateI_full is ∀-quantified in our
Lean port, breaks the Coq-style iExists trick).
Direct proof using wptp_preservation + ofSimple iG construction:
1. obtain n κs hsteps from erasedStep_nSteps
2. pure_soundness + step_fupdN_soundness_gen (n := k+1) (m := k+1) hlc
3. intro Hinv, iintro Hcr, split £ (k+1) → £ k ⊗ £ 1
4. letI iG := ofSimple Hinv (fun _ => emp) (fun _ => True) (fun _ => 0)
5. Specialize user's _Hwp at stateI := fun _ => emp, forkPost := fun _ => True
6. Convert WP e1 to singleton wptp via bigSepL2_singleton.2
7. Apply wptp_preservation → |={⊤,∅}=> step_fupdN^[k] |={∅,⊤}=> ∃ nt', stateI σ2 ∗ wptp
8. imod + step_fupdN_compose to split k = k+0 → 1-step continuation
9. Inside 1-step (after simp [Nat.repeat] to expose |={∅,∅}=> ▷ |={∅,∅}=>):
imod Hinner with ⟨%nt', HSI, _Hwptp⟩
10. HSI ≡ emp by ofSimple reducibility; apply Hφ HSI → ∃ E, |={⊤,E}=> ⌜φ⌝
11. Destructure E, open Hcl to pure hφ
12. Close masks: fupd_mask_intro_discard empty_subset + later_intro + fupd_intro
No new helpers added — uses step_fupdN_compose (already in file).
Remaining: wp_strong_adequacy_gen, wp_adequacy_gen.
Agent leanprover-community#2 (a5452010) proved wp_adequacy_gen. Strategy: bypass wp_strong_adequacy_gen oracle (∀-quantified stateI_full vs user's ofSimple stateI_simple don't unify via WP), split into 2 conjuncts of adequate_alt: - Part 2 (NotStuck): delegate to wp_progress_gen (same ofSimple shape) - Part 1 (φ v2 from t2 = ofVal v2 :: ...): direct via wptp_postconditions + new helpers adequacy_value_inner_bridge + adequacy_value_widen_bridge Key technical changes: 1. fromOptionVal: dropped qualifier (now used in public signature of wptp_postconditions) 2. wptp_postconditions signature: changed from inline to (avoids match aux-def collision between wptp_postconditions's body and the new adequacy_value bridges) 3. Dropped the in wptp_postconditions's proof body (no longer needed since signature now directly uses fromOptionVal) 4. Two new private helpers extracted to avoid whnf heartbeat timeout (same pattern as progress_widen_bridge) Remaining: wp_strong_adequacy_gen (agent still running).
ALL 4 Group B meta theorems now PROVEN. Adequacy.lean has ZERO sorry. Proof structure (~165 lines): 1. NS derivation via wp_progress_gen (iG defeq from spike-verified ofSimple ≡ ofFull(lift)) 2. pure_soundness + step_fupdN_soundness_gen (n+1 trick) 3. Apply user's _Hwp with chosen trivial stateI/forkPost 4. wptp_preservation + step_fupdN_compose + wptp_to_postcond 5. bigSepL2_app_inv_right split + fork_block_to_filterMap + iapply Hφ 6. BIFUpdate.mono + later_intro + fupd_intro to close Key bridge: bigSepL2 match-form ↔ fromOptionVal via BIBase.Entails.trans + show Entails' to re-enter IPM. Build clean.
… feedback Address review-style issues found in self-audit: 1. Remove incorrect @[rocq_alias] annotations from wptp_progress and wp_progress_gen — neither exists in Coq adequacy.v. Replace doc comments with explicit "Lean addition" wording. 2. Update file header: change "proofs left sorry (interface skeleton)" to reflect that all theorems are now fully proven; explain the two Lean additions (per-thread NotStuck factoring). 3. Clean AI-style "Step N:" prefix comments in wp_strong_adequacy_gen proof (matches lzy0505's feedback on PR leanprover-community#389 "clean up all the AI generated comments"). Keep technical nl insight, drop step numbering. 4. Rephrase "By spike:" narration as direct technical statement. Coq 1:1 audit: - All 5 Local Lemmas + Lemma wp_strong_adequacy_gen + Definitions + Record + Lemma adequate_alt + Theorem adequate_tp_safe present with matching signatures. - wptp / steps_sum match Coq's Notation / Local Fixpoint. - Two Lean additions (wptp_progress / wp_progress_gen) documented. No proof body changes; build clean, 0 sorry.
…quacy / _invariance) Replace True placeholders with proper abbrev specializations to hlc := true, matching Coq's Definition X := X_gen HasLc pattern. No new infrastructure needed — just partial application of the _gen theorems. All 3 definitions now 1:1 with Coq adequacy.v.
…gen.ofKObs helper Signatures restructured (per nl-proof refactor-coq-1to1-meta-signatures.md): - wp_progress_gen / wp_strong_adequacy_gen: stateI/Φs/forkPost/mono now ∃-bound in iprop (matching Coq ∃ at BI level, not ∀ at Pi level). numLaters stays at Pi-level due to Lean IPM 'istart' not supporting metavariables (Coq's (S _) partial-term pattern infeasible; documented as option X). - wp_adequacy_gen / wp_invariance_gen: stateI is 2-arg (state + obs list) via new IrisGS_gen.ofKObs helper, matching Coq's 'λ σ _ κs _, stateI σ κs' lifting. numLaters hardcoded to 'λ _, 0' inside ofKObs (Coq same). wp_invariance_gen end hypothesis uses 'stateI σ2 []' (Coq same). New helper: - IrisGS_gen.ofKObs: 2-arg stateI + forkPost → IrisGS_gen instance. Lifts stateInterp via 'fun σ _ κs _ => stateI σ κs'; mono trivial via fupd_intro (state_interp ignores ns). Proof adjustments: - All 4 proofs ∃-eliminate stateI/forkPost/Φs/mono via imod-with-pattern before constructing iG (defeq with user's let-bound iG via @[reducible]). - HSI bridge for κs ↔ κs ++ [] via List.append_nil (only matters for wp_adequacy/_invariance/_progress where stateI uses obs argument). - wp_strong_adequacy_gen output: bridge stateI σ2 (n+0) [] (0+nt') → stateI σ2 n [] t2'.length using hlen_t2'_nt'. Build clean (0 sorry). Spikes verified letI inside iprop ∃ (leanprover-community#1), multi-∃ imod (leanprover-community#2), ofKObs ≡ ofFull defeq (leanprover-community#4). Spike leanprover-community#3 (numLaters in ∃) confirmed infeasible — documented in refactor doc.
9a26f26 to
5bdfb19
Compare
|
Note that the proof of adequacy in Rocq recently changed: https://gitlab.mpi-sws.org/iris/iris/-/merge_requests/1217 |
- Add 'omit iG in' to 4 meta theorems (wp_progress_gen, wp_strong_adequacy_gen, wp_adequacy_gen, wp_invariance_gen) — these construct their own iG via letI inside the proof, so the section-level [iG] auto-inclusion is unused. - Suppress unused-simp-args linter on wptp_preservation refl case (the 3 args linter flags as unused individually all interact with the surrounding simp chain; removing any 2+ breaks subsequent iframe).
|
Just noticed CI failed on the previous push — the strict linter caught a few warnings I'd missed locally. Pushed a follow-up fix (977e271). Two things: (1) (2) Let me know if either is off. |
|
@MackieLoeffel Thanks for the pointer — I went through MR 1217. The good news is that the user-facing signatures of Integrating it would mean porting the new Would it be okay to land this PR as-is and treat the MR 1217 integration as a follow-up? |
|
I will open a PR for my fix of #396 later today. |
|
I think for now it would be better to keep it as it is, and make a separate issue for porting this machinery. I'm starting to review the code, so I'm taking a lock on this PR for the first pass. |
|
@Kaptch One small follow-up — We translated it as a
|
|
I'll need to take a closer look, but could you please elaborate on "breaks the bridge to the underlying bigSepL2 form"? |
|
You're right — Separately, that wouldn't fix |
|
No, that's okay, thanks, I'm making a pass. |
|
Agreed re. not blocking this PR on the improved later credits. Though they are a very nice feature and I think we would do well to work on it soon. |
|
Hi again, I've made my initial pass, I'll take one more look on Monday with fresh eyes (and probably someone else as well, as I could miss something). @markusdemedeiros could you also take a look, if you have time?
I hope it doesn't sound harsh or rude, but please, when using AI, compare the output with the Rocq implementation. |
|
Thank you for giving a pass Sergei (seems like reviewing a big AI PR is a reviewer rite of passage). I'll look at it this afternoon! |
|
The code is looking pretty good now! Tidied up a couple parts here and there. |
|
Thanks for the careful review, Sergei, and apologies for the cleanup burden. Point about comparing AI output against the Rocq implementation is well taken. :D |
Description
Lean 4 port of
iris/program_logic/adequacy.v, addressing #352. Implementsthe interface skeleton for the adequacy layer.
Status: Draft. Will be marked ready for review once @ayhon's #393
(
weakestpre.v) lands in master, at which point this PR will:wp_pre→wp.pre, etc.)_hwp : Trueplaceholders in the four meta-level theoremswith their real existential signatures (
∀ Hinv : InvGS_gen, ⊢ ∃ stateI Φs fork_post state_interp_mono, IrisG Hinv ...), using whateverconstructor syntax feat: add weakest precondition #393 finalizes for
IrisGS_gen.Overlap with #393
This PR currently includes 3 extra files (
WeakestPre.lean,Lifting.lean,and
step_fupdN_neinUpdates.lean) carried from my fork's localstage/1-stage/4branches, because adequacy needs the wp interface and itisn't in master yet. These are not part of this PR's review scope — they
will be deleted on rebase when #393 + lifting land. Reviewers can focus on
Iris/ProgramLogic/Adequacy.lean+ thePORTING.mdentry.Scope (1:1 with Coq
adequacy.v)Definitions
wptp s es Φs—[∗list] e;Φ ∈ es;Φs, WP e @ s; ⊤ {{ Φ }}(Coq line 19)steps_sum num_laters start n— accumulatednum_laters_per_stepover nphysical steps (Coq line 57)
adequaterecord — postcondition + progress (Coq line 281)Local lemmas (Coq lines 21-158)
wp_step/wptp_step/wp_not_stuckwptp_preservation/wptp_postconditions/wptp_progressTop-level theorems (Coq lines 161-381)
wp_progress_gen/wp_strong_adequacy_gen/wp_strong_adequacyadequate_alt(proven) /adequate_tp_safe(proven)wp_adequacy_gen/wp_adequacywp_invariance_gen/wp_invarianceSorry inventory
wp_step/wptp_step/wp_not_stuck/wptp_preservation/wptp_postconditions/wptp_progress(6)wp_unfold/wp_strong_mono/fupd_wp/ etc.wp_progress_gen/wp_strong_adequacy_gen/wp_adequacy_gen/wp_invariance_gen(4)∀ Hinv, ⊢ |={⊤}=> ∃ stateI ...) abstracted as_hwp : True. The required infrastructure (InvGpreS,InvGS_gen,step_fupdN_soundness_gen) is already inIris/Instances/Lib/FUpd.lean; the blocker is theIrisGS_genconstructor / extends-chain syntax, which is being finalized in #393.adequate_alt/adequate_tp_safeConventions followed (per PR #389 review)
@[rocq_alias]annotationsCoq counterpart by name)
Context
I'm porting VST to Lean 4 (see #389). The split agreed with @ayhon:
weakestpre.v(feat: add weakest precondition #393) +lifting.v(Port program_logic/lifting.v #348) +ectx_lifting.v(Port program_logic/ectx_lifting.v #349)adequacy.v(this PR, Port program_logic/adequacy.v #352)Checklist
adequacy.ventry)