Skip to content

Commit 1fe451a

Browse files
committed
chore: target nightly-2026-08-21 and port to exception stacks
The tuple refactor (leanprover/lean4#14836) replaces the bespoke exception postcondition types. In the vcgen documents, EPost.Nil becomes EStack⟨⟩, EPost.Nil.mk becomes (), and the Result monad carries a bare Error → Prop exception postcondition, so epost e replaces epost.head e and the head-projection step leaves the monotonicity proof. The push helpers for transformers are PredTrans.pushExceptT and PredTrans.pushOptionT. The EStack⟨…⟩ and estack⟨…⟩ notations get a syntax block in the exception postcondition section. The recorded Lake transcripts gain the new lint option --checks and the package configuration field checks. Verified with ./generate-html.sh on nightly-2026-08-21.
1 parent b547b93 commit 1fe451a

6 files changed

Lines changed: 35 additions & 22 deletions

File tree

Manual/BuildTools/Lake.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ root = "Tests"
763763
libPrefixOnWindows := false,
764764
allowImportAll := false,
765765
builtinLint? := none,
766+
checks := #[],
766767
fixedToolchain := false},
767768
configFile := FilePath.mk "lakefile",
768769
relConfigFile := FilePath.mk "lakefile",
@@ -927,6 +928,7 @@ lean_exe «my-package-tests» where
927928
libPrefixOnWindows := false,
928929
allowImportAll := false,
929930
builtinLint? := none,
931+
checks := #[],
930932
fixedToolchain := false},
931933
configFile := FilePath.mk "lakefile.lean",
932934
relConfigFile := FilePath.mk "lakefile.lean",
@@ -1131,6 +1133,7 @@ root = "Lint"
11311133
libPrefixOnWindows := false,
11321134
allowImportAll := false,
11331135
builtinLint? := none,
1136+
checks := #[],
11341137
fixedToolchain := false},
11351138
configFile := FilePath.mk "lakefile",
11361139
relConfigFile := FilePath.mk "lakefile",
@@ -1296,6 +1299,7 @@ lean_exe «my-package-lint» where
12961299
libPrefixOnWindows := false,
12971300
allowImportAll := false,
12981301
builtinLint? := none,
1302+
checks := #[],
12991303
fixedToolchain := false},
13001304
configFile := FilePath.mk "lakefile.lean",
13011305
relConfigFile := FilePath.mk "lakefile.lean",

Manual/BuildTools/Lake/CLI.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,9 @@ OPTIONS:
895895
--code-quality records each linter warning as a code quality check result
896896
and runs the registered code quality checks.
897897
Setting this flag will skip lint driver.
898+
--checks <mods> comma-separated list of workspace modules providing
899+
package code quality checks; they are imported
900+
alongside each linted module (implies --code-quality)
898901

899902
A lint driver can be configured by either setting the `lintDriver` package
900903
configuration option or by tagging a script or executable `@[lint_driver]`.

Manual/BuildTools/Lake/Config.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ name = "example-package"
193193
libPrefixOnWindows := false,
194194
allowImportAll := false,
195195
builtinLint? := none,
196+
checks := #[],
196197
fixedToolchain := false},
197198
configFile := FilePath.mk "lakefile",
198199
relConfigFile := FilePath.mk "lakefile",
@@ -284,6 +285,7 @@ name = "Sorting"
284285
libPrefixOnWindows := false,
285286
allowImportAll := false,
286287
builtinLint? := none,
288+
checks := #[],
287289
fixedToolchain := false},
288290
configFile := FilePath.mk "lakefile",
289291
relConfigFile := FilePath.mk "lakefile",

Manual/VCGen.lean

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,14 @@ Since unit and pair types come with {name}`Assertion` instances, such exception
165165
The {name}`WP` translation turns monad transformer stacks turn into exception postcondition stacks.
166166
The notation `EStack⟨e₁, e₂, ...⟩` abbreviates the type of exception postcondition stack `e₁ × (e₂ × (... × Unit))`, and the notation `estack⟨v₁, v₂, ...⟩` builds a value `(v₁, v₂, ..., ())` of such a stack.
167167

168+
:::syntax term (title := "Exception Postcondition Stacks") (namespace := Std.WP)
168169
```grammar
169-
Insert this, Claude!
170+
EStack⟨$_,*⟩
170171
```
172+
```grammar
173+
estack⟨$_,*⟩
174+
```
175+
:::
171176

172177
{TODO}[I think the partial vs. total correctness discussion should maybe happen after we have actually introduced Triple? It discusses its syntax.]
173178

@@ -209,15 +214,15 @@ The {name}`bind` operator composes predicate transformers.
209214

210215
{docstring Lean.Order.instMonadPredTrans}
211216

212-
The helper operators {name}`Lean.Order.pushArg`, {name}`PredTrans.pushExcept`, and {name}`PredTrans.pushOption` modify a predicate transformer by adding a standard side effect.
217+
The helper operators {name}`Lean.Order.pushArg`, {name}`PredTrans.pushExceptT`, and {name}`PredTrans.pushOptionT` modify a predicate transformer by adding a standard side effect.
213218
They are used to implement the {name}`WP` instances for transformers such as {name}`StateT`, {name}`ExceptT`, and {name}`OptionT`; they can also be used to implement monads that can be thought of in terms of one of these.
214219
For example, {name}`Lean.Order.pushArg` is typically used for state monads, but can also be used to implement a reader monad's instance, treating the reader's value as read-only state.
215220

216221
{docstring Lean.Order.pushArg}
217222

218-
{docstring PredTrans.pushExcept}
223+
{docstring PredTrans.pushExceptT}
219224

220-
{docstring PredTrans.pushOption}
225+
{docstring PredTrans.pushOptionT}
221226

222227
### Weakest Preconditions
223228

@@ -291,7 +296,7 @@ The {name}`WP` interpretation of {name}`Identity` is a plain definition marked {
291296
{TODO}[It is a wart of `vcgen` that we cannot declare the WP instance below. I'll think about fixing it by canonicalizing `WPMonad.toWP` to the `WP` instance, but not in time for 4.35. Let's maybe leave an "under construction" sign for the reader.]
292297
```lean
293298
@[instance_reducible] def Identity.wpInst :
294-
WP (Identity α) α Prop EPost.Nil where
299+
WP (Identity α) α Prop EStack⟨⟩ where
295300
wpTrans x := ⟨fun post _ => post x.run⟩
296301
wp_trans_monotone x := fun _ _ _ _ _ hpost => hpost x.run
297302

@@ -303,7 +308,7 @@ This interpretation alone suffices to state weakest preconditions and to prove a
303308
```lean +error (name := noInst)
304309
theorem Identity.of_run_eq_wp' {x : α} {prog : Identity α}
305310
(h : Identity.run prog = x) (P : α → Prop)
306-
(hwp : wp prog P EPost.Nil.mk) : P x := by
311+
(hwp : wp prog P ()) : P x := by
307312
simp_all [wp, WP.wpTrans, ← h]
308313

309314
theorem rev_correct_bad {xs : List α} :
@@ -321,7 +326,7 @@ No spec applicable to program (forIn xs [] fun x __s => pure (ForInStep.yield (x
321326
```
322327
The issue can be resolved by defining a {name}`WPMonad` instance whose {name}`WPMonad.toWP` field carries the interpretation:
323328
```lean
324-
instance : WPMonad Identity Prop EPost.Nil where
329+
instance : WPMonad Identity Prop EStack⟨⟩ where
325330
toWP _ := Identity.wpInst
326331
pure_le_wp_pure x post epost := PartialOrder.rel_refl
327332
bind_le_wp_bind x f post epost := PartialOrder.rel_refl
@@ -330,7 +335,7 @@ With this instance, and a suitable invariant, {tactic}`vcgen` and the {tactic}`g
330335
```lean
331336
theorem Identity.of_run_eq_wp {x : α} {prog : Identity α}
332337
(h : Identity.run prog = x) (P : α → Prop)
333-
(hwp : wp prog P EPost.Nil.mk) : P x := by
338+
(hwp : wp prog P ()) : P x := by
334339
simp_all [wp, WP.wpTrans, ← h]
335340

336341
theorem rev_correct {xs : List α} :
@@ -451,7 +456,7 @@ The assertion in the precondition is a function because the assertion type of {l
451456
:::
452457
```lean -show -keep
453458
-- Test preceding examples' claims
454-
#synth WP (StateM Nat Unit) Unit (Nat → Prop) EPost.Nil
459+
#synth WP (StateM Nat Unit) Unit (Nat → Prop) EStack⟨⟩
455460
```
456461

457462
## Invariant Specifications
@@ -608,12 +613,12 @@ This operator was designed to model state monads, but {name}`LogM` can be seen a
608613
This appending is visible in the body of the instance, where the initial state and the log that resulted from the action are appended:
609614
```lean
610615
@[instance_reducible]
611-
def LogM.instWP : WP (LogM β α) α (Array β → Prop) EPost.Nil where
616+
def LogM.instWP : WP (LogM β α) α (Array β → Prop) EStack⟨⟩ where
612617
wpTrans | { log, value } => pushArg (fun s => pure (value, s ++ log))
613618
wp_trans_monotone x := fun _ _ _ _ _ hpost s =>
614619
hpost x.value (s ++ x.log)
615620

616-
instance : WPMonad (LogM β) (Array β → Prop) EPost.Nil where
621+
instance : WPMonad (LogM β) (Array β → Prop) EStack⟨⟩ where
617622
toWP _ := LogM.instWP
618623
pure_le_wp_pure x post epost := by simp [wp, WP.wpTrans, pure]
619624
bind_le_wp_bind x f post epost := by simp [wp, WP.wpTrans, bind]
@@ -626,7 +631,7 @@ Semantically, the empty array is the correct choice so as to not place items in
626631
theorem LogM.of_run_eq_wp {α : Type u} {β : Type v}
627632
{x : α × Array β} {prog : LogM β α}
628633
(h : LogM.run prog = x) (P : α × Array β → Prop)
629-
(hwp : wp prog (fun v l => P (v, l)) EPost.Nil.mk #[]) : P x := by
634+
(hwp : wp prog (fun v l => P (v, l)) () #[]) : P x := by
630635
rw [← h]
631636
simp [wp, WP.wpTrans] at hwp
632637
exact hwp

Tutorial/VCGen.lean

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ variable {m : Type u → Type v} [Monad m] {Pred EPred : Type} [Assertion Pred]
768768
```
769769

770770
A {lean}`StateT σ` layer turns the assertion type `Pred` into `σ → Pred` and leaves the exception postcondition type unchanged.
771-
An {lean}`ExceptT ε` layer leaves the assertion type unchanged and turns the exception postcondition type `EPred` into {lean}`EPost.Cons (ε → Pred) EPred`, adding one exception branch of type `ε → Pred`.
771+
An {lean}`ExceptT ε` layer leaves the assertion type unchanged and turns the exception postcondition type `EPred` into {lean}`(ε → Pred) × EPred`, adding one exception branch of type `ε → Pred`.
772772
The {name}`WP` instance for {lean}`EStateM ε σ` uses assertions of type `σ → Prop` and exception postconditions of type `ε → σ → Prop`, mirroring the single exception branch of the equivalent transformer stack {lean}`ExceptT ε (StateM σ)`.
773773
:::
774774

@@ -836,8 +836,8 @@ Supporting this monad in {tactic}`vcgen` is a matter of:
836836
universe u v
837837
variable {α : Type u} {v : α} {e : Error}
838838
```
839-
The {name}`WPMonad` instance for {name}`Result` picks the assertion type {lean}`Prop` because there are no state-like effects, and the exception postcondition type {lean}`EPost⟨Error → Prop` because there is a single exception of type {lean}`Error`.
840-
Its {name}`WP` interpretation translates a program in {lean}`Result α` to a predicate transformer in {lean}`PredTrans Prop EPost⟨Error → Prop α`.
839+
The {name}`WPMonad` instance for {name}`Result` picks the assertion type {lean}`Prop` because there are no state-like effects, and the exception postcondition type {lean}`Error → Prop` because there is a single exception of type {lean}`Error`.
840+
Its {name}`WP` interpretation translates a program in {lean}`Result α` to a predicate transformer in {lean}`PredTrans Prop (Error → Prop) α`.
841841
That is, a function mapping a postcondition and an exception postcondition to the weakest precondition.
842842
Each case of {name}`Result` determines the precondition directly:
843843
* {lean}`Result.ok v` yields the success postcondition at {lean}`v`.
@@ -850,16 +850,15 @@ For {name}`WPMonad.bind_le_wp_bind`, a case split on the program exposes the {ke
850850
::::
851851
```lean
852852
instance Result.instWPMonad :
853-
WPMonad Result Prop EPost⟨Error → Prop where
853+
WPMonad Result Prop (Error → Prop) where
854854
toWP α := {
855855
wpTrans x := ⟨fun post epost =>
856856
match x with
857857
| .ok v => post v
858-
| .fail e => epost.head e
858+
| .fail e => epost e
859859
| .div => False⟩
860860
wp_trans_monotone x := by
861861
intro post post' epost epost' hepost hpost
862-
have hhead := EPost.Cons.le_head hepost
863862
cases x <;> simp_all [PartialOrder.rel]
864863
}
865864
pure_le_wp_pure x post epost := PartialOrder.rel_refl
@@ -873,7 +872,7 @@ Finally, we also prove an adequacy lemma similar to {name}`Except.of_eq_wp` for
873872
```lean
874873
theorem Result.of_eq_wp {α} {x prog : Result α}
875874
(h : prog = x) (P : Result α → Prop)
876-
(hwp : wp prog (fun a => P (.ok a)) epost⟨fun e => P (.fail e)) :
875+
(hwp : wp prog (fun a => P (.ok a)) (fun e => P (.fail e))) :
877876
P x := by
878877
subst h
879878
match prog with
@@ -911,8 +910,8 @@ There are two relevant specification lemmas to register:
911910
```lean
912911
@[spec]
913912
theorem Result.throw_spec {α} {post : α → Prop}
914-
{epost : EPost⟨Error → Prop} (e : Error) :
915-
⦃epost.head e⦄ throw (m := Result) (α := α) e ⦃post; epost⦄ :=
913+
{epost : Error → Prop} (e : Error) :
914+
⦃epost e⦄ throw (m := Result) (α := α) e ⦃post; epost⦄ :=
916915
⟨PartialOrder.rel_refl⟩
917916

918917
@[spec]

lean-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
leanprover/lean4:nightly-2026-08-19
1+
leanprover/lean4:nightly-2026-08-21

0 commit comments

Comments
 (0)