Compare commits

..

50 Commits

Author SHA1 Message Date
Leonardo de Moura
1c81c921e6 fix: E-matching patterns containing ground universe polymorphic patterns in grind
This PR ensures `grind` can E-match patterns containing universe
polymorphic ground sub-patterns. For example, given
```
set_option pp.universes true in
attribute [grind?] Id.run_pure
```
the pattern
```
Id.run_pure.{u_1}: [@Id.run.{u_1} #1 (@pure.{u_1, u_1} `[Id.{u_1}] `[Applicative.toPure.{u_1, u_1}] _ #0)]
```
contains two nested universe polymorphic ground patterns
- `Id.{u_1}`
- `Applicative.toPure.{u_1, u_1}`

This kind of pattern is not common, but it occurs in core.
2025-08-11 13:57:57 -07:00
Leonardo de Moura
5abf4bb651 fix: additional numeral normalization in grind (#9853)
This PR adds `Nat` and `Int` numeral normalizers in `grind`.

closes #9828
2025-08-11 19:13:17 +00:00
Leonardo de Moura
7ea711e043 fix: remove inShareCommon filter used in grind (#9852)
This PR removes the `inShareCommon` quick filter used in `grind`
preprocessing steps. `shareCommon` is no longer used only for fully
preprocessed terms.

closes #9830
2025-08-11 18:24:13 +00:00
Sebastian Graf
b853166575 feat: Deterministic case labels in mvcgen (#9843)
This PR makes `mvcgen` produce deterministic case labels for the
generated VCs. Invariants will be named `inv<n>` and every other VC will
be named `vc<n>.*`, where the `*` part serves as a loose indication of
provenance.
2025-08-11 14:57:59 +00:00
Paul Reichert
0725349bbd feat: high-level order typeclasses (#9729)
This PR introduces a canonical way to endow a type with an order
structure. The basic operations (`LE`, `LT`, `Min`, `Max`, and in later
PRs `BEq`, `Ord`, ...) and any higher-level property (a preorder, a
partial order, a linear order etc.) are then put in relation to `LE` as
necessary. The PR provides `IsLinearOrder` instances for many core types
and updates the signatures of some lemmas.

**BREAKING CHANGES:**

* The requirements of the `lt_of_le_of_lt`/`le_trans` lemmas for
`Vector`, `List` and `Array` are simplified. They now require an
`IsLinearOrder` instance. The new requirements are logically equivalent
to the old ones, but the `IsLinearOrder` instance is not automatically
inferred from the smaller typeclasses.
* Hypotheses of type `Std.Total (¬ · < · : α → α → Prop)` are replaced
with the equivalent class `Std.Asymm (· < · : α → α → Prop)`. Breakage
should be limited because there is now an instance that derives the
latter from the former.
* In `Init.Data.List.MinMax`, multiple theorem signatures are modified,
replacing explicit parameters for antisymmetry, totality, `min_ex_or`
etc. with corresponding instance parameters.
2025-08-11 14:55:17 +00:00
Sebastian Graf
264e451d3c feat: Add @[spec] lemmas for forIn at Std.PRange (#9848)
This PR adds `@[spec]` lemmas for `forIn` and `forIn'` at `Std.PRange`.
2025-08-11 14:34:34 +00:00
Cameron Zwarich
5b5bb5174b fix: check for recursive decls before instance proj inlining (#9847)
This PR adds a check for reursive decls in this bespoke inlining path,
which fixes a regression from the old compiler.

Fixes #9624.
2025-08-11 13:50:26 +00:00
Sofia Rodrigues
14120a519c fix: replace 'D' with 'd' for day representation in long date format (#9799)
This PR fixes the #9410 issue.
2025-08-11 13:17:34 +00:00
Sebastian Graf
2875e8f277 chore: Add Nodup and Fresh tests to doLogicTests.lean (#9837)
Two test cases that will be added to the reference manual
2025-08-11 09:12:38 +00:00
Sebastian Graf
9a0c1ab2d0 feat: Simpler first-order implementation for pure SPreds (#9841)
This PR migrates the ⌜p⌝ notation for embedding pure p : Prop into SPred
σs to expand into a simple, first-order expression SPred.pure p that can
be supported by e-matching in grind.

Doing so deprives ⌜p⌝ notation of its idiom-bracket-like support for
#selector and ‹Nat›ₛ syntax which is thus removed.
2025-08-11 08:32:16 +00:00
Paul Reichert
f15d531acb refactor: reduce omega's dependency on fvar IDs (#9723)
This PR replaces some `HashSet Expr`-typed collections of facts in
`omega`'s implementation with plain lists. This change makes some
`omega` calls faster, some slower, but the advantage is that `omega`'s
performance is more independent the state of the name generator that
produces fvar IDs.

I've created this PR for discussion and am happy to hear opinions on
whether this should be merged or not. A good reason *not* to merge is
that it causes regressions in some places and `grind` is expected to
supersede `omega` either way. A good reason to merge is that `omega` is
used all over the place and its flaky performance increases the noise in
future benchmarks.
2025-08-11 07:17:24 +00:00
Sebastian Graf
e0fcaf5e7d chore: Naming in Invariant.withEarlyReturn (#9835)
Just a small renaming leftover.
2025-08-11 06:43:30 +00:00
Sebastian Graf
1b78d8f0a3 fix: Rewriting in mvcgen when there are excess arguments to wp (#9834)
This PR fixes a bug in `mvcgen` triggered by excess state arguments to
the `wp` application, a situation which arises when working with
`StateT` primitives.
2025-08-11 06:42:08 +00:00
Sebastian Graf
66772d77fc fix: Work around a DefEq bug in mspec involving delayed assignments (#9833)
This PR works around a DefEq bug in `mspec` involving delayed
assignments.
2025-08-11 06:40:19 +00:00
Sebastian Graf
d64637e8c7 fix: Add simp lemmas SPred.entails_<n> to replace SPred.entails_cons (#9832)
This PR adds simp lemmas `SPred.entails_<n>` to replace
`SPred.entails_cons` which was disfunctional as a simp lemma due to
#8074.
2025-08-11 06:38:33 +00:00
Sebastian Graf
02fa9641fd feat: Add delaborator for Std.Range (#9831)
This PR adds a delaborator for `Std.Range` notation.
2025-08-11 06:36:26 +00:00
Cameron Zwarich
4506173a27 fix: support overapplication of Quot.lift in the compiler (#9827)
This PR changes the lowering of `Quot.lcInv` (the compiler-internal form
of `Quot.lift`) in `toMono` to support overapplication.

Fixes #9806.
2025-08-11 01:51:54 +00:00
Kyle Miller
20eea7372f feat: make delta deriving more robust and handle binders (#9800)
This PR improves the delta deriving handler, giving it the ability to
process definitions with binders, as well as the ability to recursively
unfold definitions. Furthermore, delta deriving now tries all explicit
non-out-param arguments to a class, and it can handle "mixin" instance
arguments. The `deriving` syntax has been changed to accept general
terms, which makes it possible to derive specific instances with for
example `deriving OfNat _ 1` or `deriving Module R`. The class is
allowed to be a pi type, to add additional hypotheses; here is a Mathlib
example:
```lean
def Sym (α : Type*) (n : ℕ) :=
  { s : Multiset α // Multiset.card s = n }
deriving [DecidableEq α] → DecidableEq _
```
This underscore stands for where `Sym α n` may be inserted, which is
necessary when `→` is used. The `deriving instance` command can refer to
scoped variables when delta deriving as well. Breaking change: the
derived instance's name uses the `instance` command's name generator,
and the new instance is added to the current namespace.

This closes
[mathlib4#380](https://github.com/leanprover-community/mathlib4/issues/380).
2025-08-10 21:21:54 +00:00
Mac Malone
79f6bb6f54 refactor: lake: reorganize tests/module (#9824)
This PR reorganizes the directory structure of Lake's module test and
renames some of the files to be more descriptive.

Originally, this was meant to be combined with a fix, but that fix
appears to be incorrect, so this is just a refactor.
2025-08-10 19:16:55 +00:00
Kyle Miller
fc076c5acc fix: get DecidableEq deriving handler to work for enumerations in higher universes (#9818)
This PR fixes a bug where the `DecidableEq` deriving handler did not
take universe levels into account for enumerations (inductive types
whose constructors all have no fields). Closes #9541.
2025-08-10 16:29:02 +00:00
Henrik Böving
44d3cfb3dc chore: stabilize benchmark output (#9820) 2025-08-10 10:53:38 +00:00
Sebastian Ullrich
0985326b2e chore: remove unnecessary withoutExporting use (#9821) 2025-08-10 10:20:31 +00:00
Kyle Miller
cbeef963a9 fix: have unsafe term produce an opaqueDecl (#9819)
This PR makes the `unsafe t` term create an auxiliary opaque
declaration, rather than an auxiliary definition with opaque
reducibility hints.
2025-08-10 09:30:55 +00:00
Cameron Zwarich
544f9912b7 chore: add separate profiling entries for base, mono, and IR phases (#9817) 2025-08-10 05:00:49 +00:00
Cameron Zwarich
361ca788a7 refactor: split the LCNF pass list into separate base/mono lists (#9816)
This will make it easier to run the two phases in parallel.
2025-08-10 04:23:19 +00:00
Leonardo de Moura
68a249d23d perf: normalizeLevels in grind (#9814)
This PR skips the `normalizeLevels` preprocessing step in `grind` when
it is not needed.
2025-08-10 00:51:20 +00:00
Leonardo de Moura
95c8f1f866 fix: unfoldReducible in grind (#9813)
This PR fixes an unexpected bound variable panic in `unfoldReducible`
used in `grind`.
2025-08-10 00:02:05 +00:00
Leonardo de Moura
fa17ea2715 chore: include generation in grind.internalize trace message (#9812) 2025-08-09 23:48:43 +00:00
Sebastian Ullrich
c970c74d66 feat: introduce Lean.realizeValue for sharing computation results between compatible environment branches (#9798)
This PR introduces `Lean.realizeValue`, a new metaprogramming API for
parallelism-aware caching of `MetaM` computations
2025-08-09 17:19:29 +00:00
Leonardo de Moura
479da83f57 feat: grind annotation analyzer (#9809)
This PR adds a script for analyzing `grind` E-matching annotations. The
script is useful for detecting matching loops. We plan to add
user-facing commands for running the script in the future.
2025-08-09 17:14:57 +00:00
Yaël Dillies
feca9e8103 fix: allow trailing comma in the arg list of simp?, dsimp?, simpa, etc (#9804)
This PR allows trailing comma in the argument list of `simp?`, `dsimp?`,
`simpa`, etc... Previously, it was only allowed in the non `?` variants
of `simp`, `dsimp`, `simp_all`.

Closes #7383.
2025-08-09 16:37:30 +00:00
Leonardo de Moura
a041ffa702 chore: remove leftover (#9808) 2025-08-09 15:58:50 +00:00
Sebastian Graf
5eafc080e1 feat: Simplify Std.List.Zipper.pref using mleave (#9807)
This PR adds `Std.List.Zipper.pref` to the simp set of `mleave`.
2025-08-09 15:57:47 +00:00
Sebastian Graf
8558b2d278 feat: Improved API for invariants and postconditions (#9805)
This PR improves the API for invariants and postconditions and as such
introduces a few breaking changes to the existing pre-release API around
`Std.Do`. It also adds Markus Himmel's `pairsSumToZero` example as a
test case.
2025-08-09 14:42:37 +00:00
Cameron Zwarich
756f837f82 perf: reduce redundant inc/dec using "implied borrows" from projections and liveness (#9801)
This PR changes the IR RC pass to take "implied borrows" from
projections into account. If a projected value's lifetime is contained
in that of its parent (or any projection ancestor), then it does not
need its reference count incremented (or later decremented).

I believe that this same technique should generalize to both the
reset/reuse and borrow signature inference passes.
2025-08-09 14:13:50 +00:00
Sebastian Ullrich
0b838ff2c9 chore: update stage0 2025-08-09 12:35:07 +02:00
Sebastian Ullrich
ca43608aa0 feat: allow combining private/public and protected 2025-08-09 12:35:07 +02:00
Rob23oba
ad471b46b8 fix: Inhabited instance of StdGen (#9782)
This PR corrects the `Inhabited` instance of `StdGen` to use a valid
initial state for the pseudorandom number generator. Previously, the
`default` generator had the property that `Prod.snd (stdNext default) =
default`, so it would produce only constant sequences.

[Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/113489-new-members/topic/inhabited.20instance.20for.20StdGen.20isn't.20very.20random/with/533247146)
2025-08-08 06:23:48 +00:00
Kim Morrison
e6b357e87a chore: @[expose] List.mapIdxM (#9794) 2025-08-08 04:55:50 +00:00
Kim Morrison
b676fb1164 fix: @[expose] String.firstDiffPos and String.extract (#9792)
This PR adds `@[expose]` to two definitions with `where` clauses that
Batteries proves theorems about.
2025-08-08 04:55:45 +00:00
Kim Morrison
ca68b84623 chore: @[expose] List.filterMapTR (#9793)
This PR adds `@[expose]`, as Batteries wants access to the `where`
clause.
2025-08-08 04:55:38 +00:00
Kim Morrison
d6bc78dcb8 feat: split out Expr.getMVarDependencies from MVarId.getMVarDependencies (#9785)
This PR splits out an implementation detail of
MVarId.getMVarDependencies into a top-level function. Aesop was relying
on the function defined in the where clause, which is no longer possible
after #9759.
2025-08-08 00:28:30 +00:00
Cameron Zwarich
2104fd7da9 chore: remove unused default (#9791) 2025-08-07 16:27:23 +00:00
Kyle Miller
c801a9e8cf feat: use the metavariable index when pretty printing (#9778)
This PR modifies the pretty printing of anonymous metavariables to use
the index rather than the internal name. This leads to smaller numerical
suffixes in `?m.123` since the indices are numbered within a given
metavariable context rather than across an entire file, hence each
command gets its own numbering. This does not yet affect pretty printing
of universe level metavariables.

For debugging purposes, metavariables that are not defined now pretty
print as `?_mvar.123` rather than cause pretty printing to fail.
2025-08-07 15:58:51 +00:00
Sebastian Ullrich
c9a6446041 chore: CI: include tests in rebootstrap check (#9788) 2025-08-07 15:37:36 +00:00
Cameron Zwarich
a2f24fac65 chore: use unreachable! for unreachable cases, not silent fallback (#9790) 2025-08-07 15:23:01 +00:00
Cameron Zwarich
eaec888dc3 refactor: add isPossibleRef/isDefiniteRef fields to RC VarInfo (#9789)
These are the only uses of the existing `type` field, so we might as
well compute them up-front and store them.
2025-08-07 14:21:19 +00:00
Sebastian Graf
69d8cca38a feat: Add a simp lemma for PostCond.const (#9787)
This PR adds a simp lemma `PostCond.const_apply`.
2025-08-07 13:15:22 +00:00
Sebastian Graf
04a3968206 chore: Move withFreshUserNames to Lean/Meta/Basic.lean (#9783)
This PR generalizes and moves `withFreshUserNames` to
Lean/Meta/Basic.lean where it can be reused.
2025-08-07 10:27:52 +00:00
Sebastian Graf
ae699a6b13 fix: proper hygiene for goals generated by mvcgen (#9781)
This PR ensures that `mvcgen` is hygienic. The goals it generates should
now introduce all locals inaccessibly.
2025-08-07 09:33:06 +00:00
1053 changed files with 4117 additions and 1318 deletions

View File

@@ -205,7 +205,7 @@ jobs:
id: test
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml ${{ matrix.CTARGET_OPTIONS }}
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2
@@ -235,9 +235,13 @@ jobs:
if: matrix.test-speedcenter
- name: Check rebootstrap
run: |
set -e
# clean rebuild in case of Makefile changes/Lake does not detect uncommited stage 0
# changes yet
make -C build update-stage0 && make -C build/stage1 clean-stdlib && make -C build -j$NPROC
make -C build update-stage0
make -C build/stage1 clean-stdlib
time make -C build -j$NPROC
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC
if: matrix.check-rebootstrap
- name: CCache stats
if: always()

View File

@@ -0,0 +1,87 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean
namespace Lean.Meta.Grind.Analyzer
/-!
A simple E-matching annotation analyzer.
For each theorem annotated as an E-matching candidate, it creates an artificial goal, executes `grind` and shows the
number of instances created.
For a theorem of the form `params -> type`, the artificial goal is of the form `params -> type -> False`.
-/
/--
`grind` configuration for the analyzer. We disable case-splits and lookahead,
increase the number of generations, and limit the number of instances generated.
-/
def config : Grind.Config := {
splits := 0
lookahead := false
mbtc := false
ematch := 20
instances := 100
gen := 10
}
structure Config where
/-- Minimum number of instantiations to trigger summary report -/
min : Nat := 10
/-- Minimum number of instantiations to trigger detailed report -/
detailed : Nat := 50
def mkParams : MetaM Params := do
let params Grind.mkParams config
let ematch getEMatchTheorems
let casesTypes Grind.getCasesTypes
return { params with ematch, casesTypes }
/-- Returns the total number of generated instances. -/
private def sum (cs : PHashMap Origin Nat) : Nat := Id.run do
let mut r := 0
for (_, c) in cs do
r := r + c
return r
private def thmsToMessageData (thms : PHashMap Origin Nat) : MetaM MessageData := do
let data := thms.toArray.filterMap fun (origin, c) =>
match origin with
| .decl declName => some (declName, c)
| _ => none
let data := data.qsort fun (d₁, c₁) (d₂, c₂) => if c₁ == c₂ then Name.lt d₁ d₂ else c₁ > c₂
let data data.mapM fun (declName, counter) =>
return .trace { cls := `thm } m!"{.ofConst (← mkConstWithLevelParams declName)} ↦ {counter}" #[]
return .trace { cls := `thm } "instances" data
/--
Analyzes theorem `declName`. That is, creates the artificial goal based on `declName` type,
and invokes `grind` on it.
-/
def analyzeEMatchTheorem (declName : Name) (c : Config) : MetaM Unit := do
let info getConstInfo declName
let mvarId forallTelescope info.type fun _ type => do
withLocalDeclD `h type fun _ => do
return ( mkFreshExprMVar (mkConst ``False)).mvarId!
let result Grind.main mvarId ( mkParams) (pure ())
let thms := result.counters.thm
let s := sum thms
if s > c.min then
IO.println s!"{declName} : {s}"
if s > c.detailed then
logInfo m!"{declName}\n{← thmsToMessageData thms}"
/-- Analyzes all theorems in the standard library marked as E-matching theorems. -/
def analyzeEMatchTheorems (c : Config := {}) : MetaM Unit := do
let origins := ( getEMatchTheorems).getOrigins
for o in origins do
let .decl declName := o | pure ()
analyzeEMatchTheorem declName c
set_option maxHeartbeats 5000000
run_meta analyzeEMatchTheorems
-- We can analyze specific theorems using commands such as
set_option trace.grind.ematch.instance true in
run_meta analyzeEMatchTheorem ``List.filterMap_some {}

View File

@@ -49,5 +49,6 @@ public import Init.Data.Vector
public import Init.Data.Iterators
public import Init.Data.Range.Polymorphic
public import Init.Data.Slice
public import Init.Data.Order
public section

View File

@@ -12,9 +12,12 @@ public import Init.Data.Array.Lemmas
public import Init.Data.List.Lex
import Init.Data.Range.Polymorphic.Lemmas
import Init.Data.Range.Polymorphic.NatLemmas
import Init.Data.Order.Lemmas
public section
open Std
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -28,8 +31,8 @@ namespace Array
@[simp] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList xs < ys := Iff.rfl
@[simp] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ys.toList xs ys := Iff.rfl
grind_pattern _root_.List.lt_toArray => l₁.toArray < l₂.toArray
grind_pattern _root_.List.le_toArray => l₁.toArray l₂.toArray
grind_pattern _root_.List.lt_toArray => l₁.toArray < l₂.toArray
grind_pattern _root_.List.le_toArray => l₁.toArray l₂.toArray
grind_pattern lt_toList => xs.toList < ys.toList
grind_pattern le_toList => xs.toList ys.toList
@@ -100,6 +103,14 @@ theorem singleton_lex_singleton [BEq α] {lt : αα → Bool} : #[a].lex #[
xs.toList.lex ys.toList lt = xs.lex ys lt := by
cases xs <;> cases ys <;> simp
instance [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α] : IsLinearOrder (Array α) := by
apply IsLinearOrder.of_le
· constructor
intro _ _ hab hba
simpa using Std.le_antisymm (α := List α) hab hba
· constructor; exact Std.le_trans (α := List α)
· constructor; exact fun _ _ => Std.le_total (α := List α)
protected theorem lt_irrefl [LT α] [Std.Irrefl (· < · : α α Prop)] (xs : Array α) : ¬ xs < xs :=
List.lt_irrefl xs.toList
@@ -131,27 +142,35 @@ instance [LT α] [Trans (· < · : αα → Prop) (· < ·) (· < ·)] :
Trans (· < · : Array α Array α Prop) (· < ·) (· < ·) where
trans h₁ h₂ := Array.lt_trans h₁ h₂
protected theorem lt_of_le_of_lt [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
protected theorem lt_of_le_of_lt [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
Std.lt_of_le_of_lt (α := List α) h₁ h₂
@[deprecated Array.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
List.lt_of_le_of_lt h₁ h₂
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
protected theorem le_trans [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
fun h₃ => h₁ (Array.lt_of_le_of_lt h₂ h₃)
instance [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)] :
@[deprecated Array.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.le_trans h₁ h₂
instance [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α] :
Trans (· · : Array α Array α Prop) (· ·) (· ·) where
trans h₁ h₂ := Array.le_trans h₁ h₂
@@ -165,7 +184,7 @@ instance [LT α]
asymm _ _ := Array.lt_asymm
protected theorem le_total [LT α]
[i : Std.Total (¬ · < · : α α Prop)] (xs ys : Array α) : xs ys ys xs :=
[i : Std.Asymm (· < · : α α Prop)] (xs ys : Array α) : xs ys ys xs :=
List.le_total xs.toList ys.toList
@[simp] protected theorem not_lt [LT α]
@@ -175,19 +194,22 @@ protected theorem le_total [LT α]
{xs ys : Array α} : ¬ ys xs xs < ys := Classical.not_not
protected theorem le_of_lt [LT α]
[i : Std.Total (¬ · < · : α α Prop)]
[i : Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} (h : xs < ys) : xs ys :=
List.le_of_lt h
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Total (¬ · < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} : xs ys xs < ys xs = ys := by
simpa using List.le_iff_lt_or_eq (l₁ := xs.toList) (l₂ := ys.toList)
instance [LT α]
[Std.Total (¬ · < · : α α Prop)] :
protected theorem le_antisymm [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{xs ys : Array α} : xs ys ys xs xs = ys := by
simpa using List.le_antisymm (as := xs.toList) (bs := ys.toList)
instance [LT α] [Std.Asymm (· < · : α α Prop)] :
Std.Total (· · : Array α Array α Prop) where
total := Array.le_total
@@ -266,7 +288,6 @@ protected theorem lt_iff_exists [LT α] {xs ys : Array α} :
simp [List.lt_iff_exists]
protected theorem le_iff_exists [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {xs ys : Array α} :
xs ys
@@ -286,7 +307,6 @@ theorem append_left_lt [LT α] {xs ys zs : Array α} (h : ys < zs) :
simpa using List.append_left_lt h
theorem append_left_le [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
{xs ys zs : Array α} (h : ys zs) :
@@ -310,10 +330,8 @@ protected theorem map_lt [LT α] [LT β]
simpa using List.map_lt w h
protected theorem map_le [LT α] [LT β]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Irrefl (· < · : β β Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
{xs ys : Array α} {f : α β} (w : x y, x < y f x < f y) (h : xs ys) :

View File

@@ -19,9 +19,12 @@ public import Init.Data.Int.LemmasAux
public import Init.Data.Int.Pow
public import Init.Data.Int.LemmasAux
public import Init.Data.BitVec.Bootstrap
public import Init.Data.Order.Factories
public section
open Std
set_option linter.missingDocs true
namespace BitVec
@@ -4015,6 +4018,16 @@ protected theorem ne_of_lt {x y : BitVec n} : x < y → x ≠ y := by
simp only [lt_def, ne_eq, toNat_eq]
apply Nat.ne_of_lt
instance instIsLinearOrder : IsLinearOrder (BitVec n) := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply BitVec.le_antisymm
case le_trans => constructor; apply BitVec.le_trans
case le_total => constructor; apply BitVec.le_total
instance instLawfulOrderLT : LawfulOrderLT (BitVec n) := by
apply LawfulOrderLT.of_le
simpa using fun _ _ => BitVec.lt_asymm
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y x % y < y := by
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
apply Nat.mod_lt

View File

@@ -8,5 +8,6 @@ module
prelude
public import Init.Data.Char.Basic
public import Init.Data.Char.Lemmas
public import Init.Data.Char.Order
public section

View File

@@ -61,6 +61,7 @@ instance leTotal : Std.Total (· ≤ · : Char → Char → Prop) where
total := Char.le_total
-- This instance is useful while setting up instances for `String`.
@[deprecated ltAsymm (since := "2025-08-01")]
def notLTTotal : Std.Total (¬ · < · : Char Char Prop) where
total := fun x y => by simpa using Char.le_total y x

View File

@@ -0,0 +1,27 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Char.Basic
import Init.Data.Char.Lemmas
public import Init.Data.Order.Factories
open Std
namespace Char
public instance instIsLinearOrder : IsLinearOrder Char := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Char.le_antisymm
case le_trans => constructor; apply Char.le_trans
case le_total => constructor; apply Char.le_total
public instance : LawfulOrderLT Char where
lt_iff a b := by
simp [ Char.not_le, Decidable.imp_iff_not_or, Std.Total.total]
end Char

View File

@@ -12,9 +12,13 @@ public import Init.Ext
public import Init.ByCases
public import Init.Conv
public import Init.Omega
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
namespace Fin
@[simp] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@@ -251,6 +255,16 @@ protected theorem le_antisymm_iff {x y : Fin n} : x = y ↔ x ≤ y ∧ y ≤ x
protected theorem le_antisymm {x y : Fin n} (h1 : x y) (h2 : y x) : x = y :=
Fin.le_antisymm_iff.2 h1, h2
instance instIsLinearOrder : IsLinearOrder (Fin n) := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Fin.le_antisymm
case le_total => constructor; apply Fin.le_total
case le_trans => constructor; apply Fin.le_trans
instance : LawfulOrderLT (Fin n) where
lt_iff := by
simp [ Fin.not_le, Decidable.imp_iff_not_or, Std.Total.total]
@[simp, grind =] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Data.Int.Lemmas
public import Init.ByCases
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
/-!
# Results about the order properties of the integers, and the integers as an ordered ring.
-/
@@ -1415,4 +1419,14 @@ theorem natAbs_eq_iff_mul_eq_zero : natAbs a = n ↔ (a - n) * (a + n) = 0 := by
@[deprecated natAbs_eq_iff_mul_eq_zero (since := "2025-03-11")]
abbrev eq_natAbs_iff_mul_eq_zero := @natAbs_eq_iff_mul_eq_zero
instance instIsLinearOrder : IsLinearOrder Int := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int.le_antisymm
case le_total => constructor; apply Int.le_total
case le_trans => constructor; apply Int.le_trans
instance : LawfulOrderLT Int where
lt_iff := by
simp [ Int.not_le, Decidable.imp_iff_not_or, Std.Total.total]
end Int

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Control.Lawful.Basic
public import Init.Data.Subtype
public import Init.Data.Subtype.Basic
public import Init.PropLemmas
public section

View File

@@ -8,7 +8,7 @@ module
prelude
public import all Init.Data.List.Lemmas -- for dsimping with `getElem?_cons_succ`
public import Init.Data.List.Count
public import Init.Data.Subtype
public import Init.Data.Subtype.Basic
public import Init.BinderNameHint
public section

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Data.List.Lemmas
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -18,6 +22,11 @@ namespace List
/-! ### Lexicographic ordering -/
instance [LT α] [Std.Asymm (α := List α) (· < ·)] : LawfulOrderLT (List α) where
lt_iff := by
simp only [LE.le, List.le, Classical.not_not, iff_and_self]
apply Std.Asymm.asymm
@[simp] theorem lex_lt [LT α] {l₁ l₂ : List α} : Lex (· < ·) l₁ l₂ l₁ < l₂ := Iff.rfl
@[simp] theorem not_lex_lt [LT α] {l₁ l₂ : List α} : ¬ Lex (· < ·) l₁ l₂ l₂ l₁ := Iff.rfl
@@ -79,7 +88,6 @@ theorem not_cons_lex_cons_iff [DecidableEq α] [DecidableRel r] {a b} {l₁ l₂
rw [cons_lex_cons_iff, not_or, Decidable.not_and_iff_or_not, and_or_left]
theorem cons_le_cons_iff [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
{a b} {l₁ l₂ : List α} :
@@ -101,19 +109,22 @@ theorem cons_le_cons_iff [LT α]
exact i₂.antisymm _ _ h₃ h₁, h₂
· rintro (h | h₁, h₂)
· left
exact i₁.asymm _ _ h, fun w => i₀.irrefl _ (w h)
exact i₁.asymm _ _ h, fun w => Irrefl.irrefl _ (w h)
· right
exact fun w => i₀.irrefl _ (h₁ w), h₂
exact fun w => Irrefl.irrefl _ (h₁ w), h₂
theorem not_lt_of_cons_le_cons [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
{a b : α} {l₁ l₂ : List α} (h : a :: l₁ b :: l₂) : ¬ b < a := by
rw [cons_le_cons_iff] at h
rcases h with h | rfl, h
· exact i₁.asymm _ _ h
· exact i₀.irrefl _
· exact Irrefl.irrefl _
theorem left_le_left_of_cons_le_cons [LT α] [LE α] [IsLinearOrder α]
[LawfulOrderLT α] {a b : α} {l₁ l₂ : List α} (h : a :: l₁ b :: l₂) : a b := by
simpa [not_lt] using not_lt_of_cons_le_cons h
theorem le_of_cons_le_cons [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
@@ -165,11 +176,7 @@ instance [LT α] [Trans (· < · : αα → Prop) (· < ·) (· < ·)] :
protected theorem lt_of_le_of_lt [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
protected theorem lt_of_le_of_lt [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ < l₃) : l₁ < l₃ := by
induction h₂ generalizing l₁ with
| nil => simp_all
@@ -179,11 +186,8 @@ protected theorem lt_of_le_of_lt [LT α]
| nil => simp_all
| cons c l₁ =>
apply Lex.rel
replace h₁ := not_lt_of_cons_le_cons h₁
apply Classical.byContradiction
intro h₂
have := i₃.trans h₁ h₂
contradiction
replace h₁ := left_le_left_of_cons_le_cons h₁
exact lt_of_le_of_lt h₁ hab
| cons w₃ ih =>
rename_i a as bs
cases l₁ with
@@ -193,21 +197,34 @@ protected theorem lt_of_le_of_lt [LT α]
by_cases w₅ : a = c
· subst w₅
exact Lex.cons (ih (le_of_cons_le_cons h₁))
· exact Lex.rel (Classical.byContradiction fun w₆ => w₅ (i₂.antisymm _ _ w₄ w₆))
· simp only [not_lt] at w₄
exact Lex.rel (lt_of_le_of_ne w₄ (w₅.imp Eq.symm))
protected theorem le_trans [LT α]
[Std.Irrefl (· < · : α α Prop)]
@[deprecated List.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ < l₃) : l₁ < l₃ :=
letI : LE α := .ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
List.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ l₃) : l₁ l₃ :=
fun h₃ => h₁ (List.lt_of_le_of_lt h₂ h₃)
@[deprecated List.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{l₁ l₂ l₃ : List α} (h₁ : l₁ l₂) (h₂ : l₂ l₃) : l₁ l₃ :=
fun h₃ => h₁ (List.lt_of_le_of_lt h₂ h₃)
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
List.le_trans h₁ h₂
instance [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)] :
instance [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α] :
Trans (· · : List α List α Prop) (· ·) (· ·) where
trans h₁ h₂ := List.le_trans h₁ h₂
@@ -247,14 +264,21 @@ theorem not_lex_total {r : αα → Prop}
obtain (_ | _) := not_lex_total h l₁ l₂ <;> contradiction
protected theorem le_total [LT α]
[i : Std.Total (¬ · < · : α α Prop)] (l₁ l₂ : List α) : l₁ l₂ l₂ l₁ :=
not_lex_total i.total l₂ l₁
[i : Std.Asymm (· < · : α α Prop)] (l₁ l₂ : List α) : l₁ l₂ l₂ l₁ :=
not_lex_total i.total_not.total l₂ l₁
instance [LT α]
[Std.Total (¬ · < · : α α Prop)] :
protected theorem le_total_of_asymm [LT α]
[i : Std.Asymm (· < · : α α Prop)] (l₁ l₂ : List α) : l₁ l₂ l₂ l₁ :=
List.le_total l₁ l₂
instance [LT α] [Std.Asymm (· < · : α α Prop)] :
Std.Total (· · : List α List α Prop) where
total := List.le_total
@[no_expose]
instance instIsLinearOrder [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α] :
IsLinearOrder (List α) := IsLinearOrder.of_le
@[simp] protected theorem not_lt [LT α]
{l₁ l₂ : List α} : ¬ l₁ < l₂ l₂ l₁ := Iff.rfl
@@ -262,7 +286,7 @@ instance [LT α]
{l₁ l₂ : List α} : ¬ l₂ l₁ l₁ < l₂ := Classical.not_not
protected theorem le_of_lt [LT α]
[i : Std.Total (¬ · < · : α α Prop)]
[i : Std.Asymm (· < · : α α Prop)]
{l₁ l₂ : List α} (h : l₁ < l₂) : l₁ l₂ := by
obtain (h' | h') := List.le_total l₁ l₂
· exact h'
@@ -272,7 +296,7 @@ protected theorem le_of_lt [LT α]
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Total (¬ · < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
{l₁ l₂ : List α} : l₁ l₂ l₁ < l₂ l₁ = l₂ := by
constructor
· intro h
@@ -456,7 +480,6 @@ protected theorem lt_iff_exists [LT α] {l₁ l₂ : List α} :
simp
protected theorem le_iff_exists [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {l₁ l₂ : List α} :
l₁ l₂
@@ -480,7 +503,6 @@ theorem append_left_lt [LT α] {l₁ l₂ l₃ : List α} (h : l₂ < l₃) :
| cons a l₁ ih => simp [cons_lt_cons_iff, ih]
theorem append_left_le [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
{l₁ l₂ l₃ : List α} (h : l₂ l₃) :
@@ -514,10 +536,8 @@ protected theorem map_lt [LT α] [LT β]
simp [cons_lt_cons_iff, w, h]
protected theorem map_le [LT α] [LT β]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Irrefl (· < · : β β Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
{l₁ l₂ : List α} {f : α β} (w : x y, x < y f x < f y) (h : l₁ l₂) :

View File

@@ -61,7 +61,7 @@ proof that the index is valid.
`List.mapIdxM` is a variant that does not provide the function with evidence that the index is
valid.
-/
@[inline] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) :=
@[inline, expose] def mapFinIdxM [Monad m] (as : List α) (f : (i : Nat) α (h : i < as.length) m β) : m (List β) :=
go as #[] (by simp)
where
/-- Auxiliary for `mapFinIdxM`:
@@ -78,7 +78,7 @@ found, returning the list of results.
`List.mapFinIdxM` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where
@[inline, expose] def mapIdxM [Monad m] (f : Nat α m β) (as : List α) : m (List β) := go as #[] where
/-- Auxiliary for `mapIdxM`:
`mapIdxM.go [a₀, a₁, ...] acc = acc.toList ++ [f acc.size a₀, f (acc.size + 1) a₁, ...]` -/
@[specialize] go : List α Array β m (List β)

View File

@@ -8,9 +8,14 @@ module
prelude
public import Init.Data.List.Lemmas
public import Init.Data.List.Pairwise
public import Init.Data.Order.Factories
public import Init.Data.Subtype.Order
import Init.Data.Order.Lemmas
public section
open Std
/-!
# Lemmas about `List.min?` and `List.max?.
-/
@@ -55,7 +60,7 @@ theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
have hx : min x y = x := rel_of_pairwise_cons h mem_cons_self
rw [foldl_cons, ih _ (hx.symm h.sublist (by simp)), hx]
theorem min?_mem [Min α] (min_eq_or : a b : α, min a b = a min a b = b) :
theorem min?_mem [Min α] [MinEqOr α] :
{xs : List α} xs.min? = some a a xs := by
intro xs
match xs with
@@ -72,13 +77,10 @@ theorem min?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a min a b = b
have p := ind _ eq
cases p with
| inl p =>
cases min_eq_or x y with | _ q => simp [p, q]
cases MinEqOr.min_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem le_min?_iff [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c) :
theorem le_min?_iff [Min α] [LE α] [LawfulOrderInf α] :
{xs : List α} xs.min? = some a {x}, x a b, b xs x b
| nil => by simp
| cons x xs => by
@@ -93,34 +95,60 @@ theorem le_min?_iff [Min α] [LE α]
simp at eq
simp [ih _ eq, le_min_iff, and_assoc]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`.
theorem min?_eq_some_iff [Min α] [LE α]
(le_refl : a : α, a a)
(min_eq_or : a b : α, min a b = a min a b = b)
(le_min_iff : a b c : α, a min b c a b a c) {xs : List α}
(anti : a b, a xs b xs a b b a a = b := by
exact fun a b _ _ => Std.Antisymm.antisymm a b) :
theorem min?_eq_some_iff [Min α] [LE α] {xs : List α} [IsLinearOrder α] [LawfulOrderMin α] :
xs.min? = some a a xs b, b xs a b := by
refine fun h => min?_mem min_eq_or h, (le_min?_iff le_min_iff h).1 (le_refl _), ?_
refine fun h => min?_mem h, (le_min?_iff h).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti _ _ (min?_mem min_eq_or rfl) h₁
((le_min?_iff le_min_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem min_eq_or (xs := x::xs) rfl))
rw [List.min?]
exact congrArg some <| le_antisymm
((le_min?_iff (xs := x :: xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem (xs := x :: xs) rfl))
theorem min?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
private theorem min?_attach [Min α] [MinEqOr α] {xs : List α} :
xs.attach.min? = (xs.min?.pmap (fun m hm => m, min?_mem hm) (fun _ => id)) := by
cases xs with
| nil => simp
| cons x xs =>
simp only [min?, attach_cons, Option.some.injEq, Option.pmap_some]
rw [foldl_map]
simp only [Subtype.ext_iff]
rw [ foldl_attach (l := xs)]
apply Eq.trans (foldl_hom (f := Subtype.val) ?_).symm
· rfl
· intros; rfl
theorem min?_eq_min?_attach [Min α] [MinEqOr α] {xs : List α} :
xs.min? = (xs.attach.min?.map Subtype.val) := by
simp [min?_attach, Option.map_pmap]
theorem min?_eq_some_iff_subtype [Min α] [LE α] {xs : List α}
[MinEqOr α] [IsLinearOrder (Subtype (· xs))] [LawfulOrderMin (Subtype (· xs))] :
xs.min? = some a a xs b, b xs a b := by
have := fun a => min?_eq_some_iff (xs := xs.attach) (a := a)
rw [min?_eq_min?_attach]
simp [min?_eq_some_iff]
constructor
· rintro ha, h
exact ha, h
· rintro ha, h
exact ha, h
theorem min?_replicate [Min α] [Std.IdempotentOp (min : α α α)] {n : Nat} {a : α} :
(replicate n a).min? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons']
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons', Std.IdempotentOp.idempotent]
@[simp] theorem min?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
@[simp] theorem min?_replicate_of_pos [Min α] [MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).min? = some a := by
simp [min?_replicate, Nat.ne_of_gt h, w]
simp [min?_replicate, Nat.ne_of_gt h]
/--
Requirements are satisfied for `[OrderData α] [Min α] [IsLinearOrder α] [LawfulOrderMin α]`
-/
theorem foldl_min [Min α] [Std.IdempotentOp (min : α α α)] [Std.Associative (min : α α α)]
{l : List α} {a : α} : l.foldl (init := a) min = min a (l.min?.getD a) := by
cases l <;> simp [min?, foldl_assoc, Std.IdempotentOp.idempotent]
@@ -144,54 +172,120 @@ theorem isSome_max?_of_mem {l : List α} [Max α] {a : α} (h : a ∈ l) :
l.max?.isSome := by
cases l <;> simp_all [max?_cons']
theorem max?_mem [Max α] (min_eq_or : a b : α, max a b = a max a b = b) :
{xs : List α} xs.max? = some a a xs
| nil => by simp
| cons x xs => by
rw [max?]; rintro
induction xs generalizing x with simp at *
| cons y xs ih =>
rcases ih (max x y) with h | h <;> simp [h]
simp [ or_assoc, min_eq_or x y]
-- See also `Init.Data.List.Nat.Basic` for specialisations of the next two results to `Nat`.
theorem max?_le_iff [Max α] [LE α]
(max_le_iff : a b c : α, max b c a b a c a) :
{xs : List α} xs.max? = some a {x}, a x b xs, b x
| nil => by simp
| cons x xs => by
rw [max?]; rintro y
induction xs generalizing x with
theorem max?_eq_head? {α : Type u} [Max α] {l : List α}
(h : l.Pairwise (fun a b => max a b = a)) : l.max? = l.head? := by
cases l with
| nil => rfl
| cons x l =>
rw [head?_cons, max?_cons', Option.some.injEq]
induction l generalizing x with
| nil => simp
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
| cons y l ih =>
have hx : max x y = x := rel_of_pairwise_cons h mem_cons_self
rw [foldl_cons, ih _ (hx.symm h.sublist (by simp)), hx]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
-- and `le_min_iff`.
theorem max?_eq_some_iff [Max α] [LE α] [anti : Std.Antisymm (· · : α α Prop)]
(le_refl : a : α, a a)
(max_eq_or : a b : α, max a b = a max a b = b)
(max_le_iff : a b c : α, max b c a b a c a) {xs : List α} :
xs.max? = some a a xs b xs, b a := by
refine fun h => max?_mem max_eq_or h, (max?_le_iff max_le_iff h).1 (le_refl _), ?_
theorem max?_mem [Max α] [MaxEqOr α] :
{xs : List α} xs.max? = some a a xs := by
intro xs
match xs with
| nil => simp
| x :: xs =>
simp only [max?_cons', Option.some.injEq, mem_cons]
intro eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons y xs ind =>
simp at eq
have p := ind _ eq
cases p with
| inl p =>
cases MaxEqOr.max_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
theorem max?_le_iff [Max α] [LE α] [LawfulOrderSup α] :
{xs : List α} xs.max? = some a {x}, a x b, b xs b x
| nil => by simp
| cons x xs => by
rw [max?]
intro eq y
simp only [Option.some.injEq] at eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons z xs ih =>
simp at eq
simp [ih _ eq, max_le_iff, and_assoc]
theorem max?_eq_some_iff [Max α] [LE α] {xs : List α} [IsLinearOrder (α)]
[LawfulOrderMax α] : xs.max? = some a a xs b, b xs b a := by
refine fun h => max?_mem h, (max?_le_iff h).1 (le_refl _), ?_
intro h₁, h₂
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1 _ _
(h₂ _ (max?_mem max_eq_or (xs := x::xs) rfl))
((max?_le_iff max_le_iff (xs := x::xs) rfl).1 (le_refl _) _ h₁)
rw [List.max?]
exact congrArg some <| le_antisymm
(h₂ _ (max?_mem (xs := x :: xs) rfl))
((max?_le_iff (xs := x :: xs) rfl).1 (le_refl _) _ h₁)
theorem max?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
private theorem max?_attach [Max α] [MaxEqOr α] {xs : List α} :
xs.attach.max? = (xs.max?.pmap (fun m hm => m, max?_mem hm) (fun _ => id)) := by
cases xs with
| nil => simp
| cons x xs =>
simp only [max?, attach_cons, Option.some.injEq, Option.pmap_some]
rw [foldl_map]
simp only [Subtype.ext_iff]
rw [ foldl_attach (l := xs)]
apply Eq.trans (foldl_hom (f := Subtype.val) ?_).symm
· rfl
· intros; rfl
theorem max?_eq_max?_attach [Max α] [MaxEqOr α] {xs : List α} :
xs.max? = (xs.attach.max?.map Subtype.val) := by
simp [max?_attach, Option.map_pmap]
theorem max?_eq_some_iff_subtype [Max α] [LE α] {xs : List α}
[MaxEqOr α] [IsLinearOrder (Subtype (· xs))]
[LawfulOrderMax (Subtype (· xs))] :
xs.max? = some a a xs b, b xs b a := by
have := fun a => max?_eq_some_iff (xs := xs.attach) (a := a)
rw [max?_eq_max?_attach]
simp [max?_eq_some_iff]
constructor
· rintro ha, h
exact ha, h
· rintro ha, h
exact ha, h
@[deprecated max?_eq_some_iff (since := "2025-08-01")]
theorem max?_eq_some_iff_legacy [Max α] [LE α] [anti : Std.Antisymm (· · : α α Prop)]
(le_refl : a : α, a a)
(max_eq_or : a b : α, max a b = a max a b = b)
(max_le_iff : a b c : α, max b c a b a c a) {xs : List α} :
xs.max? = some a a xs b xs, b a := by
haveI : MaxEqOr α := max_eq_or
haveI : LawfulOrderMax α := .of_le (fun _ _ _ => max_le_iff _ _ _) max_eq_or
haveI : Refl (α := α) (· ·) := le_refl
haveI : IsLinearOrder α := .of_refl_of_antisymm_of_lawfulOrderMax
apply max?_eq_some_iff
theorem max?_replicate [Max α] [Std.IdempotentOp (max : α α α)] {n : Nat} {a : α} :
(replicate n a).max? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons']
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons', Std.IdempotentOp.idempotent]
@[simp] theorem max?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
@[simp] theorem max?_replicate_of_pos [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).max? = some a := by
simp [max?_replicate, Nat.ne_of_gt h, w]
simp [max?_replicate, Nat.ne_of_gt h]
/--
Requirements are satisfied for `[OrderData α] [Max α] [LinearOrder α] [LawfulOrderMax α]`
-/
theorem foldl_max [Max α] [Std.IdempotentOp (max : α α α)] [Std.Associative (max : α α α)]
{l : List α} {a : α} : l.foldl (init := a) max = max a (l.max?.getD a) := by
cases l <;> simp [max?, foldl_assoc, Std.IdempotentOp.idempotent]

View File

@@ -10,6 +10,7 @@ public import Init.Data.List.Count
public import Init.Data.List.Find
public import Init.Data.List.MinMax
public import Init.Data.Nat.Lemmas
import Init.Data.Nat.Order
public section
@@ -210,12 +211,10 @@ theorem mem_eraseIdx_iff_getElem? {x : α} {l} {k} : x ∈ eraseIdx l k ↔ ∃
/-! ### min? -/
-- A specialization of `min?_eq_some_iff` to Nat.
@[deprecated min?_eq_some_iff (since := "2025-08-08")]
theorem min?_eq_some_iff' {xs : List Nat} :
xs.min? = some a (a xs b xs, a b) :=
min?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => Nat.min_def .. by split <;> simp)
(le_min_iff := fun _ _ _ => Nat.le_min)
xs.min? = some a (a xs b xs, a b) := by
exact min?_eq_some_iff
theorem min?_get_le_of_mem {l : List Nat} {a : Nat} (h : a l) :
l.min?.get (isSome_min?_of_mem h) a := by
@@ -237,12 +236,10 @@ theorem min?_getD_le_of_mem {l : List Nat} {a k : Nat} (h : a ∈ l) : l.min?.ge
/-! ### max? -/
-- A specialization of `max?_eq_some_iff` to Nat.
@[deprecated max?_eq_some_iff (since := "2025-08-08")]
theorem max?_eq_some_iff' {xs : List Nat} :
xs.max? = some a (a xs b xs, b a) :=
max?_eq_some_iff
(le_refl := Nat.le_refl)
(max_eq_or := fun _ _ => Nat.max_def .. by split <;> simp)
(max_le_iff := fun _ _ _ => Nat.max_le)
theorem le_max?_get_of_mem {l : List Nat} {a : Nat} (h : a l) :
a l.max?.get (isSome_max?_of_mem h) := by

View File

@@ -11,6 +11,7 @@ public import Init.Data.Nat.Div
public import Init.Data.Nat.Dvd
public import Init.Data.Nat.Gcd
public import Init.Data.Nat.MinMax
public import Init.Data.Nat.Order
public import Init.Data.Nat.Bitwise
public import Init.Data.Nat.Control
public import Init.Data.Nat.Log2
@@ -23,5 +24,6 @@ public import Init.Data.Nat.Lcm
public import Init.Data.Nat.Compare
public import Init.Data.Nat.Simproc
public import Init.Data.Nat.Fold
public import Init.Data.Nat.Order
public section

View File

@@ -0,0 +1,41 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Nat.Basic
import Init.Data.Nat.MinMax
public import Init.Data.Order.Factories
open Std
namespace Nat
public instance instIsLinearOrder : IsLinearOrder Nat := by
apply IsLinearOrder.of_le
· constructor; apply Nat.le_antisymm
· constructor; apply Nat.le_trans
· constructor; apply Nat.le_total
public instance : LawfulOrderLT Nat := by
apply LawfulOrderLT.of_le
simp [Nat.lt_iff_le_and_ne]
public instance : LawfulOrderMin Nat := by
apply LawfulOrderMin.of_le
· apply Nat.le_min
· intro a b
simp only [Nat.min_def]
split <;> simp
public instance : LawfulOrderMax Nat := by
apply LawfulOrderMax.of_le
· apply Nat.max_le
· intro a b
simp only [Nat.max_def]
split <;> simp
end Nat

12
src/Init/Data/Order.lean Normal file
View File

@@ -0,0 +1,12 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Order.Classes
public import Init.Data.Order.Lemmas
public import Init.Data.Order.Factories
public import Init.Data.Subtype.Order

View File

@@ -0,0 +1,173 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Core
namespace Std
/-!
# Order-related typeclasses
This module provides the typeclasses used to state that basic operations on some type `α`
reflect a certain well-behaved order structure on `α`.
The basic operations are provided by the typeclasses `LE α`, `LT α`, `BEq α`, `Ord α`, `Min α` and
`Max α`.
All of them describe at least some way to compare elements in `α`. Usually, any subset of them
is available and one can/must show that these comparisons are well-behaved in some sense.
For example, one could merely require that the available operations reflect a preorder
(where the less-or-equal relation only needs to be reflexive and transitive). Alternatively,
one could require a full linear order (additionally requiring antisymmetry and totality of the
less-or-equal relation).
There are many ways to characterize, say, linear orders:
* `(· ≤ ·)` is reflexive, transitive, antisymmetric and total.
* `(· ≤ ·)` is antisymmetric, `a < b ↔ ¬ b ≤ a` and `(· < ·)` is irreflexive, transitive and asymmetric.
* `min a b` is either `a` or `b`, is symmetric and satisfies the
following property: `min c (min a b) = c` if and only if `min c a = c` and `min c b = c`.
It is desirable that lemmas about linear orders state this hypothesis in a canonical way.
Therefore, the classes defining preorders, partial orders, linear preorders and linear orders
are all formulated purely in terms of `LE`. For other operations, there are
classes for compatibility of `LE` with other operations. Hence, a lemma may look like:
```lean
theorem lt_trans {α : Type u} [LE α] [LT α]
[IsPreorder α] -- The order on `α` induced by `LE α` is, among other things, transitive.
[LawfulOrderLT α] -- `<` is the less-than relation induced by `LE α`.
{a b : α} : a < b → b < c → a < c := by
sorry
```
-/
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a preorder. In other words, the less-or-equal relation is reflexive and transitive.
-/
public class IsPreorder (α : Type u) [LE α] where
le_refl : a : α, a a
le_trans : a b c : α, a b b c a c
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a partial order.
In other words, the less-or-equal relation is reflexive, transitive and antisymmetric.
-/
public class IsPartialOrder (α : Type u) [LE α] extends IsPreorder α where
le_antisymm : a b : α, a b b a a = b
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a linear preorder.
In other words, the less-or-equal relation is reflexive, transitive and total.
-/
public class IsLinearPreorder (α : Type u) [LE α] extends IsPreorder α where
le_total : a b : α, a b b a
/--
This typeclass states that the order structure on `α`, represented by an `LE α` instance,
is a linear order.
In other words, the less-or-equal relation is reflexive, transitive, antisymmetric and total.
-/
public class IsLinearOrder (α : Type u) [LE α] extends IsPartialOrder α, IsLinearPreorder α
section LT
/--
This typeclass states that the synthesized `LT α` instance is compatible with the `LE α`
instance. This means that `LT.lt a b` holds if and only if `a` is less or equal to `b` according
to the `LE α` instance, but `b` is not less or equal to `a`.
`LawfulOrderLT α` automatically entails that `LT α` is asymmetric: `a < b` and `b < a` can never
be true simultaneously.
`LT α` does not uniquely determine the `LE α`: There can be only one compatible order data
instance that is total, but there can be others that are not total.
-/
public class LawfulOrderLT (α : Type u) [LT α] [LE α] where
lt_iff : a b : α, a < b a b ¬ b a
end LT
section Min
/--
This typeclass states that `Min.min a b` returns one of its arguments, either `a` or `b`.
-/
public class MinEqOr (α : Type u) [Min α] where
min_eq_or : a b : α, min a b = a min a b = b
/--
If both `a` and `b` satisfy some property `P`, then so does `min a b`, because it is equal to
either `a` or `b`.
-/
public def MinEqOr.elim {α : Type u} [Min α] [MinEqOr α] {P : α Prop} {a b : α} (ha : P a) (hb : P b) :
P (min a b) := by
cases MinEqOr.min_eq_or a b <;> rename_i h
case inl => exact h.symm ha
case inr => exact h.symm hb
/--
This typeclass states that being less or equal to `min a b` is equivalent to being less or
equal to both `a` and `b`..
-/
public class LawfulOrderInf (α : Type u) [Min α] [LE α] where
le_min_iff : a b c : α, a (min b c) a b a c
/--
This typeclass bundles `MinEqOr α` and `LawfulOrderInf α`. It characterizes when a `Min α`
instance reasonably computes minima in some type `α` that has an `LE α` instance.
As long as `α` is a preorder (see `IsPreorder α`), this typeclass implies that the order on
`α` is total and that `Min.min a b` returns either `a` or `b`, whichever is less or equal to
the other.
-/
public class LawfulOrderMin (α : Type u) [Min α] [LE α] extends MinEqOr α, LawfulOrderInf α
end Min
section Max
/--
This typeclass states that `Max.max a b` returns one of its arguments, either `a` or `b`.
-/
public class MaxEqOr (α : Type u) [Max α] where
max_eq_or : a b : α, max a b = a max a b = b
/--
If both `a` and `b` satisfy some property `P`, then so does `max a b`, because it is equal to
either `a` or `b`.
-/
public def MaxEqOr.elim {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} {a b : α} (ha : P a) (hb : P b) :
P (max a b) := by
cases MaxEqOr.max_eq_or a b <;> rename_i h
case inl => exact h.symm ha
case inr => exact h.symm hb
/--
This typeclass states that being less or equal to `Max.max a b` is equivalent to being less or
equal to both `a` and `b`.
-/
public class LawfulOrderSup (α : Type u) [Max α] [LE α] where
max_le_iff : a b c : α, (max a b) c a c b c
/--
This typeclass bundles `MaxEqOr α` and `LawfulOrderSup α`. It characterizes when a `Max α`
instance reasonably computes maxima in some type `α` that has an `LE α` instance.
As long as `α` is a preorder (see `IsPreorder α`), this typeclass implies that the order on
`α` is total and that `Min.min a b` returns either `a` or `b`, whichever is greater or equal to
the other.
-/
public class LawfulOrderMax (α : Type u) [Max α] [LE α] extends MaxEqOr α, LawfulOrderSup α
end Max
end Std

View File

@@ -0,0 +1,236 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Order.Classes
import Init.Classical
namespace Std
/-!
This module provides utilities for the creation of order-related typeclass instances.
-/
section OfLE
/--
This instance is only publicly defined in `Init.Data.Order.Lemmas`.
-/
instance {r : α α Prop} [Total r] : Refl r where
refl a := by simpa using Total.total a a
/--
If an `LE α` instance is reflexive and transitive, then it represents a preorder.
-/
public theorem IsPreorder.of_le {α : Type u} [LE α]
(le_refl : Std.Refl (α := α) (· ·) := by exact inferInstance)
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance) :
IsPreorder α where
le_refl := le_refl.refl
le_trans _ _ _ := le_trans.trans
/--
If an `LE α` instance is transitive and total, then it represents a linear preorder.
-/
public theorem IsLinearPreorder.of_le {α : Type u} [LE α]
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance)
(le_total : Total (α := α) (· ·) := by exact inferInstance) :
IsLinearPreorder α where
toIsPreorder := .of_le
le_total := le_total.total
/--
If an `LE α` is reflexive, antisymmetric and transitive, then it represents a partial order.
-/
public theorem IsPartialOrder.of_le {α : Type u} [LE α]
(le_refl : Std.Refl (α := α) (· ·) := by exact inferInstance)
(le_antisymm : Std.Antisymm (α := α) (· ·) := by exact inferInstance)
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance) :
IsPartialOrder α where
toIsPreorder := .of_le
le_antisymm := le_antisymm.antisymm
/--
If an `LE α` instance is antisymmetric, transitive and total, then it represents a linear order.
-/
public theorem IsLinearOrder.of_le {α : Type u} [LE α]
(le_antisymm : Std.Antisymm (α := α) (· ·) := by exact inferInstance)
(le_trans : Trans (α := α) (· ·) (· ·) (· ·) := by exact inferInstance)
(le_total : Total (α := α) (· ·) := by exact inferInstance) :
IsLinearOrder α where
toIsLinearPreorder := .of_le
le_antisymm := le_antisymm.antisymm
/--
Returns a `LawfulOrderLT α` instance given certain properties.
If an `OrderData α` instance is compatible with an `LE α` instance, then this lemma derives
a `LawfulOrderLT α` instance from a property relating the `LE α` and `LT α` instances.
-/
public theorem LawfulOrderLT.of_le {α : Type u} [LT α] [LE α]
(lt_iff : a b : α, a < b a b ¬ b a) : LawfulOrderLT α where
lt_iff := lt_iff
/--
This lemma characterizes in terms of `LE α` when a `Min α` instance "behaves like an infimum
operator".
-/
public theorem LawfulOrderInf.of_le {α : Type u} [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c) : LawfulOrderInf α where
le_min_iff := le_min_iff
/--
Returns a `LawfulOrderMin α` instance given certain properties.
This lemma derives a `LawfulOrderMin α` instance from two properties involving `LE α` and `Min α`
instances.
The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_le {α : Type u} [Min α] [LE α]
(le_min_iff : a b c : α, a min b c a b a c)
(min_eq_or : a b : α, min a b = a min a b = b) : LawfulOrderMin α where
toLawfulOrderInf := .of_le le_min_iff
toMinEqOr := min_eq_or
/--
This lemma characterizes in terms of `LE α` when a `Max α` instance "behaves like a supremum
operator".
-/
public def LawfulOrderSup.of_le {α : Type u} [Max α] [LE α]
(max_le_iff : a b c : α, max a b c a c b c) : LawfulOrderSup α where
max_le_iff := max_le_iff
/--
Returns a `LawfulOrderMax α` instance given certain properties.
This lemma derives a `LawfulOrderMax α` instance from two properties involving `LE α` and `Max α`
instances.
The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public def LawfulOrderMax.of_le {α : Type u} [Max α] [LE α]
(max_le_iff : a b c : α, max a b c a c b c)
(max_eq_or : a b : α, max a b = a max a b = b) : LawfulOrderMax α where
toLawfulOrderSup := .of_le max_le_iff
toMaxEqOr := max_eq_or
end OfLE
section OfLT
/--
Creates a *total* `LE α` instance from an `LT α` instance.
This only makes sense for asymmetric `LT α` instances (see `Std.Asymm`).
-/
public def LE.ofLT (α : Type u) [LT α] : LE α where
le a b := ¬ b < a
/--
The `LE α` instance obtained from an asymmetric `LT α` instance is compatible with said
`LT α` instance.
-/
public instance LawfulOrderLT.of_lt {α : Type u} [LT α] [i : Asymm (α := α) (· < ·)] :
haveI := LE.ofLT α
LawfulOrderLT α :=
letI := LE.ofLT α
{ lt_iff a b := by simpa [LE.ofLT, Classical.not_not] using i.asymm a b }
/--
If an `LT α` instance is asymmetric and its negation is transitive, then `LE.ofLT α` represents a
linear preorder.
-/
public theorem IsLinearPreorder.of_lt {α : Type u} [LT α]
(lt_asymm : Asymm (α := α) (· < ·) := by exact inferInstance)
(not_lt_trans : Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) := by exact inferInstance) :
haveI := LE.ofLT α
IsLinearPreorder α :=
letI := LE.ofLT α
{ le_trans := by simpa [LE.ofLT] using fun a b c hab hbc => not_lt_trans.trans hbc hab
le_total a b := by
apply Or.symm
open Classical in simpa [LE.ofLT, Decidable.imp_iff_not_or] using lt_asymm.asymm a b
le_refl a := by
open Classical in simpa [LE.ofLT] using lt_asymm.asymm a a }
/--
If an `LT α` instance is asymmetric and its negation is transitive and antisymmetric, then
`LE.ofLT α` represents a linear order.
-/
public theorem IsLinearOrder.of_lt {α : Type u} [LT α]
(lt_asymm : Asymm (α := α) (· < ·) := by exact inferInstance)
(not_lt_trans : Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) := by exact inferInstance)
(not_lt_antisymm : Antisymm (α := α) (¬ · < ·) := by exact inferInstance) :
haveI := LE.ofLT α
IsLinearOrder α :=
letI := LE.ofLT α
haveI : IsLinearPreorder α := .of_lt
{ le_antisymm := by
simpa [LE.ofLT] using fun a b hab hba => not_lt_antisymm.antisymm a b hba hab }
/--
This lemma characterizes in terms of `LT α` when a `Min α` instance
"behaves like an infimum operator" with respect to `LE.ofLT α`.
-/
public theorem LawfulOrderInf.of_lt {α : Type u} [Min α] [LT α]
(min_lt_iff : a b c : α, min b c < a b < a c < a) :
haveI := LE.ofLT α
LawfulOrderInf α :=
letI := LE.ofLT α
{ le_min_iff a b c := by
open Classical in
simp only [LE.ofLT, Decidable.not_iff_not (a := ¬ min b c < a)]
simpa [Decidable.imp_iff_not_or] using min_lt_iff a b c }
/--
Derives a `LawfulOrderMin α` instance for `OrderData.ofLT` from two properties involving
`LT α` and `Min α` instances.
The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_lt {α : Type u} [Min α] [LT α]
(min_lt_iff : a b c : α, min b c < a b < a c < a)
(min_eq_or : a b : α, min a b = a min a b = b) :
haveI := LE.ofLT α
LawfulOrderMin α :=
letI := LE.ofLT α
{ toLawfulOrderInf := .of_lt min_lt_iff
toMinEqOr := min_eq_or }
/--
This lemma characterizes in terms of `LT α` when a `Max α` instance
"behaves like an supremum operator" with respect to `OrderData.ofLT α`.
-/
public def LawfulOrderSup.of_lt {α : Type u} [Max α] [LT α]
(lt_max_iff : a b c : α, c < max a b c < a c < b) :
haveI := LE.ofLT α
LawfulOrderSup α :=
letI := LE.ofLT α
{ max_le_iff a b c := by
open Classical in
simp only [LE.ofLT, Decidable.not_iff_not ( a := ¬ c < max a b)]
simpa [Decidable.imp_iff_not_or] using lt_max_iff a b c }
/--
Derives a `LawfulOrderMax α` instance for `OrderData.ofLT` from two properties involving `LT α` and
`Max α` instances.
The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public def LawfulOrderMax.of_lt {α : Type u} [Max α] [LT α]
(lt_max_iff : a b c : α, c < max a b c < a c < b)
(max_eq_or : a b : α, max a b = a max a b = b) :
haveI := LE.ofLT α
LawfulOrderMax α :=
letI := LE.ofLT α
{ toLawfulOrderSup := .of_lt lt_max_iff
toMaxEqOr := max_eq_or }
end OfLT
end Std

View File

@@ -0,0 +1,342 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Order.Classes
public import Init.Data.Order.Factories
import Init.SimpLemmas
import Init.Classical
namespace Std
/-!
This module provides typeclass instances and lemmas about order-related typeclasses.
-/
section AxiomaticInstances
public instance (r : α α Prop) [Asymm r] : Irrefl r where
irrefl a h := Asymm.asymm a a h h
public instance {r : α α Prop} [Total r] : Refl r where
refl a := by simpa using Total.total a a
public instance Total.asymm_of_total_not {r : α α Prop} [i : Total (¬ r · ·)] : Asymm r where
asymm a b h := by cases i.total a b <;> trivial
public theorem Asymm.total_not {r : α α Prop} [i : Asymm r] : Total (¬ r · ·) where
total a b := by
apply Classical.byCases (p := r a b) <;> intro hab
· exact Or.inr <| i.asymm a b hab
· exact Or.inl hab
public instance {α : Type u} [LE α] [IsPartialOrder α] :
Std.Antisymm (α := α) (· ·) where
antisymm := IsPartialOrder.le_antisymm
public instance {α : Type u} [LE α] [IsPreorder α] :
Trans (α := α) (· ·) (· ·) (· ·) where
trans := IsPreorder.le_trans _ _ _
public instance {α : Type u} [LE α] [IsPreorder α] :
Std.Refl (α := α) (· ·) where
refl a := IsPreorder.le_refl a
public instance {α : Type u} [LE α] [IsLinearPreorder α] :
Std.Total (α := α) (· ·) where
total a b := IsLinearPreorder.le_total a b
end AxiomaticInstances
section LE
public theorem le_refl {α : Type u} [LE α] [Refl (α := α) (· ·)] (a : α) : a a := by
simp [Refl.refl]
public theorem le_antisymm {α : Type u} [LE α] [Std.Antisymm (α := α) (· ·)] {a b : α}
(hab : a b) (hba : b a) : a = b :=
Std.Antisymm.antisymm _ _ hab hba
public theorem le_trans {α : Type u} [LE α] [Trans (α := α) (· ·) (· ·) (· ·)] {a b c : α}
(hab : a b) (hbc : b c) : a c :=
Trans.trans hab hbc
public theorem le_total {α : Type u} [LE α] [Std.Total (α := α) (· ·)] {a b : α} :
a b b a :=
Std.Total.total a b
public instance {α : Type u} [LE α] [IsPreorder α] :
Refl (α := α) (· ·) where
refl := IsPreorder.le_refl
public instance {α : Type u} [LE α] [IsPreorder α] :
Trans (α := α) (· ·) (· ·) (· ·) where
trans := IsPreorder.le_trans _ _ _
public instance {α : Type u} [LE α] [IsLinearPreorder α] :
Total (α := α) (· ·) where
total := IsLinearPreorder.le_total
public instance {α : Type u} [LE α] [IsPartialOrder α] :
Antisymm (α := α) (· ·) where
antisymm := IsPartialOrder.le_antisymm
end LE
section LT
public theorem lt_iff_le_and_not_ge {α : Type u} [LT α] [LE α] [LawfulOrderLT α] {a b : α} :
a < b a b ¬ b a :=
LawfulOrderLT.lt_iff a b
public theorem not_lt {α : Type u} [LT α] [LE α] [Std.Total (α := α) (· ·)] [LawfulOrderLT α]
{a b : α} : ¬ a < b b a := by
simp [lt_iff_le_and_not_ge, Classical.not_not, Std.Total.total]
public theorem not_gt_of_lt {α : Type u} [LT α] [i : Std.Asymm (α := α) (· < ·)] {a b : α}
(h : a < b) : ¬ b < a :=
i.asymm a b h
public instance {α : Type u} [LT α] [LE α] [LawfulOrderLT α] :
Std.Asymm (α := α) (· < ·) where
asymm a b := by
simp only [LawfulOrderLT.lt_iff]
intro h h'
exact h.2.elim h'.1
public instance {α : Type u} [LT α] [LE α] [IsPreorder α] [LawfulOrderLT α] :
Std.Irrefl (α := α) (· < ·) := inferInstance
public instance {α : Type u} [LT α] [LE α]
[Trans (α := α) (· ·) (· ·) (· ·) ] [LawfulOrderLT α] :
Trans (α := α) (· < ·) (· < ·) (· < ·) where
trans {a b c} hab hbc := by
simp only [lt_iff_le_and_not_ge] at hab hbc
apply And.intro
· exact le_trans hab.1 hbc.1
· intro hca
exact hab.2.elim (le_trans hbc.1 hca)
public instance {α : Type u} {_ : LT α} [LE α] [LawfulOrderLT α]
[Total (α := α) (· ·)] [Antisymm (α := α) (· ·)] :
Antisymm (α := α) (¬ · < ·) where
antisymm a b hab hba := by
simp only [not_lt] at hab hba
exact Antisymm.antisymm (r := (· ·)) a b hba hab
public instance {α : Type u} {_ : LT α} [LE α] [LawfulOrderLT α]
[Total (α := α) (· ·)] [Trans (α := α) (· ·) (· ·) (· ·)] :
Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) where
trans {a b c} hab hbc := by
simp only [not_lt] at hab hbc
exact le_trans hbc hab
public instance {α : Type u} {_ : LT α} [LE α] [LawfulOrderLT α] [Total (α := α) (· ·)] :
Total (α := α) (¬ · < ·) where
total a b := by simp [not_lt, Std.Total.total]
public theorem lt_of_le_of_lt {α : Type u} [LE α] [LT α]
[Trans (α := α) (· ·) (· ·) (· ·)] [LawfulOrderLT α] {a b c : α} (hab : a b)
(hbc : b < c) : a < c := by
simp only [lt_iff_le_and_not_ge] at hbc
apply And.intro
· exact le_trans hab hbc.1
· intro hca
exact hbc.2.elim (le_trans hca hab)
public theorem lt_of_le_of_ne {α : Type u} [LE α] [LT α]
[Std.Antisymm (α := α) (· ·)] [LawfulOrderLT α] {a b : α}
(hle : a b) (hne : a b) : a < b := by
apply Classical.byContradiction
simp only [lt_iff_le_and_not_ge, hle, true_and, Classical.not_not, imp_false]
intro hge
exact hne.elim <| Std.Antisymm.antisymm a b hle hge
end LT
end Std
namespace Classical.Order
open Std
public scoped instance instLT {α : Type u} [LE α] :
LT α where
lt a b := a b ¬ b a
public instance instLawfulOrderLT {α : Type u} [LE α] :
LawfulOrderLT α where
lt_iff _ _ := Iff.rfl
end Classical.Order
namespace Std
section Min
public theorem min_self {α : Type u} [Min α] [Std.IdempotentOp (min : α α α)] {a : α} :
min a a = a :=
Std.IdempotentOp.idempotent a
public theorem le_min_iff {α : Type u} [Min α] [LE α]
[LawfulOrderInf α] {a b c : α} :
a min b c a b a c :=
LawfulOrderInf.le_min_iff a b c
public theorem min_le_left {α : Type u} [Min α] [LE α] [Refl (α := α) (· ·)] [LawfulOrderInf α]
{a b : α} : min a b a :=
le_min_iff.mp (le_refl _) |>.1
public theorem min_le_right {α : Type u} [Min α] [LE α] [Refl (α := α) (· ·)] [LawfulOrderInf α]
{a b : α} : min a b b :=
le_min_iff.mp (le_refl _) |>.2
public theorem min_le {α : Type u} [Min α] [LE α] [IsPreorder α] [LawfulOrderMin α] {a b c : α} :
min a b c a c b c := by
cases MinEqOr.min_eq_or a b <;> rename_i h
· simpa [h] using le_trans (h min_le_right (a := a) (b := b))
· simpa [h] using le_trans (h min_le_left (a := a) (b := b))
public theorem min_eq_or {α : Type u} [Min α] [MinEqOr α] {a b : α} :
min a b = a min a b = b :=
MinEqOr.min_eq_or a b
public instance {α : Type u} [LE α] [Min α] [IsLinearOrder α] [LawfulOrderInf α] :
MinEqOr α where
min_eq_or a b := by
open Classical.Order in
cases le_total (a := a) (b := b)
· apply Or.inl
apply le_antisymm
· apply min_le_left
· rw [le_min_iff]
exact le_refl a, _
· apply Or.inr
apply le_antisymm
· apply min_le_right
· rw [le_min_iff]
exact _, le_refl b
/--
If a `Min α` instance satisfies typical properties in terms of a reflexive and antisymmetric `LE α`
instance, then the `LE α` instance represents a linear order.
-/
public theorem IsLinearOrder.of_refl_of_antisymm_of_lawfulOrderMin {α : Type u} [LE α]
[LE α] [Min α] [Refl (α := α) (· ·)] [Antisymm (α := α) (· ·)] [LawfulOrderMin α] :
IsLinearOrder α := by
apply IsLinearOrder.of_le
· infer_instance
· constructor
intro a b c hab hbc
have : b = min b c := by
apply le_antisymm
· rw [le_min_iff]
exact le_refl b, hbc
· apply min_le_left
rw [this, le_min_iff] at hab
exact hab.2
· constructor
intro a b
cases min_eq_or (a := a) (b := b) <;> rename_i h
· exact Or.inl (h min_le_right)
· exact Or.inr (h min_le_left)
public instance {α : Type u} [Min α] [MinEqOr α] :
Std.IdempotentOp (min : α α α) where
idempotent a := by cases MinEqOr.min_eq_or a a <;> assumption
open Classical.Order in
public instance {α : Type u} [LE α] [Min α] [IsLinearOrder α] [LawfulOrderMin α] :
Std.Associative (min : α α α) where
assoc a b c := by apply le_antisymm <;> simp [min_le, le_min_iff, le_refl]
end Min
section Max
public theorem max_self {α : Type u} [Max α] [Std.IdempotentOp (max : α α α)] {a : α} :
max a a = a :=
Std.IdempotentOp.idempotent a
public theorem max_le_iff {α : Type u} [Max α] [LE α] [LawfulOrderSup α] {a b c : α} :
max a b c a c b c :=
LawfulOrderSup.max_le_iff a b c
public theorem left_le_max {α : Type u} [Max α] [LE α] [Refl (α := α) (· ·)] [LawfulOrderSup α]
{a b : α} : a max a b :=
max_le_iff.mp (le_refl _) |>.1
public theorem right_le_max {α : Type u} [Max α] [LE α] [Refl (α := α) (· ·)]
[LawfulOrderSup α] {a b : α} : b max a b :=
max_le_iff.mp (le_refl _) |>.2
public theorem le_max {α : Type u} [Max α] [LE α] [IsPreorder α] [LawfulOrderMax α] {a b c : α} :
a max b c a b a c := by
cases MaxEqOr.max_eq_or b c <;> rename_i h
· simpa [h] using (le_trans · (h right_le_max))
· simpa [h] using (le_trans · (h left_le_max))
public theorem max_eq_or {α : Type u} [Max α] [MaxEqOr α] {a b : α} :
max a b = a max a b = b :=
MaxEqOr.max_eq_or a b
public instance {α : Type u} [LE α] [Max α] [IsLinearOrder α] [LawfulOrderSup α] :
MaxEqOr α where
max_eq_or a b := by
open Classical.Order in
cases le_total (a := a) (b := b)
· apply Or.inr
apply le_antisymm
· rw [max_le_iff]
exact _, le_refl b
· apply right_le_max
· apply Or.inl
apply le_antisymm
· rw [max_le_iff]
exact le_refl a, _
· apply left_le_max
/--
If a `Max α` instance satisfies typical properties in terms of a reflexive and antisymmetric `LE α`
instance, then the `LE α` instance represents a linear order.
-/
public theorem IsLinearOrder.of_refl_of_antisymm_of_lawfulOrderMax {α : Type u} [LE α] [Max α]
[Refl (α := α) (· ·)] [Antisymm (α := α) (· ·)] [LawfulOrderMax α] :
IsLinearOrder α := by
apply IsLinearOrder.of_le
· infer_instance
· constructor
intro a b c hab hbc
have : b = max a b := by
apply le_antisymm
· exact right_le_max
· rw [max_le_iff]
exact hab, le_refl b
rw [this, max_le_iff] at hbc
exact hbc.1
· constructor
intro a b
cases max_eq_or (a := a) (b := b) <;> rename_i h
· exact Or.inr (h right_le_max)
· exact Or.inl (h left_le_max)
public instance {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} : Max (Subtype P) where
max a b := Max.max a.val b.val, MaxEqOr.elim a.property b.property
public instance {α : Type u} [Max α] [MaxEqOr α] :
Std.IdempotentOp (max : α α α) where
idempotent a := by cases MaxEqOr.max_eq_or a a <;> assumption
open Classical.Order in
public instance {α : Type u} [LE α] [Max α] [IsLinearOrder α] [LawfulOrderMax α] :
Std.Associative (max : α α α) where
assoc a b c := by
apply le_antisymm
all_goals
simp only [max_le_iff]
simp [le_max, le_refl]
end Max
end Std

View File

@@ -36,7 +36,14 @@ structure StdGen where
s1 : Nat
s2 : Nat
instance : Inhabited StdGen := { s1 := 0, s2 := 0 }
/-- Returns a standard number generator. -/
def mkStdGen (s : Nat := 0) : StdGen :=
let q := s / 2147483562
let s1 := s % 2147483562
let s2 := q % 2147483398
s1 + 1, s2 + 1
instance : Inhabited StdGen := mkStdGen
/-- The range of values returned by `StdGen` -/
def stdRange := (1, 2147483562)
@@ -77,13 +84,6 @@ instance : RandomGen StdGen := {
split := stdSplit
}
/-- Returns a standard number generator. -/
def mkStdGen (s : Nat := 0) : StdGen :=
let q := s / 2147483562
let s1 := s % 2147483562
let s2 := q % 2147483398
s1 + 1, s2 + 1
/--
Auxiliary function for randomNatVal.
Generate random values until we exceed the target magnitude.

View File

@@ -441,7 +441,7 @@ instance RangeIterator.instIteratorLoop {su} [UpwardEnumerable α] [SupportsUppe
(f : (out : α) UpwardEnumerable.LE least out SupportsUpperBound.IsSatisfied upperBound out (c : γ) n (Subtype (fun s : ForInStep γ => Pl out c s)))
(next : α) (hl : UpwardEnumerable.LE least next) (hu : SupportsUpperBound.IsSatisfied upperBound next) : n γ := do
match f next hl hu acc with
| .yield acc', h =>
| .yield acc', _ =>
match hs : UpwardEnumerable.succ? next with
| some next' =>
if hu : SupportsUpperBound.IsSatisfied upperBound next' then

View File

@@ -15,9 +15,12 @@ public import Init.Data.Int.LemmasAux
public import all Init.Data.UInt.Basic
public import Init.Data.UInt.Lemmas
public import Init.System.Platform
import Init.Data.Order.Lemmas
public section
open Std
open Lean in
set_option hygiene false in
macro "declare_int_theorems" typeName:ident _bits:term:arg : command => do
@@ -3025,6 +3028,56 @@ protected theorem Int64.lt_asymm {a b : Int64} : a < b → ¬b < a :=
protected theorem ISize.lt_asymm {a b : ISize} : a < b ¬b < a :=
fun hab hba => ISize.lt_irrefl (ISize.lt_trans hab hba)
instance Int8.instIsLinearOrder : IsLinearOrder Int8 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int8.le_antisymm
case le_total => constructor; apply Int8.le_total
case le_trans => constructor; apply Int8.le_trans
instance : LawfulOrderLT Int8 where
lt_iff := by
simp [ Int8.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance Int16.instIsLinearOrder : IsLinearOrder Int16 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int16.le_antisymm
case le_total => constructor; apply Int16.le_total
case le_trans => constructor; apply Int16.le_trans
instance : LawfulOrderLT Int16 where
lt_iff := by
simp [ Int16.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance Int32.instIsLinearOrder : IsLinearOrder Int32 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int32.le_antisymm
case le_total => constructor; apply Int32.le_total
case le_trans => constructor; apply Int32.le_trans
instance : LawfulOrderLT Int32 where
lt_iff := by
simp [ Int32.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance Int64.instIsLinearOrder : IsLinearOrder Int64 := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Int64.le_antisymm
case le_total => constructor; apply Int64.le_total
case le_trans => constructor; apply Int64.le_trans
instance : LawfulOrderLT Int64 where
lt_iff := by
simp [ Int64.not_le, Decidable.imp_iff_not_or, Std.Total.total]
instance ISize.instIsLinearOrder : IsLinearOrder ISize := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply ISize.le_antisymm
case le_total => constructor; apply ISize.le_total
case le_trans => constructor; apply ISize.le_trans
instance : LawfulOrderLT ISize where
lt_iff := by
simp [ ISize.not_le, Decidable.imp_iff_not_or, Std.Total.total]
protected theorem Int8.add_neg_eq_sub {a b : Int8} : a + -b = a - b := Int8.toBitVec_inj.1 BitVec.add_neg_eq_sub
protected theorem Int16.add_neg_eq_sub {a b : Int16} : a + -b = a - b := Int16.toBitVec_inj.1 BitVec.add_neg_eq_sub
protected theorem Int32.add_neg_eq_sub {a b : Int32} : a + -b = a - b := Int32.toBitVec_inj.1 BitVec.add_neg_eq_sub

View File

@@ -485,6 +485,7 @@ Examples:
* `"tea".firstDiffPos "teas" = ⟨3⟩`
* `"teas".firstDiffPos "tea" = ⟨3⟩`
-/
@[expose]
def firstDiffPos (a b : String) : Pos :=
let stopPos := a.endPos.min b.endPos
let rec loop (i : Pos) : Pos :=
@@ -511,7 +512,7 @@ Examples:
* `"red green blue".extract ⟨4⟩ ⟨100⟩ = "green blue"`
* `"L∃∀N".extract ⟨2⟩ ⟨100⟩ = "green blue"`
-/
@[extern "lean_string_utf8_extract"]
@[extern "lean_string_utf8_extract", expose]
def extract : (@& String) (@& Pos) (@& Pos) String
| s, b, e => if b.byteIdx e.byteIdx then "" else go₁ s 0 b e
where

View File

@@ -6,11 +6,15 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Char.Order
public import Init.Data.Char.Lemmas
public import Init.Data.List.Lex
import Init.Data.Order.Lemmas
public section
open Std
namespace String
protected theorem data_eq_of_eq {a b : String} (h : a = b) : a.data = b.data :=
@@ -34,4 +38,14 @@ protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
have := String.lt_irrefl a
intro h; subst h; contradiction
instance instIsLinearOrder : IsLinearOrder String := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply String.le_antisymm
case le_trans => constructor; apply String.le_trans
case le_total => constructor; apply String.le_total
instance : LawfulOrderLT String where
lt_iff a b := by
simp [ String.not_le, Decidable.imp_iff_not_or, Std.Total.total]
end String

View File

@@ -1,32 +1,11 @@
/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
Authors: Paul Reichert
-/
module
prelude
public import Init.Ext
public import Init.Core
public section
namespace Subtype
universe u
variable {α : Sort u} {p q : α Prop}
@[ext]
protected theorem ext : {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) a1 = a2
| _, _, _, _, rfl => rfl
@[simp]
protected theorem «forall» {q : { a // p a } Prop} : ( x, q x) a b, q a, b :=
fun h a b h a, b, fun h a, b h a b
@[simp]
protected theorem «exists» {q : { a // p a } Prop} :
(Exists fun x => q x) Exists fun a => Exists fun b => q a, b :=
fun a, b, h a, b, h, fun a, b, h a, b, h
end Subtype
public import Init.Data.Subtype.Basic
public import Init.Data.Subtype.Order
public import Init.Data.Subtype.OrderExtra

View File

@@ -0,0 +1,32 @@
/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
module
prelude
public import Init.Ext
public import Init.Core
public section
namespace Subtype
universe u
variable {α : Sort u} {p q : α Prop}
@[ext]
protected theorem ext : {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) a1 = a2
| _, _, _, _, rfl => rfl
@[simp]
protected theorem «forall» {q : { a // p a } Prop} : ( x, q x) a b, q a, b :=
fun h a b h a, b, fun h a, b h a b
@[simp]
protected theorem «exists» {q : { a // p a } Prop} :
(Exists fun x => q x) Exists fun a => Exists fun b => q a, b :=
fun a, b, h a, b, h, fun a, b, h a, b, h
end Subtype

View File

@@ -0,0 +1,94 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
import Init.SimpLemmas
public import Init.Data.Order.Classes
public import Init.Data.Order.Lemmas
import Init.Data.Order.Factories
import Init.Data.Subtype.Basic
namespace Std
public instance {α : Type u} [LE α] {P : α Prop} : LE (Subtype P) where
le a b := a.val b.val
public instance {α : Type u} [LT α] {P : α Prop} : LT (Subtype P) where
lt a b := a.val < b.val
public instance {α : Type u} [LT α] [LE α] [LawfulOrderLT α]
{P : α Prop} : LawfulOrderLT (Subtype P) where
lt_iff a b := by simp [LT.lt, LE.le, LawfulOrderLT.lt_iff]
public instance {α : Type u} [BEq α] {P : α Prop} : BEq (Subtype P) where
beq a b := a.val == b.val
public instance {α : Type u} [Min α] [MinEqOr α] {P : α Prop} : Min (Subtype P) where
min a b := Min.min a.val b.val, MinEqOr.elim a.property b.property
public instance {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} : Max (Subtype P) where
max a b := max a.val b.val, MaxEqOr.elim a.property b.property
public instance {α : Type u} [LE α] [i : Refl (α := α) (· ·)] {P : α Prop} :
Refl (α := Subtype P) (· ·) where
refl a := i.refl a.val
public instance {α : Type u} [LE α] [i : Antisymm (α := α) (· ·)] {P : α Prop} :
Antisymm (α := Subtype P) (· ·) where
antisymm a b hab hba := private Subtype.ext <| i.antisymm a.val b.val hab hba
public instance {α : Type u} [LE α] [i : Total (α := α) (· ·)] {P : α Prop} :
Total (α := Subtype P) (· ·) where
total a b := i.total a.val b.val
public instance {α : Type u} [LE α] [i : Trans (α := α) (· ·) (· ·) (· ·)]
{P : α Prop} :
Trans (α := Subtype P) (· ·) (· ·) (· ·) where
trans := i.trans
public instance {α : Type u} [Min α] [MinEqOr α] {P : α Prop} :
MinEqOr (Subtype P) where
min_eq_or a b := by
cases min_eq_or (a := a.val) (b := b.val) <;> rename_i h
· exact Or.inl <| Subtype.ext h
· exact Or.inr <| Subtype.ext h
public instance {α : Type u} [LE α] [Min α] [LawfulOrderMin α] {P : α Prop} :
LawfulOrderMin (Subtype P) where
le_min_iff _ _ _ := by
exact le_min_iff (α := α)
public instance {α : Type u} [Max α] [MaxEqOr α] {P : α Prop} :
MaxEqOr (Subtype P) where
max_eq_or a b := by
cases max_eq_or (a := a.val) (b := b.val) <;> rename_i h
· exact Or.inl <| Subtype.ext h
· exact Or.inr <| Subtype.ext h
public instance {α : Type u} [LE α] [Max α] [LawfulOrderMax α] {P : α Prop} :
LawfulOrderMax (Subtype P) where
max_le_iff _ _ _ := by
open Classical.Order in
exact max_le_iff (α := α)
public instance {α : Type u} [LE α] [IsPreorder α] {P : α Prop} :
IsPreorder (Subtype P) :=
IsPreorder.of_le
public instance {α : Type u} [LE α] [IsLinearPreorder α] {P : α Prop} :
IsLinearPreorder (Subtype P) :=
IsLinearPreorder.of_le
public instance {α : Type u} [LE α] [IsPartialOrder α] {P : α Prop} :
IsPartialOrder (Subtype P) :=
IsPartialOrder.of_le
public instance {α : Type u} [LE α] [IsLinearOrder α] {P : α Prop} :
IsLinearOrder (Subtype P) :=
IsLinearOrder.of_le
end Std

View File

@@ -0,0 +1,13 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Data.Subtype.Order
public import Init.Data.Ord
public instance {α : Type u} [Ord α] {P : α Prop} : Ord (Subtype P) where
compare a b := compare a.val b.val

View File

@@ -8,9 +8,13 @@ module
prelude
public import Init.Data.UInt.BasicAux
public import Init.Data.BitVec.Basic
public import Init.Data.Order.Classes
import Init.Data.Order.Factories
@[expose] public section
open Std
set_option linter.missingDocs true
open Nat

View File

@@ -15,9 +15,13 @@ public import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Lemmas
public import Init.Data.Nat.Div.Lemmas
public import Init.System.Platform
public import Init.Data.Order.Factories
import Init.Data.Order.Lemmas
public section
open Std
open Lean in
set_option hygiene false in
macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
@@ -206,6 +210,19 @@ macro "declare_uint_theorems" typeName:ident bits:term:arg : command => do
protected theorem le_antisymm {a b : $typeName} (h₁ : a b) (h₂ : b a) : a = b :=
le_antisymm_iff.2 h₁, h₂
open $typeName renaming
le_refl le_refl', le_antisymm le_antisymm', le_total le_total', le_trans le_trans' in
instance instIsLinearOrder : IsLinearOrder $typeName := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply le_antisymm'
case le_total => constructor; apply le_total'
case le_trans => constructor; apply le_trans'
open $typeName renaming not_le not_le'
instance : LawfulOrderLT $typeName where
lt_iff _ _ := by
simp [ not_le', Decidable.imp_iff_not_or, Std.Total.total]
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := (rfl)
@[simp] protected theorem ofNat_toNat {x : $typeName} : ofNat x.toNat = x := by

View File

@@ -11,15 +11,17 @@ public import Init.Data.Vector.Lemmas
public import all Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lex.Lemmas
import Init.Data.Range.Polymorphic.Lemmas
import Init.Data.Order.Lemmas
public section
open Std
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace Vector
/-! ### Lexicographic ordering -/
@[simp] theorem lt_toArray [LT α] {xs ys : Vector α n} : xs.toArray < ys.toArray xs < ys := Iff.rfl
@@ -96,27 +98,35 @@ instance [LT α]
Trans (· < · : Vector α n Vector α n Prop) (· < ·) (· < ·) where
trans h₁ h₂ := Vector.lt_trans h₁ h₂
protected theorem lt_of_le_of_lt [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
protected theorem lt_of_le_of_lt [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
Array.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α]
[Std.Irrefl (· < · : α α Prop)]
@[deprecated Vector.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.lt_of_le_of_lt h₁ h₂
protected theorem le_trans [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
fun h₃ => h₁ (Vector.lt_of_le_of_lt h₂ h₃)
@[deprecated Vector.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Vector α n} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
fun h₃ => h₁ (Vector.lt_of_le_of_lt h₂ h₃)
letI := LE.ofLT α
haveI : IsLinearOrder α := IsLinearOrder.of_lt
Array.le_trans h₁ h₂
instance [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)] :
instance [LT α] [LE α] [LawfulOrderLT α] [IsLinearOrder α] :
Trans (· · : Vector α n Vector α n Prop) (· ·) (· ·) where
trans h₁ h₂ := Vector.le_trans h₁ h₂
@@ -129,30 +139,44 @@ instance [LT α]
Std.Asymm (· < · : Vector α n Vector α n Prop) where
asymm _ _ := Vector.lt_asymm
protected theorem le_total [LT α]
[i : Std.Total (¬ · < · : α α Prop)] (xs ys : Vector α n) : xs ys ys xs :=
protected theorem le_total [LT α] [i : Std.Asymm (· < · : α α Prop)] (xs ys : Vector α n) :
xs ys ys xs :=
Array.le_total _ _
instance [LT α]
[Std.Total (¬ · < · : α α Prop)] :
protected theorem le_antisymm [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α]
{xs ys : Vector α n} (h₁ : xs ys) (h₂ : ys xs) : xs = ys :=
Vector.toArray_inj.mp <| Array.le_antisymm h₁ h₂
instance [LT α] [Std.Asymm (· < · : α α Prop)] :
Std.Total (· · : Vector α n Vector α n Prop) where
total := Vector.le_total
instance [LT α] [LE α] [IsLinearOrder α] [LawfulOrderLT α] :
IsLinearOrder (Vector α n) := by
apply IsLinearOrder.of_le
case le_antisymm => constructor; apply Vector.le_antisymm
case le_total => constructor; apply Vector.le_total
case le_trans => constructor; apply Vector.le_trans
@[simp] protected theorem not_lt [LT α]
{xs ys : Vector α n} : ¬ xs < ys ys xs := Iff.rfl
@[simp] protected theorem not_le [LT α]
{xs ys : Vector α n} : ¬ ys xs xs < ys := Classical.not_not
instance [LT α] [Std.Asymm (· < · : α α Prop)] : LawfulOrderLT (Vector α n) where
lt_iff _ _ := by
open Classical in
simp [ Vector.not_le, Decidable.imp_iff_not_or, Std.Total.total]
protected theorem le_of_lt [LT α]
[i : Std.Total (¬ · < · : α α Prop)]
[i : Std.Asymm (· < · : α α Prop)]
{xs ys : Vector α n} (h : xs < ys) : xs ys :=
Array.le_of_lt h
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Total (¬ · < · : α α Prop)]
{xs ys : Vector α n} : xs ys xs < ys xs = ys := by
simpa using Array.le_iff_lt_or_eq (xs := xs.toArray) (ys := ys.toArray)
@@ -222,7 +246,6 @@ protected theorem lt_iff_exists [LT α] {xs ys : Vector α n} :
simp_all [Array.lt_iff_exists]
protected theorem le_iff_exists [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {xs ys : Vector α n} :
xs ys
@@ -237,7 +260,6 @@ theorem append_left_lt [LT α] {xs : Vector α n} {ys ys' : Vector α m} (h : ys
simpa using Array.append_left_lt h
theorem append_left_le [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
{xs : Vector α n} {ys ys' : Vector α m} (h : ys ys') :
@@ -250,10 +272,8 @@ protected theorem map_lt [LT α] [LT β]
simpa using Array.map_lt w h
protected theorem map_le [LT α] [LT β]
[Std.Irrefl (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Irrefl (· < · : β β Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
{xs ys : Vector α n} {f : α β} (w : x y, x < y f x < f y) (h : xs ys) :

View File

@@ -713,7 +713,7 @@ A `simpArg` is either a `*`, `-lemma` or a simp lemma specification
meta def simpArg := simpStar.binary `orelse (simpErase.binary `orelse simpLemma)
/-- A simp args list is a list of `simpArg`. This is the main argument to `simp`. -/
syntax simpArgs := " [" simpArg,* "]"
syntax simpArgs := " [" simpArg,*,? "]"
/--
A `dsimpArg` is similar to `simpArg`, but it does not have the `simpStar` form
@@ -722,7 +722,7 @@ because it does not make sense to use hypotheses in `dsimp`.
meta def dsimpArg := simpErase.binary `orelse simpLemma
/-- A dsimp args list is a list of `dsimpArg`. This is the main argument to `dsimp`. -/
syntax dsimpArgs := " [" dsimpArg,* "]"
syntax dsimpArgs := " [" dsimpArg,*,? "]"
/-- The common arguments of `simp?` and `simp?!`. -/
syntax simpTraceArgsRest := optConfig (discharger)? (&" only")? (simpArgs)? (ppSpace location)?

View File

@@ -177,8 +177,8 @@ where
catch _ => pure ()
def addAndCompile (decl : Declaration) : CoreM Unit := do
def addAndCompile (decl : Declaration) (logCompileErrors : Bool := true) : CoreM Unit := do
addDecl decl
compileDecl decl
compileDecl decl (logErrors := logCompileErrors)
end Lean

View File

@@ -8,7 +8,6 @@ module
prelude
public import Lean.Runtime
public import Lean.Compiler.IR.CompilerM
public import Lean.Compiler.IR.LiveVars
public section
@@ -19,17 +18,99 @@ This transformation is applied before lower level optimizations
that introduce the instructions `release` and `set`
-/
structure VarProjInfo where
parent? : Option VarId
children : VarIdSet
deriving Inhabited
abbrev VarProjMap := Std.HashMap VarId VarProjInfo
namespace CollectProjInfo
structure State where
varMap : VarProjMap := {}
borrowedParams : VarIdSet := {}
abbrev M := StateM State
private def visitParam (p : Param) : M Unit :=
modify fun s => { s with
varMap := s.varMap.insert p.x {
parent? := none
children := {}
}
borrowedParams :=
if p.borrow && p.ty.isPossibleRef then
s.borrowedParams.insert p.x
else s.borrowedParams
}
private partial def visitFnBody (b : FnBody) : M Unit := do
match b with
| .vdecl x _ e b =>
match e with
| .proj _ parent =>
modify fun s => { s with
varMap := s.varMap.modify parent fun info =>
{ info with children := info.children.insert x }
}
modify fun s => { s with
varMap := s.varMap.insert x {
parent? := some parent
children := {}
}
}
| .reset _ x =>
if let some (some parent) := ( get).varMap.get? x |>.map (·.parent?) then
modify fun s => { s with
varMap := s.varMap.modify parent fun info =>
{ info with children := info.children.erase x }
}
| _ => pure ()
visitFnBody b
| .jdecl _ ps v b =>
ps.forM visitParam
visitFnBody v
visitFnBody b
| .case _ _ _ alts => alts.forM (visitFnBody ·.body)
| _ => if !b.isTerminal then visitFnBody b.body
private partial def collectProjInfo (ps : Array Param) (b : FnBody)
: VarProjMap × VarIdSet := Id.run do
let _, { varMap, borrowedParams } := go |>.run { }
return varMap, borrowedParams
where go : M Unit := do
ps.forM visitParam
visitFnBody b
end CollectProjInfo
structure VarInfo where
type : IRType
isPossibleRef : Bool
isDefiniteRef: Bool
persistent : Bool
inheritsBorrowFromParam : Bool
deriving Inhabited
abbrev VarMap := Std.TreeMap VarId VarInfo (fun x y => compare x.idx y.idx)
structure LiveVars where
vars : VarIdSet
borrows : VarIdSet := {}
deriving Inhabited
@[inline]
def LiveVars.merge (liveVars1 liveVars2 : LiveVars) : LiveVars :=
let vars := liveVars1.vars.merge liveVars2.vars
let borrows := liveVars1.borrows.merge liveVars2.borrows
{ vars, borrows }
abbrev JPLiveVarMap := Std.TreeMap JoinPointId LiveVars (fun x y => compare x.idx y.idx)
structure Context where
env : Environment
decls : Array Decl
borrowedParams : VarIdSet
varProjMap : VarProjMap
varMap : VarMap := {}
jpLiveVarMap : JPLiveVarMap := {} -- map: join point => live variables
localCtx : LocalContext := {} -- we use it to store the join point declarations
@@ -43,31 +124,93 @@ def getVarInfo (ctx : Context) (x : VarId) : VarInfo :=
def getJPParams (ctx : Context) (j : JoinPointId) : Array Param :=
ctx.localCtx.getJPParams j |>.get!
def getJPLiveVars (ctx : Context) (j : JoinPointId) : LiveVarSet :=
ctx.jpLiveVarMap.get? j |>.getD {}
@[specialize]
private partial def addDescendants (ctx : Context) (x : VarId) (s : VarIdSet)
(shouldAdd : VarId Bool := fun _ => true) : VarIdSet :=
if let some info := ctx.varProjMap.get? x then
info.children.foldl (init := s) fun s child =>
let s := if shouldAdd child then s.insert child else s
addDescendants ctx child s shouldAdd
else s
def mustConsume (ctx : Context) (x : VarId) : Bool :=
let info := getVarInfo ctx x
info.type.isPossibleRef && !info.inheritsBorrowFromParam
private def mkRetLiveVars (ctx : Context) : LiveVars :=
let borrows := ctx.borrowedParams.foldl (init := {}) fun borrows x =>
addDescendants ctx x (borrows.insert x)
{ vars := {}, borrows }
def getJPLiveVars (ctx : Context) (j : JoinPointId) : LiveVars :=
ctx.jpLiveVarMap.get! j
@[specialize]
private def useVar (ctx : Context) (x : VarId) (liveVars : LiveVars)
(shouldBorrow : VarId Bool := fun _ => true) : LiveVars := Id.run do
let contains, vars := liveVars.vars.containsThenInsert x
let borrows := if contains then
liveVars.borrows
else
addDescendants ctx x liveVars.borrows fun y =>
!liveVars.vars.contains y && shouldBorrow y
return { vars, borrows }
@[inline]
private def bindVar (x : VarId) (liveVars : LiveVars) : LiveVars :=
let vars := liveVars.vars.erase x
let borrows := liveVars.borrows.erase x
{ vars, borrows }
@[inline]
private def useArg (ctx : Context) (args : Array Arg) (arg : Arg) (liveVars : LiveVars) : LiveVars :=
match arg with
| .var x => useVar ctx x liveVars fun y =>
args.all fun arg =>
match arg with
| .var z => y != z
| .erased => true
| .erased => liveVars
private def useArgs (ctx : Context) (args : Array Arg) (liveVars : LiveVars) : LiveVars :=
args.foldl (init := liveVars) fun liveVars arg => useArg ctx args arg liveVars
private def useExpr (ctx : Context) (e : Expr) (liveVars : LiveVars) : LiveVars :=
match e with
| .proj _ x | .uproj _ x | .sproj _ _ x | .box _ x | .unbox x | .reset _ x | .isShared x =>
useVar ctx x liveVars
| .ctor _ ys | .fap _ ys | .pap _ ys =>
useArgs ctx ys liveVars
| .ap x ys | .reuse x _ _ ys =>
let liveVars := useVar ctx x liveVars
useArgs ctx ys liveVars
| .lit _ => liveVars
@[inline] def addInc (ctx : Context) (x : VarId) (b : FnBody) (n := 1) : FnBody :=
let info := getVarInfo ctx x
if n == 0 then b else .inc x n (!info.type.isDefiniteRef) info.persistent b
if n == 0 then b else .inc x n (!info.isDefiniteRef) info.persistent b
@[inline] def addDec (ctx : Context) (x : VarId) (b : FnBody) : FnBody :=
let info := getVarInfo ctx x
.dec x 1 (!info.type.isDefiniteRef) info.persistent b
.dec x 1 (!info.isDefiniteRef) info.persistent b
private def updateRefUsingCtorInfo (ctx : Context) (x : VarId) (c : CtorInfo) : Context :=
let m := ctx.varMap
{ ctx with
varMap := match m.get? x with
| some info => m.insert x { info with type := c.type }
| none => m }
| some info =>
let isPossibleRef := c.type.isPossibleRef
let isDefiniteRef := c.type.isDefiniteRef
m.insert x { info with isPossibleRef, isDefiniteRef }
| none => m
}
private def addDecForAlt (ctx : Context) (caseLiveVars altLiveVars : LiveVarSet) (b : FnBody) : FnBody :=
caseLiveVars.foldl (init := b) fun b x =>
if !altLiveVars.contains x && mustConsume ctx x then addDec ctx x b else b
private def addDecForAlt (ctx : Context) (caseLiveVars altLiveVars : LiveVars) (b : FnBody) : FnBody :=
caseLiveVars.vars.foldl (init := b) fun b x =>
let info := getVarInfo ctx x
if !altLiveVars.vars.contains x then
if info.isPossibleRef && !caseLiveVars.borrows.contains x then
addDec ctx x b
else b
else if caseLiveVars.borrows.contains x && !altLiveVars.borrows.contains x then
addInc ctx x b
else b
/-- `isFirstOcc xs x i = true` if `xs[i]` is the first occurrence of `xs[i]` in `xs` -/
private def isFirstOcc (xs : Array Arg) (i : Nat) : Bool :=
@@ -98,29 +241,29 @@ private def getNumConsumptions (x : VarId) (ys : Array Arg) (consumeParamPred :
| .erased => n
| .var y => if x == y && consumeParamPred i then n+1 else n
private def addIncBeforeAux (ctx : Context) (xs : Array Arg) (consumeParamPred : Nat Bool) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
private def addIncBeforeAux (ctx : Context) (xs : Array Arg) (consumeParamPred : Nat Bool) (b : FnBody) (liveVarsAfter : LiveVars) : FnBody :=
xs.size.fold (init := b) fun i _ b =>
let x := xs[i]
match x with
| .erased => b
| .var x =>
let info := getVarInfo ctx x
if !info.type.isPossibleRef || !isFirstOcc xs i then b
if !info.isPossibleRef || !isFirstOcc xs i then b
else
let numConsumptions := getNumConsumptions x xs consumeParamPred
let numIncs :=
if info.inheritsBorrowFromParam ||
liveVarsAfter.contains x || -- `x` is live after executing instruction
if liveVarsAfter.vars.contains x || -- `x` is live after executing instruction
liveVarsAfter.borrows.contains x ||
isBorrowParamAux x xs consumeParamPred -- `x` is used in a position that is passed as a borrow reference
then numConsumptions
else numConsumptions - 1
addInc ctx x b numIncs
private def addIncBefore (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
private def addIncBefore (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (liveVarsAfter : LiveVars) : FnBody :=
addIncBeforeAux ctx xs (fun i => ! ps[i]!.borrow) b liveVarsAfter
/-- See `addIncBeforeAux`/`addIncBefore` for the procedure that inserts `inc` operations before an application. -/
private def addDecAfterFullApp (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
private def addDecAfterFullApp (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVars) : FnBody :=
xs.size.fold (init := b) fun i _ b =>
match xs[i] with
| .erased => b
@@ -129,22 +272,27 @@ private def addDecAfterFullApp (ctx : Context) (xs : Array Arg) (ps : Array Para
and it has been borrowed by the application.
Remark: `x` may occur multiple times in the application (e.g., `f x y x`).
This is why we check whether it is the first occurrence. -/
if mustConsume ctx x && isFirstOcc xs i && isBorrowParam x xs ps && !bLiveVars.contains x then
let info := getVarInfo ctx x
if info.isPossibleRef &&
isFirstOcc xs i &&
isBorrowParam x xs ps &&
!bLiveVars.vars.contains x &&
!bLiveVars.borrows.contains x then
addDec ctx x b
else b
private def addIncBeforeConsumeAll (ctx : Context) (xs : Array Arg) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
private def addIncBeforeConsumeAll (ctx : Context) (xs : Array Arg) (b : FnBody) (liveVarsAfter : LiveVars) : FnBody :=
addIncBeforeAux ctx xs (fun _ => true) b liveVarsAfter
/-- Add `dec` instructions for parameters that are references, are not alive in `b`, and are not borrow.
That is, we must make sure these parameters are consumed. -/
private def addDecForDeadParams (ctx : Context) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody × LiveVarSet :=
private def addDecForDeadParams (ctx : Context) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVars) : FnBody × LiveVars :=
ps.foldl (init := b, bLiveVars) fun b, bLiveVars p =>
let b :=
if !p.borrow && p.ty.isPossibleRef && !bLiveVars.contains p.x then
if !p.borrow && p.ty.isPossibleRef && !bLiveVars.vars.contains p.x then
addDec ctx p.x b
else b
let bLiveVars := bLiveVars.erase p.x
let bLiveVars := bindVar p.x bLiveVars
b, bLiveVars
private def isPersistent : Expr Bool
@@ -165,30 +313,32 @@ private def typeForScalarBoxedInTaggedPtr? (v : Expr) : Option IRType :=
| _ => none
private def updateVarInfo (ctx : Context) (x : VarId) (t : IRType) (v : Expr) : Context :=
let inheritsBorrowFromParam :=
match v with
| .proj _ x => match ctx.varMap.get? x with
| some info => info.inheritsBorrowFromParam
| none => false
| _ => false
let type := typeForScalarBoxedInTaggedPtr? v |>.getD t
let isPossibleRef := type.isPossibleRef
let isDefiniteRef := type.isDefiniteRef
{ ctx with
varMap := ctx.varMap.insert x {
type := typeForScalarBoxedInTaggedPtr? v |>.getD t
isPossibleRef
isDefiniteRef
persistent := isPersistent v,
inheritsBorrowFromParam
}
}
private def addDecIfNeeded (ctx : Context) (x : VarId) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
if mustConsume ctx x && !bLiveVars.contains x then addDec ctx x b else b
private def addDecIfNeeded (ctx : Context) (x : VarId) (b : FnBody) (bLiveVars : LiveVars) : FnBody :=
let info := getVarInfo ctx x
if info.isPossibleRef &&
!bLiveVars.vars.contains x &&
!bLiveVars.borrows.contains x then
addDec ctx x b
else b
private def processVDecl (ctx : Context) (z : VarId) (t : IRType) (v : Expr) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody × LiveVarSet :=
private def processVDecl (ctx : Context) (z : VarId) (t : IRType) (v : Expr) (b : FnBody) (bLiveVars : LiveVars) : FnBody × LiveVars :=
let b := match v with
| .ctor _ ys | .reuse _ _ _ ys | .pap _ ys =>
addIncBeforeConsumeAll ctx ys (.vdecl z t v b) bLiveVars
| .proj _ x =>
let b := addDecIfNeeded ctx x b bLiveVars
let b := if !(getVarInfo ctx x).inheritsBorrowFromParam then addInc ctx z b else b
let b := if !bLiveVars.borrows.contains z then addInc ctx z b else b
.vdecl z t v b
| .uproj _ x | .sproj _ _ x | .unbox x =>
.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
@@ -202,16 +352,19 @@ private def processVDecl (ctx : Context) (z : VarId) (t : IRType) (v : Expr) (b
addIncBeforeConsumeAll ctx ysx (.vdecl z t v b) bLiveVars
| .lit _ | .box .. | .reset .. | .isShared _ =>
.vdecl z t v b
let liveVars := updateLiveVars v bLiveVars
let liveVars := liveVars.erase z
let liveVars := useExpr ctx v bLiveVars
let liveVars := bindVar z liveVars
b, liveVars
def updateVarInfoWithParams (ctx : Context) (ps : Array Param) : Context :=
let m := ps.foldl (init := ctx.varMap) fun m p =>
m.insert p.x { type := p.ty, persistent := false, inheritsBorrowFromParam := p.borrow }
m.insert p.x {
isPossibleRef := p.ty.isPossibleRef
isDefiniteRef := p.ty.isDefiniteRef
persistent := false }
{ ctx with varMap := m }
partial def visitFnBody (b : FnBody) (ctx : Context) : FnBody × LiveVarSet :=
partial def visitFnBody (b : FnBody) (ctx : Context) : FnBody × LiveVars :=
match b with
| .vdecl x t v b =>
let ctx := updateVarInfo ctx x t v
@@ -230,15 +383,15 @@ partial def visitFnBody (b : FnBody) (ctx : Context) : FnBody × LiveVarSet :=
| .uset x i y b =>
let b, s := visitFnBody b ctx
-- We don't need to insert `y` since we only need to track live variables that are references at runtime
let s := s.insert x
let s := useVar ctx x s
.uset x i y b, s
| .sset x i o y t b =>
let b, s := visitFnBody b ctx
-- We don't need to insert `y` since we only need to track live variables that are references at runtime
let s := s.insert x
let s := useVar ctx x s
.sset x i o y t b, s
| .case tid x xType alts =>
let alts : Array (Alt × LiveVarSet) := alts.map fun alt => match alt with
let alts : Array (Alt × LiveVars) := alts.map fun alt => match alt with
| .ctor c b =>
let ctx := updateRefUsingCtorInfo ctx x c
let b, altLiveVars := visitFnBody b ctx
@@ -246,9 +399,10 @@ partial def visitFnBody (b : FnBody) (ctx : Context) : FnBody × LiveVarSet :=
| .default b =>
let b, altLiveVars := visitFnBody b ctx
.default b, altLiveVars
let caseLiveVars : LiveVarSet := alts.foldl (init := {}) fun liveVars _, altLiveVars =>
liveVars.merge altLiveVars
let caseLiveVars := caseLiveVars.insert x
let caseLiveVars := alts.foldl (init := { vars := {}, borrows := {} })
fun liveVars _, altLiveVars =>
liveVars.merge altLiveVars
let caseLiveVars := useVar ctx x caseLiveVars
let alts := alts.map fun alt, altLiveVars => match alt with
| .ctor c b =>
let ctx := updateRefUsingCtorInfo ctx x c
@@ -258,29 +412,32 @@ partial def visitFnBody (b : FnBody) (ctx : Context) : FnBody × LiveVarSet :=
let b := addDecForAlt ctx caseLiveVars altLiveVars b
.default b
.case tid x xType alts, caseLiveVars
| .ret x =>
match x with
| .var x =>
let info := getVarInfo ctx x
let b :=
if info.type.isPossibleRef && info.inheritsBorrowFromParam then
addInc ctx x b
else b
b, mkLiveVarSet x
| .erased => b, {}
| .jmp j xs =>
let jLiveVars := getJPLiveVars ctx j
let ps := getJPParams ctx j
let b := addIncBefore ctx xs ps b jLiveVars
let bLiveVars := collectLiveVars b ctx.jpLiveVarMap
let bLiveVars := useArgs ctx xs jLiveVars
b, bLiveVars
| .unreachable => .unreachable, {}
| _ => b, {} -- unreachable if well-formed
| .ret x =>
let liveVars := mkRetLiveVars ctx
match x with
| .var x =>
let info := ctx.varMap.get! x
let liveVars := useVar ctx x liveVars
let b :=
if info.isPossibleRef && liveVars.borrows.contains x then
addInc ctx x b
else b
b, liveVars
| .erased => b, liveVars
| .unreachable => .unreachable, mkRetLiveVars ctx
| .set .. | .setTag .. | .inc .. | .dec .. | .del .. => unreachable!
partial def visitDecl (env : Environment) (decls : Array Decl) (d : Decl) : Decl :=
match d with
| .fdecl (xs := xs) (body := b) .. =>
let ctx := updateVarInfoWithParams { env, decls } xs
let varProjMap, borrowedParams := CollectProjInfo.collectProjInfo xs b
let ctx := updateVarInfoWithParams { env, decls, borrowedParams, varProjMap } xs
let b, bLiveVars := visitFnBody b ctx
let b, _ := addDecForDeadParams ctx xs b bLiveVars
d.updateBody! b

View File

@@ -23,7 +23,7 @@ inductive Phase where
| base
/-- In this phase polymorphism has been eliminated. -/
| mono
deriving Inhabited
deriving Inhabited, BEq
/--
The state managed by the `CompilerM` `Monad`.

View File

@@ -108,20 +108,31 @@ def run (declNames : Array Name) : CompilerM (Array IR.Decl) := withAtLeastMaxRe
if let some info getDeclInfo? declName then
if !(isValidMainType info.type) then
throwError "`main` function must have type `(List String →)? IO (UInt32 | Unit | PUnit)`"
let mut decls declNames.mapM toDecl
decls := markRecDecls decls
let decls declNames.mapM toDecl
let decls := markRecDecls decls
let manager getPassManager
let isCheckEnabled := compiler.check.get ( getOptions)
for pass in manager.passes do
decls withTraceNode `Compiler (fun _ => return m!"compiler phase: {pass.phase}, pass: {pass.name}") do
withPhase pass.phase <| pass.run decls
withPhase pass.phaseOut <| checkpoint pass.name decls (isCheckEnabled || pass.shouldAlwaysRunCheck)
let decls profileitM Exception "compilation (LCNF base)" ( getOptions) do
let mut decls := decls
for pass in manager.basePasses do
decls withTraceNode `Compiler (fun _ => return m!"compiler phase: {pass.phase}, pass: {pass.name}") do
withPhase pass.phase <| pass.run decls
withPhase pass.phaseOut <| checkpoint pass.name decls (isCheckEnabled || pass.shouldAlwaysRunCheck)
return decls
let decls profileitM Exception "compilation (LCNF mono)" ( getOptions) do
let mut decls := decls
for pass in manager.monoPasses do
decls withTraceNode `Compiler (fun _ => return m!"compiler phase: {pass.phase}, pass: {pass.name}") do
withPhase pass.phase <| pass.run decls
withPhase pass.phaseOut <| checkpoint pass.name decls (isCheckEnabled || pass.shouldAlwaysRunCheck)
return decls
if ( Lean.isTracingEnabledFor `Compiler.result) then
for decl in decls do
let decl normalizeFVarIds decl
Lean.addTrace `Compiler.result m!"size: {decl.size}\n{← ppDecl' decl}"
let irDecls IR.toIR decls
IR.compile irDecls
profileitM Exception "compilation (IR)" ( getOptions) do
let irDecls IR.toIR decls
IR.compile irDecls
end PassManager
@@ -134,9 +145,8 @@ def showDecl (phase : Phase) (declName : Name) : CoreM Format := do
@[export lean_lcnf_compile_decls]
def main (declNames : Array Name) : CoreM Unit := do
profileitM Exception "compilation" ( getOptions) do
withTraceNode `Compiler (fun _ => return m!"compiling: {declNames}") do
CompilerM.run <| discard <| PassManager.run declNames
withTraceNode `Compiler (fun _ => return m!"compiling: {declNames}") do
CompilerM.run <| discard <| PassManager.run declNames
builtin_initialize
registerTraceClass `Compiler.init (inherited := true)

View File

@@ -73,6 +73,8 @@ Can be used to install, remove, replace etc. passes by tagging a declaration
of type `PassInstaller` with the `cpass` attribute.
-/
structure PassInstaller where
/-- Affected phase. -/
phase : Phase
/--
When the installer is run this function will receive a list of all
current `Pass`es and return a new one, this can modify the list (and
@@ -86,7 +88,8 @@ The `PassManager` used to store all `Pass`es that will be run within
pipeline.
-/
structure PassManager where
passes : Array Pass
basePasses : Array Pass
monoPasses : Array Pass
deriving Inhabited
instance : ToString Phase where
@@ -106,40 +109,51 @@ end Pass
namespace PassManager
def validate (manager : PassManager) : CoreM Unit := do
let mut current := .base
for pass in manager.passes do
if ¬(current pass.phase) then
throwError s!"{pass.name} has phase {pass.phase} but should at least have {current}"
current := pass.phase
private def validatePasses (phase : Phase) (passes : Array Pass) : CoreM Unit := do
for pass in passes do
if pass.phase != phase then
throwError s!"{pass.name} has phase {pass.phase} but should have {phase}"
def findHighestOccurrence (targetName : Name) (passes : Array Pass) : CoreM Nat := do
def validate (manager : PassManager) : CoreM Unit := do
validatePasses .base manager.basePasses
validatePasses .mono manager.monoPasses
def findOccurrenceBounds (targetName : Name) (passes : Array Pass) : CoreM (Nat × Nat) := do
let mut lowest := none
let mut highest := none
for pass in passes do
if pass.name == targetName then
lowest := if lowest.isNone then some pass.occurrence else lowest
highest := some pass.occurrence
let some val := highest | throwError s!"Could not find any occurrence of {targetName}"
return val
let some lowestVal, some highestVal := Prod.mk lowest highest | throwError s!"Could not find any occurrence of {targetName}"
return lowestVal, highestVal
end PassManager
namespace PassInstaller
def installAtEnd (p : Pass) : PassInstaller where
def installAtEnd (phase : Phase) (p : Pass) : PassInstaller where
phase
install passes := return passes.push p
def append (passesNew : Array Pass) : PassInstaller where
def append (phase : Phase) (passesNew : Array Pass) : PassInstaller where
phase
install passes := return passes ++ passesNew
def withEachOccurrence (targetName : Name) (f : Nat PassInstaller) : PassInstaller where
def withEachOccurrence (phase : Phase) (targetName : Name) (f : Nat PassInstaller) : PassInstaller where
phase
install passes := do
let highestOccurrence PassManager.findHighestOccurrence targetName passes
let lowestOccurrence, highestOccurrence PassManager.findOccurrenceBounds targetName passes
let mut passes := passes
for occurrence in *...=highestOccurrence do
passes f occurrence |>.install passes
for occurrence in lowestOccurrence...=highestOccurrence do
let installer := f occurrence
if installer.phase != phase then
panic! "phase mismatch"
passes installer.install passes
return passes
def installAfter (targetName : Name) (p : Pass Pass) (occurrence : Nat := 0) : PassInstaller where
def installAfter (phase : Phase) (targetName : Name) (p : Pass Pass) (occurrence : Nat := 0) : PassInstaller where
phase
install passes :=
if let some idx := passes.findFinIdx? (fun p => p.name == targetName && p.occurrence == occurrence) then
let passUnderTest := passes[idx]
@@ -147,10 +161,11 @@ def installAfter (targetName : Name) (p : Pass → Pass) (occurrence : Nat := 0)
else
throwError s!"Tried to insert pass after {targetName}, occurrence {occurrence} but {targetName} is not in the pass list"
def installAfterEach (targetName : Name) (p : Pass Pass) : PassInstaller :=
withEachOccurrence targetName (installAfter targetName p ·)
def installAfterEach (phase : Phase) (targetName : Name) (p : Pass Pass) : PassInstaller :=
withEachOccurrence phase targetName (installAfter phase targetName p ·)
def installBefore (targetName : Name) (p : Pass Pass) (occurrence : Nat := 0): PassInstaller where
def installBefore (phase : Phase) (targetName : Name) (p : Pass Pass) (occurrence : Nat := 0): PassInstaller where
phase
install passes :=
if let some idx := passes.findFinIdx? (fun p => p.name == targetName && p.occurrence == occurrence) then
let passUnderTest := passes[idx]
@@ -158,19 +173,24 @@ def installBefore (targetName : Name) (p : Pass → Pass) (occurrence : Nat := 0
else
throwError s!"Tried to insert pass after {targetName}, occurrence {occurrence} but {targetName} is not in the pass list"
def installBeforeEachOccurrence (targetName : Name) (p : Pass Pass) : PassInstaller :=
withEachOccurrence targetName (installBefore targetName p ·)
def installBeforeEachOccurrence (phase : Phase) (targetName : Name) (p : Pass Pass) : PassInstaller :=
withEachOccurrence phase targetName (installBefore phase targetName p ·)
def replacePass (targetName : Name) (p : Pass Pass) (occurrence : Nat := 0) : PassInstaller where
def replacePass (phase : Phase) (targetName : Name) (p : Pass Pass) (occurrence : Nat := 0) : PassInstaller where
phase
install passes := do
let some idx := passes.findIdx? (fun p => p.name == targetName && p.occurrence == occurrence) | throwError s!"Tried to replace {targetName}, occurrence {occurrence} but {targetName} is not in the pass list"
return passes.modify idx p
def replaceEachOccurrence (targetName : Name) (p : Pass Pass) : PassInstaller :=
withEachOccurrence targetName (replacePass targetName p ·)
def replaceEachOccurrence (phase : Phase) (targetName : Name) (p : Pass Pass) : PassInstaller :=
withEachOccurrence phase targetName (replacePass phase targetName p ·)
def run (manager : PassManager) (installer : PassInstaller) : CoreM PassManager := do
return { manager with passes := ( installer.install manager.passes) }
match installer.phase with
| .base =>
return { manager with basePasses := ( installer.install manager.basePasses) }
| .mono =>
return { manager with monoPasses := ( installer.install manager.monoPasses) }
private unsafe def getPassInstallerUnsafe (declName : Name) : CoreM PassInstaller := do
ofExcept <| ( getEnv).evalConstCheck PassInstaller ( getOptions) ``PassInstaller declName
@@ -180,7 +200,7 @@ private opaque getPassInstaller (declName : Name) : CoreM PassInstaller
def runFromDecl (manager : PassManager) (declName : Name) : CoreM PassManager := do
let installer getPassInstaller declName
let newState installer.run manager
let newState PassInstaller.run manager installer
newState.validate
return newState

View File

@@ -69,7 +69,7 @@ end Pass
open Pass
def builtinPassManager : PassManager := {
passes := #[
basePasses := #[
init,
pullInstances,
cse (shouldElimFunDecls := false),
@@ -93,6 +93,8 @@ def builtinPassManager : PassManager := {
-- pass must be run for each phase; see `base/monoTransparentDeclsExt`
inferVisibility (phase := .base),
toMono,
]
monoPasses := #[
simp (occurrence := 3) (phase := .mono),
reduceJpArity (phase := .mono),
structProjCases,

View File

@@ -75,7 +75,7 @@ where
let some decl getDecl? declName | failure
match decl.value with
| .code code =>
guard (decl.getArity == args.size)
guard (!decl.recursive && decl.getArity == args.size)
let params := decl.instantiateParamsLevelParams us
let code := code.instantiateValueLevelParams decl.levelParams us
let code betaReduce params code args (mustInline := true)

View File

@@ -110,35 +110,35 @@ private def assertAfterTest (test : SimpleTest) : TestInstallerM (Pass → Pass)
Install an assertion pass right after a specific occurrence of a pass,
default is first.
-/
def assertAfter (test : SimpleTest) (occurrence : Nat := 0): TestInstaller := do
def assertAfter (phase : Phase) (test : SimpleTest) (occurrence : Nat := 0): TestInstaller := do
let passUnderTestName := (read).passUnderTestName
let assertion assertAfterTest test
return .installAfter passUnderTestName assertion occurrence
return .installAfter phase passUnderTestName assertion occurrence
/--
Install an assertion pass right after each occurrence of a pass.
-/
def assertAfterEachOccurrence (test : SimpleTest) : TestInstaller := do
def assertAfterEachOccurrence (phase : Phase) (test : SimpleTest) : TestInstaller := do
let passUnderTestName := (read).passUnderTestName
let assertion assertAfterTest test
return .installAfterEach passUnderTestName assertion
return .installAfterEach phase passUnderTestName assertion
/--
Install an assertion pass right after a specific occurrence of a pass,
default is first. The assertion operates on a per declaration basis.
-/
def assertForEachDeclAfter (assertion : Pass Decl Bool) (msg : String) (occurrence : Nat := 0) : TestInstaller :=
def assertForEachDeclAfter (phase : Phase) (assertion : Pass Decl Bool) (msg : String) (occurrence : Nat := 0) : TestInstaller :=
let assertion := do
let pass getPassUnderTest
(getDecls).forM (fun decl => assert (assertion pass decl) msg)
assertAfter assertion occurrence
assertAfter phase assertion occurrence
/--
Install an assertion pass right after the each occurrence of a pass. The
assertion operates on a per declaration basis.
-/
def assertForEachDeclAfterEachOccurrence (assertion : Pass Decl Bool) (msg : String) : TestInstaller :=
assertAfterEachOccurrence <| do
def assertForEachDeclAfterEachOccurrence (phase : Phase) (assertion : Pass Decl Bool) (msg : String) : TestInstaller :=
assertAfterEachOccurrence phase <| do
let pass getPassUnderTest
(getDecls).forM (fun decl => assert (assertion pass decl) msg)
@@ -160,20 +160,20 @@ Replace a specific occurrence, default is first, of a pass with a wrapper one th
the user to provide an assertion which takes into account both the
declarations that were sent to and produced by the pass under test.
-/
def assertAround (test : InOutTest) (occurrence : Nat := 0) : TestInstaller := do
def assertAround (phase : Phase) (test : InOutTest) (occurrence : Nat := 0) : TestInstaller := do
let passUnderTestName := (read).passUnderTestName
let assertion assertAroundTest test
return .replacePass passUnderTestName assertion occurrence
return .replacePass phase passUnderTestName assertion occurrence
/--
Replace all occurrences of a pass with a wrapper one that allows
the user to provide an assertion which takes into account both the
declarations that were sent to and produced by the pass under test.
-/
def assertAroundEachOccurrence (test : InOutTest) : TestInstaller := do
def assertAroundEachOccurrence (phase : Phase) (test : InOutTest) : TestInstaller := do
let passUnderTestName := (read).passUnderTestName
let assertion assertAroundTest test
return .replaceEachOccurrence passUnderTestName assertion
return .replaceEachOccurrence phase passUnderTestName assertion
private def throwFixPointError (err : String) (firstResult secondResult : Array Decl) : CompilerM Unit := do
let mut err := err
@@ -189,7 +189,7 @@ Insert a pass after `passUnderTestName`, that ensures, that if
`passUnderTestName` is executed twice in a row, no change in the resulting
expression will occur, i.e. the pass is at a fix point.
-/
def assertIsAtFixPoint : TestInstaller :=
def assertIsAtFixPoint (phase : Phase) : TestInstaller :=
let test := do
let passUnderTest getPassUnderTest
let decls getDecls
@@ -203,51 +203,51 @@ def assertIsAtFixPoint : TestInstaller :=
else if decls != secondResult then
let err := s!"Pass {passUnderTest.name} did not reach a fixpoint, it either changed declarations or their order:\n"
throwFixPointError err decls secondResult
assertAfterEachOccurrence test
assertAfterEachOccurrence phase test
/--
Compare the overall sizes of the input and output of `passUnderTest` with `assertion`.
If `assertion inputSize outputSize` is `false` throw an exception with `msg`.
-/
def assertSize (assertion : Nat Nat Bool) (msg : String) : TestInstaller :=
def assertSize (phase : Phase) (assertion : Nat Nat Bool) (msg : String) : TestInstaller :=
let sumDeclSizes := fun decls => decls.map Decl.size |>.foldl (init := 0) (· + ·)
let assertion := (fun inputS outputS => Testing.assert (assertion inputS outputS) s!"{msg}: input size {inputS} output size {outputS}")
assertAroundEachOccurrence (do assertion (sumDeclSizes (getInputDecls)) (sumDeclSizes (getOutputDecls)))
assertAroundEachOccurrence phase (do assertion (sumDeclSizes (getInputDecls)) (sumDeclSizes (getOutputDecls)))
/--
Assert that the overall size of the `Decl`s in the compilation pipeline does not change
after `passUnderTestName`.
-/
def assertPreservesSize (msg : String) : TestInstaller :=
assertSize (· == ·) msg
def assertPreservesSize (phase : Phase) (msg : String) : TestInstaller :=
assertSize phase (· == ·) msg
/--
Assert that the overall size of the `Decl`s in the compilation pipeline gets reduced by `passUnderTestName`.
-/
def assertReducesSize (msg : String) : TestInstaller :=
assertSize (· > ·) msg
def assertReducesSize (phase : Phase) (msg : String) : TestInstaller :=
assertSize phase (· > ·) msg
/--
Assert that the overall size of the `Decl`s in the compilation pipeline gets reduced or stays unchanged
by `passUnderTestName`.
-/
def assertReducesOrPreservesSize (msg : String) : TestInstaller :=
assertSize (· ·) msg
def assertReducesOrPreservesSize (phase : Phase) (msg : String) : TestInstaller :=
assertSize phase (· ·) msg
/--
Assert that the pass under test produces `Decl`s that do not contain
`Expr.const constName` in their `Code.let` values anymore.
-/
def assertDoesNotContainConstAfter (constName : Name) (msg : String) : TestInstaller :=
assertForEachDeclAfterEachOccurrence
def assertDoesNotContainConstAfter (phase : Phase) (constName : Name) (msg : String) : TestInstaller :=
assertForEachDeclAfterEachOccurrence phase
fun _ decl =>
match decl.value with
| .code c => !c.containsConst constName
| .extern .. => true
msg
def assertNoFun : TestInstaller :=
assertAfter do
def assertNoFun (phase : Phase) : TestInstaller :=
assertAfter phase do
for decl in ( getDecls) do
decl.value.forCodeM fun
| .fun .. => throwError "declaration `{decl.name}` contains a local function declaration"

View File

@@ -90,8 +90,18 @@ partial def LetValue.toMono (e : LetValue) (resultFVar : FVarId) : ToMonoM LetVa
-- Decidable.decide is the identity function since Decidable
-- and Bool have the same runtime representation.
return args[1]!.toLetValue
else if declName == ``Quot.mk || declName == ``Quot.lcInv then
else if declName == ``Quot.mk then
return args[2]!.toLetValue
else if declName == ``Quot.lcInv then
match args[2]! with
| .fvar fvarId =>
let mut extraArgs : Array Arg := .emptyWithCapacity (args.size - 3)
for i in 3...args.size do
let arg argToMono args[i]!
extraArgs := extraArgs.push arg
return .fvar fvarId extraArgs
| .erased | .type _ =>
return .erased
else if declName == ``Nat.zero then
return .lit (.nat 0)
else if declName == ``Nat.succ then

View File

@@ -570,8 +570,8 @@ register_builtin_option stderrAsMessages : Bool := {
Creates snapshot reporting given `withIsolatedStreams` output and diagnostics and traces from the
given state.
-/
def mkSnapshot (output : String) (ctx : Context) (st : State)
(desc : String := by exact decl_name%.toString) : BaseIO Language.SnapshotTree := do
def mkSnapshot? (output : String) (ctx : Context) (st : State)
(desc : String := by exact decl_name%.toString) : BaseIO (Option Language.SnapshotTree) := do
let mut msgs := st.messages
if !output.isEmpty then
msgs := msgs.add {
@@ -580,7 +580,9 @@ def mkSnapshot (output : String) (ctx : Context) (st : State)
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
data := output
}
return .mk {
if !msgs.hasUnreported && st.traceState.traces.isEmpty && st.snapshotTasks.isEmpty then
return none
return some <| .mk {
desc
diagnostics := ( Language.Snapshot.Diagnostics.ofMessageLog msgs)
traces := st.traceState
@@ -617,7 +619,8 @@ def wrapAsyncAsSnapshot {α : Type} (act : α → CoreM Unit) (cancelTk? : Optio
let ctx readThe Core.Context
return fun a => do
match ( (f a).toBaseIO) with
| .ok (output, st) => mkSnapshot output ctx st desc
| .ok (output, st) =>
return ( mkSnapshot? output ctx st desc).getD (toSnapshotTree (default : SnapshotLeaf))
-- interrupt or abort exception as `try catch` above should have caught any others
| .error _ => default

View File

@@ -556,13 +556,12 @@ def elabUnsafe : TermElab := fun stx expectedType? =>
let .const unsafeFn unsafeLvls .. := t.getAppFn | unreachable!
let .defnInfo unsafeDefn getConstInfo unsafeFn | unreachable!
let implName mkAuxName `unsafe_impl
addDecl <| Declaration.defnDecl {
addDecl <| Declaration.opaqueDecl {
name := implName
type := unsafeDefn.type
levelParams := unsafeDefn.levelParams
value := ( mkOfNonempty unsafeDefn.type)
hints := .opaque
safety := .safe
isUnsafe := false
}
setImplementedBy implName unsafeFn
return mkAppN (Lean.mkConst implName unsafeLvls) t.getAppArgs

View File

@@ -49,26 +49,21 @@ def checkNotAlreadyDeclared {m} [Monad m] [MonadEnv m] [MonadError m] [MonadFina
addInfo declName
throwError "a non-private declaration '{.ofConstName declName true}' has already been declared"
/-- Declaration visibility modifier. That is, whether a declaration is regular, protected or private. -/
/-- Declaration visibility modifier. That is, whether a declaration is public or private or inherits its visibility from the outer scope. -/
inductive Visibility where
| regular | «protected» | «private» | «public»
| regular | «private» | «public»
deriving Inhabited
instance : ToString Visibility where
toString
| .regular => "regular"
| .private => "private"
| .protected => "protected"
| .public => "public"
def Visibility.isPrivate : Visibility Bool
| .private => true
| _ => false
def Visibility.isProtected : Visibility Bool
| .protected => true
| _ => false
def Visibility.isPublic : Visibility Bool
| .public => true
| _ => false
@@ -92,6 +87,7 @@ structure Modifiers where
stx : TSyntax ``Parser.Command.declModifiers := .missing
docString? : Option (TSyntax ``Parser.Command.docComment) := none
visibility : Visibility := Visibility.regular
isProtected : Bool := false
computeKind : ComputeKind := .regular
recKind : RecKind := RecKind.default
isUnsafe : Bool := false
@@ -99,7 +95,6 @@ structure Modifiers where
deriving Inhabited
def Modifiers.isPrivate (m : Modifiers) : Bool := m.visibility.isPrivate
def Modifiers.isProtected (m : Modifiers) : Bool := m.visibility.isProtected
def Modifiers.isPublic (m : Modifiers) : Bool := m.visibility.isPublic
def Modifiers.isInferredPublic (env : Environment) (m : Modifiers) : Bool :=
m.visibility.isInferredPublic env
@@ -147,8 +142,8 @@ instance : ToFormat Modifiers := ⟨fun m =>
++ (match m.visibility with
| .regular => []
| .private => [f!"private"]
| .protected => [f!"protected"]
| .public => [f!"public"])
++ (if m.isProtected then [f!"protected"] else [])
++ (match m.computeKind with | .regular => [] | .meta => [f!"meta"] | .noncomputable => [f!"noncomputable"])
++ (match m.recKind with | RecKind.partial => [f!"partial"] | RecKind.nonrec => [f!"nonrec"] | _ => [])
++ (if m.isUnsafe then [f!"unsafe"] else [])
@@ -176,18 +171,19 @@ def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers :
let docCommentStx := stx.raw[0]
let attrsStx := stx.raw[1]
let visibilityStx := stx.raw[2]
let protectedStx := stx.raw[3]
let computeKind :=
if stx.raw[3].isNone then
if stx.raw[4].isNone then
.regular
else if stx.raw[3][0].getKind == ``Parser.Command.meta then
else if stx.raw[4][0].getKind == ``Parser.Command.meta then
.meta
else
.noncomputable
let unsafeStx := stx.raw[4]
let unsafeStx := stx.raw[5]
let recKind :=
if stx.raw[5].isNone then
if stx.raw[6].isNone then
RecKind.default
else if stx.raw[5][0].getKind == ``Parser.Command.partial then
else if stx.raw[6][0].getKind == ``Parser.Command.partial then
RecKind.partial
else
RecKind.nonrec
@@ -197,14 +193,14 @@ def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers :
| some v =>
match v with
| `(Parser.Command.visibility| private) => pure .private
| `(Parser.Command.visibility| protected) => pure .protected
| `(Parser.Command.visibility| public) => pure .public
| _ => throwErrorAt v "unexpected visibility modifier"
let isProtected := !protectedStx.isNone
let attrs match attrsStx.getOptional? with
| none => pure #[]
| some attrs => elabDeclAttrs attrs
return {
stx, docString?, visibility, computeKind, recKind, attrs,
stx, docString?, visibility, isProtected, computeKind, recKind, attrs,
isUnsafe := !unsafeStx.isNone
}
@@ -213,12 +209,12 @@ Ensure the function has not already been declared, and apply the given visibilit
If `private`, return the updated name using our internal encoding for private names.
If `protected`, register `declName` as protected in the environment.
-/
def applyVisibility (visibility : Visibility) (declName : Name) : m Name := do
def applyVisibility (modifiers : Modifiers) (declName : Name) : m Name := do
let mut declName := declName
if !visibility.isInferredPublic ( getEnv) then
if !modifiers.visibility.isInferredPublic ( getEnv) then
declName := mkPrivateName ( getEnv) declName
checkNotAlreadyDeclared declName
if visibility matches .protected then
if modifiers.isProtected then
modifyEnv fun env => addProtected env declName
pure declName
@@ -246,16 +242,16 @@ def mkDeclName (currNamespace : Name) (modifiers : Modifiers) (shortName : Name)
shortName := Name.mkSimple s
currNamespace := p.replacePrefix `_root_ Name.anonymous
checkIfShadowingStructureField declName
let declName applyVisibility modifiers.visibility declName
match modifiers.visibility with
| Visibility.protected =>
let declName applyVisibility modifiers declName
if modifiers.isProtected then
match currNamespace with
| .str _ s => return (declName, Name.mkSimple s ++ shortName)
| _ =>
if shortName.isAtomic then
throwError "protected declarations must be in a namespace"
return (declName, shortName)
| _ => return (declName, shortName)
else
return (declName, shortName)
/--
`declId` is of the form

View File

@@ -6,8 +6,10 @@ Authors: Leonardo de Moura, Wojciech Nawrocki
module
prelude
public import Lean.Elab.App
public import Lean.Elab.Command
public import Lean.Elab.DeclarationRange
public import Lean.Elab.DeclNameGen
public meta import Lean.Parser.Command
public section
@@ -18,53 +20,189 @@ open Command
namespace Term
open Meta
/-- Result for `mkInst?` -/
structure MkInstResult where
instVal : Expr
instType : Expr
outParams : Array Expr := #[]
/-- Result for `mkInst` -/
private structure MkInstResult where
instType : Expr
instVal : Expr
private def throwDeltaDeriveFailure (className declName : Name) (msg? : Option MessageData) (suffix : MessageData := "") : MetaM α :=
let suffix := if let some msg := msg? then m!", {msg}{suffix}" else m!".{suffix}"
throwError "Failed to delta derive `{.ofConstName className}` instance for `{.ofConstName declName}`{suffix}"
/--
Construct an instance for `className out₁ ... outₙ type`.
The method support classes with a prefix of `outParam`s (e.g. `MonadReader`). -/
private partial def mkInst? (className : Name) (type : Expr) : MetaM (Option MkInstResult) := do
let rec go? (instType instTypeType : Expr) (outParams : Array Expr) : MetaM (Option MkInstResult) := do
let instTypeType whnfD instTypeType
unless instTypeType.isForall do
return none
let d := instTypeType.bindingDomain!
if d.isOutParam then
let mvar mkFreshExprMVar d
go? (mkApp instType mvar) (instTypeType.bindingBody!.instantiate1 mvar) (outParams.push mvar)
else
unless ( isDefEqGuarded ( inferType type) d) do
return none
let instType instantiateMVars (mkApp instType type)
let instVal synthInstance instType
return some { instVal, instType, outParams }
let instType mkConstWithFreshMVarLevels className
go? instType ( inferType instType) #[]
Constructs an instance of the class `classExpr` by figuring out the correct position to insert `val`
to create a type `className ... val ...` such that there is already an instance for it.
The `declVal` argument is the value to use in place of `val` when creating the new instance.
def processDefDeriving (className : Name) (declName : Name) : TermElabM Bool := do
try
let ConstantInfo.defnInfo info getConstInfo declName | return false
let some result mkInst? className info.value | return false
let instTypeNew := mkApp result.instType.appFn! (Lean.mkConst declName (info.levelParams.map mkLevelParam))
Meta.check instTypeNew
let instName liftMacroM <| mkUnusedBaseName (declName.appendBefore "inst" |>.appendAfter className.getString!)
addAndCompile <| Declaration.defnDecl {
name := instName
levelParams := info.levelParams
type := ( instantiateMVars instTypeNew)
value := ( instantiateMVars result.instVal)
hints := info.hints
safety := info.safety
}
addInstance instName AttributeKind.global (eval_prio default)
addDeclarationRangesFromSyntax instName ( getRef)
return true
catch _ =>
return false
Heuristics:
- `val` must not use an outParam.
- `val` should use an explicit parameter, or a parameter that has already been given a value.
- If there are multiple explicit parameters, we try each possibility.
- If the class has instance arguments, we require that they be synthesizable while synthesizing this instance.
While we could allow synthesis failure and abstract such instances,
we leave such conditional instances to be defined by users.
- If this all fails and `val` is a constant application, we try unfolding it once and try again.
For example, when deriving `MonadReader (ρ : outParam (Type u)) (m : Type u → Type v)`,
we will skip `ρ` and try using `m`.
Note that we try synthesizing instances even if there are still metavariables in the type.
If that succeeds, then one can abstract those metavariables and create a parameterized instance.
The abstraction is not done by this function.
Expects to be run with an empty message log.
-/
private partial def mkInst (classExpr : Expr) (declName : Name) (declVal val : Expr) : TermElabM MkInstResult := do
let classExpr whnfCore classExpr
let cls := classExpr.getAppFn
let (xs, bis, _) forallMetaTelescopeReducing ( inferType cls)
for x in xs, y in classExpr.getAppArgs do
x.mvarId!.assign y
let classExpr := mkAppN cls xs
let some className isClass? classExpr
| throwError "Failed to delta derive instance for `{.ofConstName declName}`, not a class:{indentExpr classExpr}"
let mut instMVars := #[]
for x in xs, bi in bis do
if !( x.mvarId!.isAssigned) then
-- Assumption: assigned inst implicits are already either solved or registered as synthetic
if bi.isInstImplicit then
x.mvarId!.setKind .synthetic
instMVars := instMVars.push x.mvarId!
let instVal mkFreshExprMVar classExpr (kind := .synthetic)
instMVars := instMVars.push instVal.mvarId!
let rec go (val : Expr) : TermElabM MkInstResult := do
let val whnfCore val
trace[Elab.Deriving] "Looking for arguments to `{classExpr}` that can be used for the value{indentExpr val}"
-- Save the metacontext so that we can try each option in turn
let state saveState
let valTy inferType val
let mut anyDefEqSuccess := false
let mut messages : MessageLog := {}
for x in xs, bi in bis, i in 0...xs.size do
unless bi.isExplicit do
continue
let decl x.mvarId!.getDecl
if decl.type.isOutParam then
continue
unless isMVarApp x do
/-
This is an argument supplied by the user, and it's not a `_`.
This is to avoid counterintuitive behavior, like in the following example.
Because `MyNat` unifies with `Nat`, it would otherwise generate an `HAdd MyNat Nat Nat` instance.
Instead it generates an `HAdd Nat MyNat Nat` instance.
```
def MyNat := Nat
deriving instance HAdd Nat for MyNat
```
Likely neither of these is the intended result, but the second is more justifiable.
It's possible to have it return `MyNat` using `deriving instance HAdd Nat _ MyNat for MyNat`.
-/
continue
unless isDefEqGuarded decl.type valTy <&&> isDefEqGuarded x val do
restoreState state
continue
anyDefEqSuccess := true
trace[Elab.Deriving] "Argument {i} gives option{indentExpr classExpr}"
try
-- Finish elaboration
synthesizeAppInstMVars instMVars classExpr
Term.synthesizeSyntheticMVarsNoPostponing
catch ex =>
trace[Elab.Deriving] "Option for argument {i} failed"
logException ex
messages := messages ++ ( Core.getMessageLog)
restoreState state
continue
if ( MonadLog.hasErrors) then
-- Sometimes elaboration only logs errors
trace[Elab.Deriving] "Option for argument {i} failed, logged errors"
messages := messages ++ ( Core.getMessageLog)
restoreState state
continue
-- Success
trace[Elab.Deriving] "Argument {i} option succeeded{indentExpr classExpr}"
-- Create the type for the declaration itself.
let xs' := xs.set! i declVal
let instType := mkAppN cls xs'
return { instType, instVal }
try
if let some val' unfoldDefinition? val then
return withTraceNode `Elab.Deriving (fun _ => return m!"Unfolded value to {val'}") <| go val'
catch ex =>
if !messages.hasErrors then
throw ex
Core.resetMessageLog
if !anyDefEqSuccess then
throwDeltaDeriveFailure className declName (m!"the class has no explicit non-out-param parameters where\
{indentExpr declVal}\n\
can be inserted.")
else
Core.setMessageLog (messages ++ ( Core.getMessageLog))
throwDeltaDeriveFailure className declName none
(.note m!"Delta deriving tries the following strategies: \
(1) inserting the definition into each explicit non-out-param parameter of a class and \
(2) unfolding definitions further.")
go val
/--
Delta deriving handler. Creates an instance of class `classStx` for `decl`.
The elaborated class expression may be underapplied (e.g. `Decidable` instead of `Decidable _`),
and may be `decl`.
If unfolding `decl` results in an underapplied lambda, then this enters the body of the lambda.
We prevent `classStx` from referring to these local variables; instead it's expected that one uses section variables.
This function can handle being run from within a nontrivial local context,
and it uses `mkValueTypeClosure` to construct the final instance.
-/
def processDefDeriving (classStx : Syntax) (decl : Expr) : TermElabM Unit := do
let decl whnfCore decl
let .const declName _ := decl.getAppFn
| throwError "Failed to delta derive instance, expecting a term of the form `C ...` where `C` is a constant, given{indentExpr decl}"
-- When the definition is private, the deriving handler will need access to the private scope,
-- and we make sure to put the instance in the private scope.
withoutExporting (when := isPrivateName declName) do
let ConstantInfo.defnInfo info getConstInfo declName
| throwError "Failed to delta derive instance, `{declName}` is not a definition."
let value := info.value.beta decl.getAppArgs
let result : Closure.MkValueTypeClosureResult
-- Assumption: users intend delta deriving to apply to the body of a definition, even if in the source code
-- the function is written as a lambda expression.
-- Furthermore, we don't use `forallTelescope` because users want to derive instances for monads.
lambdaTelescope value fun xs value => withoutErrToSorry do
let decl := mkAppN decl xs
-- Make these local variables inaccessible.
let lctx xs.foldlM (init := getLCtx) fun lctx x => do
pure <| lctx.setUserName x.fvarId! ( mkFreshUserName <| (lctx.find? x.fvarId!).get!.userName)
withLCtx' lctx do
let msgLog Core.getMessageLog
Core.resetMessageLog
try
-- We need to elaborate the class within this context to ensure metavariables can unify with `xs`.
let classExpr elabTerm classStx none
synthesizeSyntheticMVars (postpone := .partial)
if ( MonadLog.hasErrors) then
throwAbortTerm
-- We allow `classExpr` to be a pi type, to support giving more hypotheses to the derived instance.
-- (Possibly `classExpr` is not a type due to being underapplied, but `forallTelescopeReducing` tolerates this.)
-- We don't reduce because of abbreviations such as `DecidableEq`
forallTelescope classExpr fun _ classExpr => do
let result mkInst classExpr declName decl value
Closure.mkValueTypeClosure result.instType result.instVal (zetaDelta := true)
finally
Core.setMessageLog (msgLog ++ ( Core.getMessageLog))
let env getEnv
let mut instName := ( getCurrNamespace) ++ ( NameGen.mkBaseNameWithSuffix "inst" result.type)
-- We don't have a facility to let users override derived names, so make an unused name if needed.
instName liftMacroM <| mkUnusedBaseName instName
-- Make the instance private if the declaration is private.
if isPrivateName declName then
instName := mkPrivateName env instName
let hints := ReducibilityHints.regular (getMaxHeight env result.value + 1)
let decl mkDefinitionValInferringUnsafe instName result.levelParams.toList result.type result.value hints
addAndCompile (logCompileErrors := !( read).isNoncomputableSection) <| Declaration.defnDecl decl
trace[Elab.Deriving] "Derived instance `{.ofConstName instName}`"
addInstance instName AttributeKind.global (eval_prio default)
addDeclarationRangesFromSyntax instName ( getRef)
end Term
@@ -85,39 +223,60 @@ def registerDerivingHandler (className : Name) (handler : DerivingHandler) : IO
| some handlers => m.insert className (handler :: handlers)
| none => m.insert className [handler]
def defaultHandler (className : Name) (typeNames : Array Name) : CommandElabM Unit := do
throwError "default handlers have not been implemented yet, class: '{className}' types: {typeNames}"
def applyDerivingHandlers (className : Name) (typeNames : Array Name) : CommandElabM Unit := do
-- When any of the types are private, the deriving handler will need access to the private scope
-- (and should also make sure to put its outputs in the private scope).
withoutExporting (when := typeNames.any isPrivateName) do
withTraceNode `Elab.Deriving (fun _ => return m!"running deriving handlers for '{className}'") do
withTraceNode `Elab.Deriving (fun _ => return m!"running deriving handlers for `{.ofConstName className}`") do
match ( derivingHandlersRef.get).find? className with
| some handlers =>
for handler in handlers do
if ( handler typeNames) then
return ()
defaultHandler className typeNames
| none => defaultHandler className typeNames
throwError "None of the deriving handlers for class `{.ofConstName className}` applied to \
{.andList <| typeNames.toList.map (m!"`{.ofConstName ·}`")}"
| none => throwError "No deriving handlers have been implemented for class `{.ofConstName className}`"
private def tryApplyDefHandler (className : Name) (declName : Name) : CommandElabM Bool :=
liftTermElabM do
Term.processDefDeriving className declName
private def applyDefHandler (classStx : Syntax) (declExpr : Expr) : TermElabM Unit :=
withTraceNode `Elab.Deriving (fun _ => return m!"running delta deriving handler for `{classStx}` and definition `{declExpr}`") do
Term.processDefDeriving classStx declExpr
private def elabDefDeriving (classes decls : Array Syntax) :
CommandElabM Unit := runTermElabM fun _ => do
for decl in decls do
withRef decl <| withLogging do
let declExpr
if decl.isIdent then
let declName realizeGlobalConstNoOverload decl
let info getConstInfo declName
unless info.isDefinition do
throwError (m!"Declaration `{.ofConstName declName}` is not a definition."
++ .note m!"When any declaration is a definition, this command goes into delta deriving mode, \
which applies only to definitions. \
Delta deriving unfolds definitions and infers pre-existing instances.")
-- Use the declaration's level parameters, to ensure the instance is fully universe polymorphic
mkConstWithLevelParams declName
else
Term.elabTermAndSynthesize decl none
for classStx in classes do
withLogging <| applyDefHandler classStx declExpr
@[builtin_command_elab «deriving»] def elabDeriving : CommandElab
| `(deriving instance $[$classes],* for $[$declNames],*) => do
let declNames liftCoreM <| declNames.mapM realizeGlobalConstNoOverloadWithInfo
for cls in classes do
try
let className liftCoreM <| realizeGlobalConstNoOverloadWithInfo cls
withRef cls do
if declNames.size == 1 then
if ( tryApplyDefHandler className declNames[0]!) then
return ()
applyDerivingHandlers className declNames
catch ex =>
logException ex
| `(deriving instance $[$classes],* for $[$decls],*) => do
let decls : Array Syntax := decls
if decls.all Syntax.isIdent then
let declNames liftCoreM <| decls.mapM (realizeGlobalConstNoOverloadWithInfo ·)
-- If any of the declarations are definitions, then we commit to delta deriving.
let infos declNames.mapM getConstInfo
if infos.any (·.isDefinition) then
elabDefDeriving classes decls
else
-- Otherwise, we commit to using deriving handlers.
let classNames liftCoreM <| classes.mapM (realizeGlobalConstNoOverloadWithInfo ·)
for className in classNames, classIdent in classes do
withRef classIdent <| withLogging <| applyDerivingHandlers className declNames
else
elabDefDeriving classes decls
| _ => throwUnsupportedSyntax
structure DerivingClassView where

View File

@@ -135,15 +135,17 @@ def mkDecEq (declName : Name) : CommandElabM Bool := do
partial def mkEnumOfNat (declName : Name) : MetaM Unit := do
let indVal getConstInfoInduct declName
let enumType := mkConst declName
let ctors := indVal.ctors.toArray
let levels := indVal.levelParams.map Level.param
let enumType := mkConst declName levels
let u getLevel enumType
let ctors := indVal.ctors.toArray.map (mkConst · levels)
withLocalDeclD `n (mkConst ``Nat) fun n => do
let cond := mkConst ``cond [1]
let cond := mkConst ``cond [u]
let rec mkDecTree (low high : Nat) : Expr :=
if low + 1 == high then
mkConst ctors[low]!
ctors[low]!
else if low + 2 == high then
mkApp4 cond enumType (mkApp2 (mkConst ``Nat.beq) n (mkRawNatLit low)) (mkConst ctors[low]!) (mkConst ctors[low+1]!)
mkApp4 cond enumType (mkApp2 (mkConst ``Nat.beq) n (mkRawNatLit low)) ctors[low]! ctors[low+1]!
else
let mid := (low + high)/2
let lowBranch := mkDecTree low mid
@@ -153,7 +155,7 @@ partial def mkEnumOfNat (declName : Name) : MetaM Unit := do
let type mkArrow (mkConst ``Nat) enumType
addAndCompile <| Declaration.defnDecl {
name := Name.mkStr declName "ofNat"
levelParams := []
levelParams := indVal.levelParams
safety := DefinitionSafety.safe
hints := ReducibilityHints.abbrev
value, type
@@ -161,24 +163,26 @@ partial def mkEnumOfNat (declName : Name) : MetaM Unit := do
def mkEnumOfNatThm (declName : Name) : MetaM Unit := do
let indVal getConstInfoInduct declName
let toCtorIdx := mkConst (Name.mkStr declName "toCtorIdx")
let ofNat := mkConst (Name.mkStr declName "ofNat")
let enumType := mkConst declName
let eqEnum := mkApp (mkConst ``Eq [levelOne]) enumType
let rflEnum := mkApp (mkConst ``Eq.refl [levelOne]) enumType
let levels := indVal.levelParams.map Level.param
let toCtorIdx := mkConst (Name.mkStr declName "toCtorIdx") levels
let ofNat := mkConst (Name.mkStr declName "ofNat") levels
let enumType := mkConst declName levels
let u getLevel enumType
let eqEnum := mkApp (mkConst ``Eq [u]) enumType
let rflEnum := mkApp (mkConst ``Eq.refl [u]) enumType
let ctors := indVal.ctors
withLocalDeclD `x enumType fun x => do
let resultType := mkApp2 eqEnum (mkApp ofNat (mkApp toCtorIdx x)) x
let motive mkLambdaFVars #[x] resultType
let casesOn := mkConst (mkCasesOnName declName) [levelZero]
let casesOn := mkConst (mkCasesOnName declName) (levelZero :: levels)
let mut value := mkApp2 casesOn motive x
for ctor in ctors do
value := mkApp value (mkApp rflEnum (mkConst ctor))
value := mkApp value (mkApp rflEnum (mkConst ctor levels))
value mkLambdaFVars #[x] value
let type mkForallFVars #[x] resultType
addAndCompile <| Declaration.thmDecl {
name := Name.mkStr declName "ofNat_toCtorIdx"
levelParams := []
levelParams := indVal.levelParams
value, type
}

View File

@@ -60,7 +60,7 @@ private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) : Term
checkValidCtorModifier ctorModifiers
let ctorName := ctor.getIdAt 3
let ctorName := declName ++ ctorName
let ctorName withRef ctor[3] <| applyVisibility ctorModifiers.visibility ctorName
let ctorName withRef ctor[3] <| applyVisibility ctorModifiers ctorName
let (binders, type?) := expandOptDeclSig ctor[4]
addDocString' ctorName ctorModifiers.docString?
addDeclarationRangesFromSyntax ctorName ctor ctor[3]

View File

@@ -1303,12 +1303,24 @@ where
addPreDefinitions preDefs
processDeriving (headers : Array DefViewElabHeader) := do
for header in headers, view in views do
if let some classNamesStx := view.deriving? then
for classNameStx in classNamesStx do
let className realizeGlobalConstNoOverload classNameStx
withRef classNameStx do
unless ( processDefDeriving className header.declName) do
throwError "failed to synthesize instance '{className}' for '{header.declName}'"
if let some classStxs := view.deriving? then
for classStx in classStxs do
withRef classStx <| withLogging <| withLCtx {} {} do
/-
Assumption: users intend delta deriving to apply to the body of a definition, even if in the source code
the function is written as a lambda expression.
Furthermore, we don't use `forallTelescope` because users want to derive instances for monads.
We enter the local context of this body, which is where `classStx` will be elaborated.
Small complication: we don't know the correlation between the section variables
and the parameters in the declaration, so for now we do not allow `classStx`
to refer to section variables that were not included.
-/
let info getConstInfo header.declName
lambdaTelescope info.value! fun xs _ => do
let decl := mkAppN (.const header.declName (info.levelParams.map mkLevelParam)) xs
processDefDeriving classStx decl
/--
Logs a snapshot task that waits for the entire snapshot tree in `defsParsedSnap` and then logs a

View File

@@ -1021,8 +1021,8 @@ private def applyComputedFields (indViews : Array InductiveView) : CommandElabM
for {ref, fieldId, type, matchAlts, modifiers, ..} in indView.computedFields do
computedFieldDefs := computedFieldDefs.push <| do
let modifiers match modifiers with
| `(Lean.Parser.Command.declModifiersT| $[$doc:docComment]? $[$attrs:attributes]? $[$vis]? $[noncomputable]?) =>
`(Lean.Parser.Command.declModifiersT| $[$doc]? $[$attrs]? $[$vis]? noncomputable)
| `(Lean.Parser.Command.declModifiersT| $[$doc:docComment]? $[$attrs:attributes]? $[$vis]? $[protected%$protectedTk]? $[noncomputable]?) =>
`(Lean.Parser.Command.declModifiersT| $[$doc]? $[$attrs]? $[$vis]? $[protected%$protectedTk]? noncomputable)
| _ => do
withRef modifiers do logError "Unsupported modifiers for computed field"
`(Parser.Command.declModifiersT| noncomputable)

View File

@@ -233,11 +233,12 @@ private def expandCtor (structStx : Syntax) (structModifiers : Modifiers) (struc
(forcePrivate : Bool) : TermElabM CtorView := do
let useDefault := do
let visibility := if forcePrivate then .private else .regular
let modifiers := { (default : Modifiers) with visibility }
let declName := structDeclName ++ defaultCtorName
let declName applyVisibility visibility declName
let declName applyVisibility modifiers declName
let ref := structStx[1].mkSynthetic
addDeclarationRangesFromSyntax declName ref
pure { ref, declId := ref, modifiers := { (default : Modifiers) with visibility }, declName }
pure { ref, declId := ref, modifiers, declName }
if structStx[4].isNone then
useDefault
else
@@ -273,7 +274,7 @@ private def expandCtor (structStx : Syntax) (structModifiers : Modifiers) (struc
throwError m!"Constructor must be `private` because one or more of this structure's fields are `private`" ++ hint
let name := ctor[1].getId
let declName := structDeclName ++ name
let declName applyVisibility ctorModifiers.visibility declName
let declName applyVisibility ctorModifiers declName
-- `binders` is type parameter binder overrides; this will be validated when the constructor is created in `Structure.mkCtor`.
let binders := ctor[2]
addDocString' declName ctorModifiers.docString?
@@ -379,7 +380,7 @@ private def expandFields (structStx : Syntax) (structModifiers : Modifiers) (str
unless name.isAtomic do
throwErrorAt ident "Invalid field name `{name.eraseMacroScopes}`: Field names must be atomic"
let declName := structDeclName ++ name
let declName applyVisibility fieldModifiers.visibility declName
let declName applyVisibility fieldModifiers declName
addDocString' declName fieldModifiers.docString?
return views.push {
ref := ident
@@ -611,13 +612,11 @@ private def getFieldDefault? (structName : Name) (params : Array Expr) (fieldNam
else
return none
private def toVisibility (fieldInfo : StructureFieldInfo) : CoreM Visibility := do
if isProtected ( getEnv) fieldInfo.projFn then
return Visibility.protected
else if isPrivateName fieldInfo.projFn then
return Visibility.private
else
return Visibility.regular
private def toModifiers (fieldInfo : StructureFieldInfo) : CoreM Modifiers := do
return {
isProtected := isProtected ( getEnv) fieldInfo.projFn
visibility := if isPrivateName fieldInfo.projFn then .private else .regular
}
mutual
@@ -654,7 +653,7 @@ private partial def withStructField (view : StructView) (sourceStructNames : Lis
its default value is overridden, otherwise the `declName` is irrelevant, except to ensure a declaration is not already declared. -/
let mut declName := view.declName ++ fieldName
if inSubobject?.isNone then
declName applyVisibility ( toVisibility fieldInfo) declName
declName applyVisibility ( toModifiers fieldInfo) declName
-- No need to validate links because this docstring was already added to the environment previously
addDocStringCore' declName ( findDocString? ( getEnv) fieldInfo.projFn)
addDeclarationRangesFromSyntax declName ( getRef)

View File

@@ -147,8 +147,7 @@ partial def computeMVarBetaPotentialForSPred (xs : Array Expr) (σs : Expr) (e :
let s mkFreshExprMVar σ
e := e.beta #[s]
let (r, _) simp e ctx
-- In practice we only need to reduce `fun s => ...`, `SVal.curry` and functions that operate
-- on the state tuple bound by `SVal.curry`.
-- In practice we only need to reduce `fun s => ...` and `SPred.pure`.
-- We could write a custom function should `simp` become a bottleneck.
e := r.expr
let count countBVarDependentMVars xs e

View File

@@ -20,7 +20,7 @@ open Lean Elab Tactic Meta
-- set_option pp.all true in
-- #check ⌜False⌝
private def falseProp (u : Level) (σs : Expr) : Expr := -- ⌜False⌝ standing in for an empty conjunction of hypotheses
mkApp3 (mkConst ``SVal.curry [u]) (mkApp (mkConst ``ULift [u, 0]) (.sort .zero)) σs <| mkLambda `tuple .default (mkApp (mkConst ``SVal.StateTuple [u]) σs) (mkApp2 (mkConst ``ULift.up [u, 0]) (.sort .zero) (mkConst ``False))
SPred.mkPure u σs (mkConst ``False)
@[builtin_tactic Lean.Parser.Tactic.mexfalso]
def elabMExfalso : Tactic | _ => do

View File

@@ -41,13 +41,10 @@ def SPred.mkType (u : Level) (σs : Expr) : Expr :=
-- set_option pp.all true in
-- #check ⌜True⌝
def SPred.mkPure (u : Level) (σs : Expr) (p : Expr) : Expr :=
mkApp3 (mkConst ``SVal.curry [u]) (mkApp (mkConst ``ULift [u, 0]) (.sort .zero)) σs <|
mkLambda `tuple .default (mkApp (mkConst ``SVal.StateTuple [u]) σs) <|
mkApp2 (mkConst ``ULift.up [u, 0]) (.sort .zero) (Expr.liftLooseBVars p 0 1)
mkApp2 (mkConst ``SPred.pure [u]) σs p
def SPred.isPure? : Expr Option (Level × Expr × Expr)
| mkApp3 (.const ``SVal.curry [u]) (mkApp (.const ``ULift _) (.sort .zero)) σs <|
.lam _ _ (mkApp2 (.const ``ULift.up _) _ p) _ => some (u, σs, (Expr.lowerLooseBVars p 0 1))
| mkApp2 (.const ``SPred.pure [u]) σs p => some (u, σs, p)
| _ => none
def emptyHypName := `emptyHyp

View File

@@ -9,6 +9,7 @@ prelude
public import Std.Tactic.Do.Syntax
public import Lean.Elab.Tactic.Do.ProofMode.MGoal
public import Lean.Elab.Tactic.Do.ProofMode.Focus
public import Lean.Elab.Tactic.Meta
public section
@@ -53,3 +54,9 @@ def elabMPure : Tactic
| _ => throwUnsupportedSyntax
macro "mpure_intro" : tactic => `(tactic| apply Pure.intro)
def MGoal.triviallyPure (goal : MGoal) : OptionT MetaM Expr := do
let mv mkFreshExprMVar goal.toExpr
let ([], _) try runTactic mv.mvarId! ( `(tactic| apply Pure.intro; trivial)) catch _ => failure
| failure
return mv.consumeMData

View File

@@ -96,11 +96,11 @@ partial def dischargeFailEntails (u : Level) (ps : Expr) (Q : Expr) (Q' : Expr)
if ps.isAppOf ``PostShape.pure then
return mkConst ``True.intro
if isDefEq Q Q' then
return mkApp2 (mkConst ``FailConds.entails.refl [u]) ps Q
if isDefEq Q (mkApp (mkConst ``FailConds.false [u]) ps) then
return mkApp2 (mkConst ``FailConds.entails_false [u]) ps Q'
if isDefEq Q' (mkApp (mkConst ``FailConds.true [u]) ps) then
return mkApp2 (mkConst ``FailConds.entails_true [u]) ps Q
return mkApp2 (mkConst ``ExceptConds.entails.refl [u]) ps Q
if isDefEq Q (mkApp (mkConst ``ExceptConds.false [u]) ps) then
return mkApp2 (mkConst ``ExceptConds.entails_false [u]) ps Q'
if isDefEq Q' (mkApp (mkConst ``ExceptConds.true [u]) ps) then
return mkApp2 (mkConst ``ExceptConds.entails_true [u]) ps Q
-- the remaining cases are recursive.
if let some (_σ, ps) := ps.app2? ``PostShape.arg then
return dischargeFailEntails u ps Q Q' goalTag
@@ -117,31 +117,29 @@ partial def dischargeFailEntails (u : Level) (ps : Expr) (Q : Expr) (Q' : Expr)
let prf₂ dischargeFailEntails u ps ( mkProj' ``Prod 1 Q) ( mkProj' ``Prod 1 Q') (goalTag ++ `except)
return mkAppM ``And.intro #[prf₁, prf₂] -- This is just a bit too painful to construct by hand
-- This case happens when decomposing with unknown `ps : PostShape`
mkFreshExprSyntheticOpaqueMVar (mkApp3 (mkConst ``FailConds.entails [u]) ps Q Q') goalTag
mkFreshExprSyntheticOpaqueMVar (mkApp3 (mkConst ``ExceptConds.entails [u]) ps Q Q') goalTag
end
def dischargeMGoal (goal : MGoal) (goalTag : Name) : n Expr := do
liftMetaM <| do trace[Elab.Tactic.Do.spec] "dischargeMGoal: {goal.target}"
-- simply try one of the assumptions for now. Later on we might want to decompose conjunctions etc; full xsimpl
-- The `withDefault` ensures that a hyp `⌜s = 4⌝` can be used to discharge `⌜s = 4⌝ s`.
-- (Recall that `⌜s = 4⌝ s` is `SVal.curry (σs:=[Nat]) (fun _ => s = 4) s` and `SVal.curry` is
-- (Recall that `⌜s = 4⌝ s` is `SPred.pure (σs:=[Nat]) (s = 4) s` and `SPred.pure` is
-- semi-reducible.)
let some prf liftMetaM (withDefault <| goal.assumption <|> goal.assumptionPure)
-- We also try `mpure_intro; trivial` through `goal.triviallyPure` here because later on an
-- assignment like `⌜s = ?c⌝` becomes impossible to discharge because `?c` will get abstracted
-- over local bindings that depend on synthetic opaque MVars (such as loop invariants), and then
-- the type of the new `?c` will not be defeq to itself. A bug, but we need to work around it for
-- now.
let some prf liftMetaM (withDefault <| goal.assumption <|> goal.assumptionPure <|> goal.triviallyPure)
| mkFreshExprSyntheticOpaqueMVar goal.toExpr goalTag
liftMetaM <| do trace[Elab.Tactic.Do.spec] "proof: {prf}"
return prf
def mkPreTag (goalTag : Name) : Name := Id.run do
let dflt := goalTag ++ `pre1
let .str p s := goalTag | return dflt
unless "pre".isPrefixOf s do return dflt
let some n := (s.toSubstring.drop 3).toString.toNat? | return dflt
return .str p ("pre" ++ toString (n + 1))
/--
Returns the proof and the list of new unassigned MVars.
-/
def mSpec (goal : MGoal) (elabSpecAtWP : Expr n SpecTheorem) (goalTag : Name) (mkPreTag := mkPreTag) : n Expr := do
def mSpec (goal : MGoal) (elabSpecAtWP : Expr n SpecTheorem) (goalTag : Name) : n Expr := do
-- First instantiate `fun s => ...` in the target via repeated `mintro ∀s`.
mIntroForallN goal goal.target.consumeMData.getNumHeadLambdas fun goal => do
-- Elaborate the spec for the wp⟦e⟧ app in the target
@@ -151,11 +149,8 @@ def mSpec (goal : MGoal) (elabSpecAtWP : Expr → n SpecTheorem) (goalTag : Name
let wp := T.getArg! 2
let specThm elabSpecAtWP wp
-- The precondition of `specThm` might look like `⌜?n = Natₛ ∧ ?m = Bool⌝`, which expands to
-- `SVal.curry (fun tuple => ?n = SVal.uncurry (getThe Nat tuple) ∧ ?m = SVal.uncurry (getThe Bool tuple))`.
-- Note that the assignments for `?n` and `?m` depend on the bound variable `tuple`.
-- Here, we further eta expand and simplify according to `etaPotential` so that the solutions for
-- `?n` and `?m` do not depend on `tuple`.
-- The precondition of `specThm` might look like `⌜?n = nₛ ∧ ?m = b⌝`, which expands to
-- `SPred.pure (?n = n ∧ ?m = b)`.
let residualEta := specThm.etaPotential - (T.getAppNumArgs - 4) -- 4 arguments expected for PredTrans.apply
mIntroForallN goal residualEta fun goal => do
@@ -196,7 +191,7 @@ def mSpec (goal : MGoal) (elabSpecAtWP : Expr → n SpecTheorem) (goalTag : Name
if !HPRfl then
-- let P := (← reduceProjBeta? P).getD P
-- Try to avoid creating a longer name if the postcondition does not need to create a goal
let tag := if !QQ'Rfl then mkPreTag goalTag else goalTag
let tag := if !QQ'Rfl then goalTag ++ `pre else goalTag
let HPPrf dischargeMGoal { goal with target := P } tag
prePrf := mkApp6 (mkConst ``SPred.entails.trans [u]) goal.σs goal.hyps P goal.target HPPrf

View File

@@ -18,13 +18,22 @@ namespace Std.Do
open Lean Parser Meta Elab Term PrettyPrinter Delaborator
open Std.Do in
@[builtin_delab app.Std.Do.PostCond.total]
private def unexpandPostCondTotal : Delab := do
@[builtin_delab PostCond.noThrow]
private def unexpandPostCondNoThrow : Delab := do
match SubExpr.withAppArg <| delab with
| `(fun $xs:term* => $e) =>
let t `( $xs* => $( SPred.Notation.unpack e))
return t.raw
| t => `($(mkIdent ``PostCond.total):term $t)
| t => `($(mkIdent ``PostCond.noThrow):term $t)
open Std.Do in
@[builtin_delab PostCond.mayThrow]
private def unexpandPostCondMayThrow : Delab := do
match SubExpr.withAppArg <| delab with
| `(fun $xs:term* => $e) =>
let t `(? $xs* => $( SPred.Notation.unpack e))
return t.raw
| t => `($(mkIdent ``PostCond.mayThrow):term $t)
@[inherit_doc Triple, builtin_doc, builtin_term_elab triple]
private def elabTriple : TermElab

View File

@@ -45,7 +45,13 @@ partial def genVCs (goal : MVarId) (ctx : Context) (fuel : Fuel) : MetaM (Array
mvar.withContext <| withReducible do
let (prf, state) StateRefT'.run (ReaderT.run (onGoal goal ( mvar.getTag)) ctx) { fuel }
mvar.assign prf
return state.vcs
for h : idx in [:state.invariants.size] do
let mv := state.invariants[idx]
mv.setTag (Name.mkSimple ("inv" ++ toString (idx + 1)))
for h : idx in [:state.vcs.size] do
let mv := state.vcs[idx]
mv.setTag (Name.mkSimple ("vc" ++ toString (idx + 1)) ++ ( mv.getTag))
return state.invariants ++ state.vcs
where
onFail (goal : MGoal) (name : Name) : VCGenM Expr := do
-- trace[Elab.Tactic.Do.vcgen] "fail {goal.toExpr}"
@@ -70,15 +76,20 @@ where
mvar.withContext <| do
-- trace[Elab.Tactic.Do.vcgen] "assignMVars {← mvar.getTag}, isDelayedAssigned: {← mvar.isDelayedAssigned},\n{mvar}"
let ty mvar.getType
if ( isProp ty) || ty.isAppOf ``PostCond || ty.isAppOf ``SPred then
-- This code path will re-introduce `mvar` as a synthetic opaque goal upon discharge failure.
-- This is the right call for (previously natural) holes such as loop invariants, which
-- would otherwise lead to spurious instantiations.
-- But it's wrong for, e.g., schematic variables. The latter should never be PostConds or
-- SPreds, hence the condition.
if isProp ty then
-- Might contain more `P ⊢ₛ wp⟦prog⟧ Q` apps. Try and prove it!
mvar.assign ( tryGoal ty ( mvar.getTag))
else
addSubGoalAsVC mvar
return
if ty.isAppOf ``PostCond || ty.isAppOf ``Invariant || ty.isAppOf ``SPred then
-- Here we make `mvar` a synthetic opaque goal upon discharge failure.
-- This is the right call for (previously natural) holes such as loop invariants, which
-- would otherwise lead to spurious instantiations and unwanted renamings (when leaving the
-- scope of a local).
-- But it's wrong for, e.g., schematic variables. The latter should never be PostConds,
-- Invariants or SPreds, hence the condition.
mvar.setKind .syntheticOpaque
addSubGoalAsVC mvar
onGoal goal name : VCGenM Expr := do
let T := goal.target
@@ -100,9 +111,8 @@ where
let args := goal.target.getAppArgs
let trans := args[2]!
-- logInfo m!"trans: {trans}"
let Q := args[3]!
let wp instantiateMVarsIfMVarApp trans
let_expr c@WP.wp m ps instWP α e := wp | onFail goal name
let_expr WP.wp m _ps _instWP α e := wp | onFail goal name
-- NB: e here is a monadic expression, in the "object language"
let e instantiateMVarsIfMVarApp e
let e := e.headBeta
@@ -150,13 +160,13 @@ where
let res Simp.simp e
unless res.expr != e do return onFail goal name
burnOne
if let .some heq := res.proof? then
trace[Elab.Tactic.Do.vcgen] "Simplified"
let prf onWPApp (goal.withNewProg res.expr) name
let prf := mkApp10 (mkConst ``Triple.rewrite_program c.constLevels!) m ps α goal.hyps Q instWP e res.expr heq prf
return prf
else
return onWPApp (goal.withNewProg res.expr) name
trace[Elab.Tactic.Do.vcgen] "Simplified program to {res.expr}"
let prf onWPApp (goal.withNewProg res.expr) name
-- context = fun e => H ⊢ₛ wp⟦e⟧ Q
let context withLocalDecl `e .default (mkApp m α) fun e => do
mkLambdaFVars #[e] (goal.withNewProg e).toExpr
let res Simp.mkCongrArg context res
return res.mkEqMPR prf
assignMVars specHoles.toList
return prf
return onFail goal name
@@ -166,18 +176,17 @@ where
onSplit (goal : MGoal) (info : SplitInfo) (name : Name)
(withAltCtx : Nat Array Expr VCGenM Expr VCGenM Expr := fun _ _ k => k) : VCGenM Expr := do
let args := goal.target.getAppArgs
let Q := args[3]!
let_expr c@WP.wp m ps instWP α e := args[2]! | throwError "Expected wp⟦e⟧ Q in goal.target, got {goal.target}"
let_expr WP.wp m _ps _instWP α e := args[2]! | throwError "Expected wp⟦e⟧ Q in goal.target, got {goal.target}"
-- Bring into simp NF
let e -- returns/continues only if old e is defeq to new e
if let .some res info.simpDiscrs? e then
burnOne
if let .some heq := res.proof? then
let prf onWPApp (goal.withNewProg res.expr) name
let prf := mkApp10 (mkConst ``Triple.rewrite_program c.constLevels!) m ps α goal.hyps Q instWP e res.expr heq prf
return prf
else
pure res.expr
let prf onWPApp (goal.withNewProg res.expr) name
-- context = fun e => H ⊢ₛ wp⟦e⟧ Q
let context withLocalDecl `e .default (mkApp m α) fun e => do
mkLambdaFVars #[e] (goal.withNewProg e).toExpr
let res Simp.mkCongrArg context res
res.mkEqMPR prf
else
pure e
-- Try reduce the matcher

View File

@@ -62,13 +62,17 @@ structure Context where
simpCtx : Simp.Context
simprocs : Simp.SimprocsArray
jps : FVarIdMap JumpSiteInfo := {}
initialCtxSize : Nat
structure State where
fuel : Fuel := .unlimited
simpState : Simp.State := {}
/--
Holes of type `Invariant` that have been generated so far.
-/
invariants : Array MVarId := #[]
/--
The verification conditions that have been generated so far.
Includes `Type`-valued goals arising from instantiation of specifications.
-/
vcs : Array MVarId := #[]
@@ -87,13 +91,18 @@ def ifOutOfFuel (x : VCGenM α) (k : VCGenM α) : VCGenM α := do
| Fuel.limited 0 => x
| _ => k
def emitVC (subGoal : Expr) (name : Name) : VCGenM Expr := do
let m liftM <| mkFreshExprSyntheticOpaqueMVar subGoal (tag := name)
modify fun s => { s with vcs := s.vcs.push m.mvarId! }
return m
def addSubGoalAsVC (goal : MVarId) : VCGenM PUnit := do
modify fun s => { s with vcs := s.vcs.push goal }
let ty goal.getType
if ty.isAppOf ``Std.Do.Invariant then
modify fun s => { s with invariants := s.invariants.push goal }
else
modify fun s => { s with vcs := s.vcs.push goal }
def emitVC (subGoal : Expr) (name : Name) : VCGenM Expr := do
withFreshUserNamesSinceIdx ( read).initialCtxSize do
let m liftM <| mkFreshExprSyntheticOpaqueMVar subGoal (tag := name)
addSubGoalAsVC m.mvarId!
return m
def liftSimpM (x : SimpM α) : VCGenM α := do
let ctx read
@@ -239,4 +248,10 @@ def mkSpecContext (optConfig : Syntax) (lemmas : Syntax) (ignoreStarArg := false
let thm mkSpecTheoremFromLocal fvar
specThms := addSpecTheoremEntry specThms thm
catch _ => continue
return { config, specThms, simpCtx := res.ctx, simprocs := res.simprocs }
return {
config,
specThms,
simpCtx := res.ctx,
simprocs := res.simprocs
initialCtxSize := ( getLCtx).numIndices
}

View File

@@ -86,7 +86,7 @@ def mkCoordinateEvalAtomsEq (e : Expr) (n : Nat) : OmegaM Expr := do
mkEqTrans eq ( mkEqSymm (mkApp2 (.const ``LinearCombo.coordinate_eval []) n atoms))
/-- Construct the linear combination (and its associated proof and new facts) for an atom. -/
def mkAtomLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
def mkAtomLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × List Expr) := do
let (n, facts) lookup e
return LinearCombo.coordinate n, mkCoordinateEvalAtomsEq e n, facts.getD
@@ -100,7 +100,7 @@ Gives a small (10%) speedup in testing.
I tried using a pointer based cache,
but there was never enough subexpression sharing to make it effective.
-/
partial def asLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
partial def asLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × List Expr) := do
let cache get
match cache.get? e with
| some (lc, prf) =>
@@ -126,7 +126,7 @@ We also transform the expression as we descend into it:
* pushing coercions: `↑(x + y)`, `↑(x * y)`, `↑(x / k)`, `↑(x % k)`, `↑k`
* unfolding `emod`: `x % k` → `x - x / k`
-/
partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × List Expr) := do
trace[omega] "processing {e}"
match groundInt? e with
| some i =>
@@ -148,7 +148,7 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
mkEqTrans
( mkAppM ``Int.add_congr #[ prf₁, prf₂])
( mkEqSymm add_eval)
pure (l₁ + l₂, prf, facts₁.union facts₂)
pure (l₁ + l₂, prf, facts₁ ++ facts₂)
| (``HSub.hSub, #[_, _, _, _, e₁, e₂]) => do
let (l₁, prf₁, facts₁) asLinearCombo e₁
let (l₂, prf₂, facts₂) asLinearCombo e₂
@@ -158,7 +158,7 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
mkEqTrans
( mkAppM ``Int.sub_congr #[ prf₁, prf₂])
( mkEqSymm sub_eval)
pure (l₁ - l₂, prf, facts₁.union facts₂)
pure (l₁ - l₂, prf, facts₁ ++ facts₂)
| (``Neg.neg, #[_, _, e']) => do
let (l, prf, facts) asLinearCombo e'
let prf' : OmegaM Expr := do
@@ -184,7 +184,7 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
mkEqTrans
( mkAppM ``Int.mul_congr #[ xprf, yprf])
( mkEqSymm mul_eval)
pure (some (LinearCombo.mul xl yl, prf, xfacts.union yfacts), true)
pure (some (LinearCombo.mul xl yl, prf, xfacts ++ yfacts), true)
else
pure (none, false)
match r? with
@@ -242,7 +242,7 @@ where
Apply a rewrite rule to an expression, and interpret the result as a `LinearCombo`.
(We're not rewriting any subexpressions here, just the top level, for efficiency.)
-/
rewrite (lhs rw : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
rewrite (lhs rw : Expr) : OmegaM (LinearCombo × OmegaM Expr × List Expr) := do
trace[omega] "rewriting {lhs} via {rw} : {← inferType rw}"
match ( inferType rw).eq? with
| some (_, _lhs', rhs) =>
@@ -250,7 +250,7 @@ where
let prf' : OmegaM Expr := do mkEqTrans rw ( prf)
pure (lc, prf', facts)
| none => panic! "Invalid rewrite rule in 'asLinearCombo'"
handleNatCast (e i n : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
handleNatCast (e i n : Expr) : OmegaM (LinearCombo × OmegaM Expr × List Expr) := do
match n with
| .fvar h =>
if let some v h.getValue? then
@@ -297,7 +297,7 @@ where
| (``Fin.val, #[n, x]) =>
handleFinVal e i n x
| _ => mkAtomLinearCombo e
handleFinVal (e i n x : Expr) : OmegaM (LinearCombo × OmegaM Expr × Std.HashSet Expr) := do
handleFinVal (e i n x : Expr) : OmegaM (LinearCombo × OmegaM Expr × List Expr) := do
match x with
| .fvar h =>
if let some v h.getValue? then
@@ -343,12 +343,11 @@ We solve equalities as they are discovered, as this often results in an earlier
-/
def addIntEquality (p : MetaProblem) (h x : Expr) : OmegaM MetaProblem := do
let (lc, prf, facts) asLinearCombo x
let newFacts : Std.HashSet Expr := facts.fold (init := ) fun s e =>
if p.processedFacts.contains e then s else s.insert e
let newFacts : List Expr := facts.filter (p.processedFacts.contains · = false)
trace[omega] "Adding proof of {lc} = 0"
pure <|
{ p with
facts := newFacts.toList ++ p.facts
facts := newFacts ++ p.facts
problem := (p.problem.addEquality lc.const lc.coeffs
(some do mkEqTrans ( mkEqSymm ( prf)) h)) |>.solveEqualities }
@@ -359,12 +358,11 @@ We solve equalities as they are discovered, as this often results in an earlier
-/
def addIntInequality (p : MetaProblem) (h y : Expr) : OmegaM MetaProblem := do
let (lc, prf, facts) asLinearCombo y
let newFacts : Std.HashSet Expr := facts.fold (init := ) fun s e =>
if p.processedFacts.contains e then s else s.insert e
let newFacts : List Expr := facts.filter (p.processedFacts.contains · = false)
trace[omega] "Adding proof of {lc} ≥ 0"
pure <|
{ p with
facts := newFacts.toList ++ p.facts
facts := newFacts ++ p.facts
problem := (p.problem.addInequality lc.const lc.coeffs
(some do mkAppM ``le_of_le_of_eq #[h, ( prf)])) |>.solveEqualities }

View File

@@ -9,7 +9,7 @@ prelude
public import Init.BinderPredicates
public import Init.Data.Int.Order
public import Init.Data.List.MinMax
public import Init.Data.Nat.MinMax
public import Init.Data.Nat.Order
public import Init.Data.Option.Lemmas
public section
@@ -35,20 +35,10 @@ We completely characterize the function via
-/
def nonzeroMinimum (xs : List Nat) : Nat := xs.filter (· 0) |>.min? |>.getD 0
-- A specialization of `minimum?_eq_some_iff` to Nat.
-- This is a duplicate `min?_eq_some_iff'` proved in `Init.Data.List.Nat.Basic`,
-- and could be deduplicated but the import hierarchy is awkward.
theorem min?_eq_some_iff'' {xs : List Nat} :
xs.min? = some a (a xs b xs, a b) :=
min?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => Nat.min_def .. by split <;> simp)
(le_min_iff := fun _ _ _ => Nat.le_min)
open Classical in
@[simp] theorem nonzeroMinimum_eq_zero_iff {xs : List Nat} :
xs.nonzeroMinimum = 0 x xs, x = 0 := by
simp [nonzeroMinimum, Option.getD_eq_iff, min?_eq_none_iff, min?_eq_some_iff'',
simp [nonzeroMinimum, Option.getD_eq_iff, min?_eq_none_iff, min?_eq_some_iff,
filter_eq_nil_iff, mem_filter]
theorem nonzeroMinimum_mem {xs : List Nat} (w : xs.nonzeroMinimum 0) :
@@ -56,7 +46,7 @@ theorem nonzeroMinimum_mem {xs : List Nat} (w : xs.nonzeroMinimum ≠ 0) :
dsimp [nonzeroMinimum] at *
generalize h : (xs.filter (· 0) |>.min?) = m at *
match m, w with
| some (m+1), _ => simp_all [min?_eq_some_iff'', mem_filter]
| some (m+1), _ => simp_all [min?_eq_some_iff, mem_filter]
theorem nonzeroMinimum_pos {xs : List Nat} (m : a xs) (h : a 0) : 0 < xs.nonzeroMinimum :=
Nat.pos_iff_ne_zero.mpr fun w => h (nonzeroMinimum_eq_zero_iff.mp w _ m)
@@ -68,7 +58,7 @@ theorem nonzeroMinimum_le {xs : List Nat} (m : a ∈ xs) (h : a ≠ 0) : xs.nonz
generalize h : (xs.filter (· 0) |>.min?) = m? at *
match m?, w with
| some m?, _ => rfl
rw [min?_eq_some_iff''] at this
rw [min?_eq_some_iff] at this
apply this.2
simp [List.mem_filter]
exact m, h

View File

@@ -168,11 +168,11 @@ def mkEqReflWithExpectedType (a b : Expr) : MetaM Expr := do
Analyzes a newly recorded atom,
returning a collection of interesting facts about it that should be added to the context.
-/
def analyzeAtom (e : Expr) : OmegaM (Std.HashSet Expr) := do
def analyzeAtom (e : Expr) : OmegaM (List Expr) := do
match e.getAppFnArgs with
| (``Nat.cast, #[.const ``Int [], _, e']) =>
-- Casts of natural numbers are non-negative.
let mut r := ( : Std.HashSet Expr).insert (Expr.app (.const ``Int.ofNat_nonneg []) e')
let mut r := [Expr.app (.const ``Int.ofNat_nonneg []) e']
match ( cfg).splitNatSub, e'.getAppFnArgs with
| true, (``HSub.hSub, #[_, _, _, _, a, b]) =>
-- `((a - b : Nat) : Int)` gives a dichotomy
@@ -194,9 +194,8 @@ def analyzeAtom (e : Expr) : OmegaM (Std.HashSet Expr) := do
let ne_zero := mkApp3 (.const ``Ne [1]) (.const ``Int []) k (toExpr (0 : Int))
let pos := mkApp4 (.const ``LT.lt [0]) (.const ``Int []) (.const ``Int.instLTInt [])
(toExpr (0 : Int)) k
pure <| ( : Std.HashSet Expr).insert
(mkApp3 (.const ``Int.mul_ediv_self_le []) x k ( mkDecideProof ne_zero)) |>.insert
(mkApp3 (.const ``Int.lt_mul_ediv_self_add []) x k ( mkDecideProof pos))
pure [mkApp3 (.const ``Int.mul_ediv_self_le []) x k ( mkDecideProof ne_zero),
mkApp3 (.const ``Int.lt_mul_ediv_self_add []) x k ( mkDecideProof pos)]
| (``HMod.hMod, #[_, _, _, _, x, k]) =>
match k.getAppFnArgs with
| (``HPow.hPow, #[_, _, _, _, b, exp]) => match natCast? b with
@@ -206,10 +205,9 @@ def analyzeAtom (e : Expr) : OmegaM (Std.HashSet Expr) := do
let b_pos := mkApp4 (.const ``LT.lt [0]) (.const ``Int []) (.const ``Int.instLTInt [])
(toExpr (0 : Int)) b
let pow_pos := mkApp3 (.const ``Lean.Omega.Int.pos_pow_of_pos []) b exp ( mkDecideProof b_pos)
pure <| ( : Std.HashSet Expr).insert
(mkApp3 (.const ``Int.emod_nonneg []) x k
(mkApp3 (.const ``Int.ne_of_gt []) k (toExpr (0 : Int)) pow_pos)) |>.insert
(mkApp3 (.const ``Int.emod_lt_of_pos []) x k pow_pos)
pure [mkApp3 (.const ``Int.emod_nonneg []) x k
(mkApp3 (.const ``Int.ne_of_gt []) k (toExpr (0 : Int)) pow_pos),
mkApp3 (.const ``Int.emod_lt_of_pos []) x k pow_pos]
| (``Nat.cast, #[.const ``Int [], _, k']) =>
match k'.getAppFnArgs with
| (``HPow.hPow, #[_, _, _, _, b, exp]) => match natCast? b with
@@ -220,28 +218,25 @@ def analyzeAtom (e : Expr) : OmegaM (Std.HashSet Expr) := do
(toExpr (0 : Nat)) b
let pow_pos := mkApp3 (.const ``Nat.pos_pow_of_pos []) b exp ( mkDecideProof b_pos)
let cast_pos := mkApp2 (.const ``Int.ofNat_pos_of_pos []) k' pow_pos
pure <| ( : Std.HashSet Expr).insert
(mkApp3 (.const ``Int.emod_nonneg []) x k
(mkApp3 (.const ``Int.ne_of_gt []) k (toExpr (0 : Int)) cast_pos)) |>.insert
(mkApp3 (.const ``Int.emod_lt_of_pos []) x k cast_pos)
pure [mkApp3 (.const ``Int.emod_nonneg []) x k
(mkApp3 (.const ``Int.ne_of_gt []) k (toExpr (0 : Int)) cast_pos),
mkApp3 (.const ``Int.emod_lt_of_pos []) x k cast_pos]
| _ => match x.getAppFnArgs with
| (``Nat.cast, #[.const ``Int [], _, x']) =>
-- Since we push coercions inside `%`, we need to record here that
-- `(x : Int) % (y : Int)` is non-negative.
pure <| ( : Std.HashSet Expr).insert (mkApp2 (.const ``Int.emod_ofNat_nonneg []) x' k)
pure [mkApp2 (.const ``Int.emod_ofNat_nonneg []) x' k]
| _ => pure
| _ => pure
| (``Min.min, #[_, _, x, y]) =>
pure <| ( : Std.HashSet Expr).insert (mkApp2 (.const ``Int.min_le_left []) x y) |>.insert
(mkApp2 (.const ``Int.min_le_right []) x y)
pure [mkApp2 (.const ``Int.min_le_left []) x y, mkApp2 (.const ``Int.min_le_right []) x y]
| (``Max.max, #[_, _, x, y]) =>
pure <| ( : Std.HashSet Expr).insert (mkApp2 (.const ``Int.le_max_left []) x y) |>.insert
(mkApp2 (.const ``Int.le_max_right []) x y)
pure [mkApp2 (.const ``Int.le_max_left []) x y, mkApp2 (.const ``Int.le_max_right []) x y]
| (``ite, #[α, i, dec, t, e]) =>
if α == (.const ``Int []) then
pure <| ( : Std.HashSet Expr).insert <| mkApp5 (.const ``ite_disjunction [0]) α i dec t e
pure [mkApp5 (.const ``ite_disjunction [0]) α i dec t e]
else
pure {}
pure []
| _ => pure
/--
@@ -254,7 +249,7 @@ Return its index, and, if it is new, a collection of interesting facts about the
* for each new atom of the form `((a - b : Nat) : Int)`, the fact:
`b ≤ a ∧ ((a - b : Nat) : Int) = a - b a < b ∧ ((a - b : Nat) : Int) = 0`
-/
def lookup (e : Expr) : OmegaM (Nat × Option (Std.HashSet Expr)) := do
def lookup (e : Expr) : OmegaM (Nat × Option (List Expr)) := do
let c getThe State
let e canon e
match c.atoms[e]? with
@@ -264,7 +259,7 @@ def lookup (e : Expr) : OmegaM (Nat × Option (Std.HashSet Expr)) := do
let facts analyzeAtom e
if isTracingEnabledFor `omega then
unless facts.isEmpty do
trace[omega] "New facts: {← facts.toList.mapM fun e => inferType e}"
trace[omega] "New facts: {← facts.mapM fun e => inferType e}"
let i modifyGetThe State fun c =>
(c.atoms.size, { c with atoms := c.atoms.insert e c.atoms.size })
return (i, some facts)

View File

@@ -529,13 +529,6 @@ private structure VisibilityMap (α : Type) where
«public» : α
deriving Inhabited, Nonempty
/-- Realization results, to be replayed onto other branches. -/
private structure RealizationResult where
newConsts : VisibilityMap (List AsyncConst)
replayKernel : Kernel.Environment Except Kernel.Exception Kernel.Environment
dyn : Dynamic
deriving Nonempty
/-- Context for `realizeConst` established by `enableRealizationsForConst`. -/
private structure RealizationContext where
/--
@@ -545,12 +538,11 @@ private structure RealizationContext where
/-- Saved options. Empty for imported constants. -/
opts : Options
/--
`realizeConst _ c ..` adds a mapping from `c` to a task of the realization results: the newly
added constants (incl. extension data in `AsyncConst.exts?`), a function for replaying the
changes onto a derived kernel environment, and auxiliary data (always `SnapshotTree` in builtin
uses, but untyped to avoid cyclic module references).
`realizeValue _ key ..` adds a mapping from `(typeName key, key)` to a task of the realization
result (`RealizeValueResult` when called from `Lean.realizeValue`, `RealizeConstResult` from
`Environment.realizeConst`).
-/
constsRef : IO.Ref (NameMap (Task RealizationResult))
realizeMapRef : IO.Ref (NameMap NonScalar /- PHashMap α (Task Dynamic) -/)
/--
Elaboration-specific extension of `Kernel.Environment` that adds tracking of asynchronously
@@ -596,19 +588,19 @@ structure Environment where
/-- Information about this asynchronous branch of the environment, if any. -/
private asyncCtx? : Option AsyncContext := none
/--
Realized constants belonging to imported declarations. Must be initialized by calling
Realized values belonging to imported declarations. Must be initialized by calling
`enableRealizationsForImports`.
-/
private realizedImportedConsts? : Option RealizationContext
private importRealizationCtx? : Option RealizationContext
/--
Realized constants belonging to local declarations. This is a map from local declarations, which
Realized values belonging to local declarations. This is a map from local declarations, which
need to be registered synchronously using `enableRealizationsForConst`, to their realization
context incl. a ref of realized constants.
context.
-/
private realizedLocalConsts : NameMap RealizationContext := {}
private localRealizationCtxMap : NameMap RealizationContext := {}
/--
Task collecting all realizations from the current and already-forked environment branches, akin to
how `checked` collects all declarations. We only use it as a fallback in
Task collecting all realized constants from the current and already-forked environment branches,
akin to how `checked` collects all declarations. We only use it as a fallback in
`findAsyncCore?`/`getState`; see there.
-/
private allRealizations : Task (NameMap AsyncConst) := .pure {}
@@ -649,7 +641,7 @@ private def asyncConsts (env : Environment) : AsyncConsts :=
-- both cases, the environment should be temporary and not leak into elaboration.
@[export lean_elab_environment_of_kernel_env]
def ofKernelEnv (env : Kernel.Environment) : Environment :=
{ base.private := env, base.public := env, realizedImportedConsts? := none }
{ base.private := env, base.public := env, importRealizationCtx? := none }
@[export lean_elab_environment_to_kernel_env]
def toKernelEnv (env : Environment) : Kernel.Environment :=
@@ -684,7 +676,7 @@ it.
-/
def importEnv? (env : Environment) : Option Environment :=
-- safety: `RealizationContext` is private
unsafe env.realizedImportedConsts?.map (unsafeCast (β := Environment) ·.env)
unsafe env.importRealizationCtx?.map (unsafeCast (β := Environment) ·.env)
/-- Forgets about the asynchronous context restrictions. Used only for `withoutModifyingEnv`. -/
def unlockAsync (env : Environment) : Environment :=
@@ -869,25 +861,22 @@ def enableRealizationsForConst (env : Environment) (opts : Options) (c : Name) :
if !asyncCtx.mayContain c then
panic! s!"{c} is outside current context {asyncCtx.declPrefix}"
return env
if env.realizedLocalConsts.contains c then
if env.localRealizationCtxMap.contains c then
return env
return { env with realizedLocalConsts := env.realizedLocalConsts.insert c {
return { env with localRealizationCtxMap := env.localRealizationCtxMap.insert c {
-- safety: `RealizationContext` is private
env := unsafe unsafeCast env
opts
constsRef := ( IO.mkRef {}) } }
realizeMapRef := ( IO.mkRef {}) } }
def areRealizationsEnabledForConst (env : Environment) (c : Name) : Bool :=
(env.base.get env |>.const2ModIdx.contains c) || env.localRealizationCtxMap.contains c
/-- Returns debug output about the asynchronous state of the environment. -/
def dbgFormatAsyncState (env : Environment) : BaseIO String :=
return s!"\
asyncCtx.declPrefix: {repr <| env.asyncCtx?.map (·.declPrefix)}\
\nasyncConsts: {repr <| env.asyncConsts.revList.reverse.map (·.constInfo.name)}\
\nrealizedLocalConsts: {repr (← env.realizedLocalConsts.toList.mapM fun (n, ctx) => do
let consts := (← ctx.constsRef.get).toList
return (n, consts.map (·.1)))}
\nrealizedImportedConsts?: {repr <| (← env.realizedImportedConsts?.mapM fun ctx => do
return (← ctx.constsRef.get).toList.map fun (n, m?) =>
(n, m?.get.1.private.map (fun c : AsyncConst => c.constInfo.name.toString) |> toString))}
\nasyncConsts: {repr <| env.asyncConsts.revList.reverse.map (·.constInfo.name)}
\nbase.private.constants.map₂: {repr <| env.base.private.constants.map₂.toList.map (·.1)}"
/-- Returns debug output about the synchronous state of the environment. -/
@@ -1158,7 +1147,7 @@ def setMainModule (env : Environment) (m : Name) : Environment := Id.run do
let env := env.modifyCheckedAsync ({ · with
header.mainModule := m
})
{ env with realizedImportedConsts? := env.realizedImportedConsts?.map ({ · with
{ env with importRealizationCtx? := env.importRealizationCtx?.map ({ · with
-- safety: `RealizationContext` is private
env := unsafe unsafeCast env
}) }
@@ -1515,7 +1504,7 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
extensions := exts
irBaseExts := exts
}
realizedImportedConsts? := none
importRealizationCtx? := none
}
structure PersistentEnvExtensionState (α : Type) (σ : Type) where
@@ -2189,7 +2178,7 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
let mut env : Environment := {
base.private := privateBase
base.public := publicBase
realizedImportedConsts? := none
importRealizationCtx? := none
serverBaseExts := ( setImportedEntries privateBase.extensions serverData)
}
if leakEnv then
@@ -2215,11 +2204,11 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
Safety: There are no concurrent accesses to `env` at this point, assuming
extensions' `addImportFn`s did not spawn any unbound tasks. -/
env unsafe Runtime.markPersistent env
return { env with realizedImportedConsts? := some {
return { env with importRealizationCtx? := some {
-- safety: `RealizationContext` is private
env := unsafe unsafeCast env
opts
constsRef := ( IO.mkRef {})
realizeMapRef := ( IO.mkRef {})
} }
/--
@@ -2432,98 +2421,123 @@ def hasUnsafe (env : Environment) (e : Expr) : Bool :=
| _ => false;
c?.isSome
/-- Plumbing function for `Lean.Meta.realizeValue`; see documentation there. -/
def realizeValue [BEq α] [Hashable α] [TypeName α] (env : Environment) (forConst : Name) (key : α)
(realize : Environment Options BaseIO Dynamic) : IO Dynamic := do
-- the following code is inherently non-deterministic in number of heartbeats, reset them at the
-- end
let heartbeats IO.getNumHeartbeats
-- find `RealizationContext` for `forConst` in `importRealizationCtx?` or `localRealizationCtxMap`
let ctx if env.base.get env |>.const2ModIdx.contains forConst then
env.importRealizationCtx?.getDM <|
throw <| .userError s!"Environment.realizeConst: `realizedImportedConsts` is empty"
else
match env.localRealizationCtxMap.find? forConst with
| some ctx => pure ctx
| none =>
throw <| .userError s!"trying to realize `{TypeName.typeName α}` value but \
`enableRealizationsForConst` must be called for '{forConst}' first"
let prom IO.Promise.new
-- atomically check whether we are the first branch to realize `key`
let existingConsts? ctx.realizeMapRef.modifyGet fun m =>
-- Safety: `typeName α` should uniquely identify `PHashMap α (Task Dynamic)`; there are no other
-- accesses to `private realizeMapRef` outside this function.
let m' := match m.find? (TypeName.typeName α) with
| some m' => unsafe unsafeCast (β := PHashMap α (Task Dynamic)) m'
| none => {}
match m'[key] with
| some prom' => (some prom', m)
| none =>
let m' := m'.insert key prom.result!
let m := m.insert (TypeName.typeName α) (unsafe unsafeCast (β := NonScalar) m')
(none, m)
let res if let some t := existingConsts? then
pure t.get
else
-- safety: `RealizationContext` is private
let realizeEnv : Environment := unsafe unsafeCast ctx.env
let realizeEnv := { realizeEnv with
-- allow realizations to recursively realize other constants for `forConst`. Do note that
-- this allows for recursive realization of `α` itself, which will deadlock.
localRealizationCtxMap := realizeEnv.localRealizationCtxMap.insert forConst ctx
importRealizationCtx? := env.importRealizationCtx?
}
let res realize realizeEnv ctx.opts
prom.resolve res
pure res
IO.setNumHeartbeats heartbeats
return res
private structure RealizeConstKey where
constName : Name
deriving BEq, Hashable, TypeName
/-- Realization results, to be replayed onto other branches. -/
private structure RealizeConstResult where
newConsts : VisibilityMap (List AsyncConst)
replayKernel : Kernel.Environment Except Kernel.Exception Kernel.Environment
dyn : Dynamic
deriving Nonempty, TypeName
/-- Plumbing function for `Lean.Meta.realizeConst`; see documentation there. -/
def realizeConst (env : Environment) (forConst : Name) (constName : Name)
(realize : Environment Options BaseIO (Environment × Dynamic)) :
IO (Environment × Task (Option Kernel.Exception) × Dynamic) := do
-- the following code is inherently non-deterministic in number of heartbeats, reset them at the
-- end
let heartbeats IO.getNumHeartbeats
if env.asyncCtx?.any (·.realizingStack.contains constName) then
throw <| IO.userError s!"Environment.realizeConst: cyclic realization of '{constName}'"
let mut env := env
-- find `RealizationContext` for `forConst` in `realizedImportedConsts?` or `realizedLocalConsts`
let ctx if env.base.get env |>.const2ModIdx.contains forConst then
env.realizedImportedConsts?.getDM <|
throw <| .userError s!"Environment.realizeConst: `realizedImportedConsts` is empty"
else
match env.realizedLocalConsts.find? forConst with
| some ctx => pure ctx
| none =>
throw <| .userError s!"trying to realize {constName} but `enableRealizationsForConst` must be called for '{forConst}' first"
let prom IO.Promise.new
-- ensure `prom` is not left unresolved from stray exceptions
BaseIO.toIO do
-- atomically check whether we are the first branch to realize `constName`
let existingConsts? ctx.constsRef.modifyGet fun m => match m.find? constName with
| some prom' => (some prom', m)
| none => (none, m.insert constName prom.result!)
let res if let some existingConsts := existingConsts? then
pure existingConsts.get
else
-- safety: `RealizationContext` is private
let realizeEnv : Environment := unsafe unsafeCast ctx.env
let realizeEnv := { realizeEnv with
-- allow realizations to recursively realize other constants for `forConst`. Do note that
-- this allows for recursive realization of `constName` itself, which will deadlock.
realizedLocalConsts := realizeEnv.realizedLocalConsts.insert forConst ctx
realizedImportedConsts? := env.realizedImportedConsts?
}
-- ensure that environment extension modifications know they are in an async context
let realizeEnv := realizeEnv.enterAsyncRealizing constName
-- skip kernel in `realize`, we'll re-typecheck anyway
let realizeOpts := debug.skipKernelTC.set ctx.opts true
let (realizeEnv', dyn) realize realizeEnv realizeOpts
-- We could check that `c` was indeed added here but in practice `realize` has already
-- reported an error so we don't.
let res env.realizeValue forConst { constName : RealizeConstKey } fun realizeEnv realizeOpts => do
-- ensure that environment extension modifications know they are in an async context
let realizeEnv := realizeEnv.enterAsyncRealizing constName
-- skip kernel in `realize`, we'll re-typecheck anyway
let realizeOpts := debug.skipKernelTC.set realizeOpts true
let (realizeEnv', dyn) realize realizeEnv realizeOpts
-- We could check that `c` was indeed added here but in practice `realize` has already
-- reported an error so we don't.
-- find new constants incl. nested realizations, add current extension state, and compute
-- closure
let numNewPrivateConsts := realizeEnv'.asyncConstsMap.private.size - realizeEnv.asyncConstsMap.private.size
let newPrivateConsts := realizeEnv'.asyncConstsMap.private.revList.take numNewPrivateConsts |>.reverse
let newPrivateConsts := newPrivateConsts.map fun c =>
let c := { c with isRealized := true }
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.base.private.extensions }
else c
let numNewPublicConsts := realizeEnv'.asyncConstsMap.public.size - realizeEnv.asyncConstsMap.public.size
let newPublicConsts := realizeEnv'.asyncConstsMap.public.revList.take numNewPublicConsts |>.reverse
let newPublicConsts := newPublicConsts.map fun c =>
let c := { c with isRealized := true }
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.base.private.extensions }
else c
let exts EnvExtension.envExtensionsRef.get
let replayKernel := replayConsts.replayKernel (skipExisting := true) realizeEnv realizeEnv' exts newPrivateConsts
let res := { newConsts.private := newPrivateConsts, newConsts.public := newPublicConsts, replayKernel, dyn }
prom.resolve res
pure res
let exPromise IO.Promise.new
let env := { env with
asyncConstsMap := {
«private» := res.newConsts.private.foldl (init := env.asyncConstsMap.private) fun consts c =>
if consts.find? c.constInfo.name |>.isSome then
consts
else
consts.add c
«public» := res.newConsts.public.foldl (init := env.asyncConstsMap.public) fun consts c =>
if consts.find? c.constInfo.name |>.isSome then
consts
else
consts.add c
}
checked := ( BaseIO.mapTask (t := env.checked) fun kenv => do
match res.replayKernel kenv with
| .ok kenv => return kenv
| .error e =>
exPromise.resolve e
return kenv)
allRealizations := env.allRealizations.map (sync := true) fun allRealizations =>
res.newConsts.private.foldl (init := allRealizations) fun allRealizations c =>
allRealizations.insert c.constInfo.name c
-- find new constants incl. nested realizations, add current extension state, and compute
-- closure
let numNewPrivateConsts := realizeEnv'.asyncConstsMap.private.size - realizeEnv.asyncConstsMap.private.size
let newPrivateConsts := realizeEnv'.asyncConstsMap.private.revList.take numNewPrivateConsts |>.reverse
let newPrivateConsts := newPrivateConsts.map fun c =>
let c := { c with isRealized := true }
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.base.private.extensions }
else c
let numNewPublicConsts := realizeEnv'.asyncConstsMap.public.size - realizeEnv.asyncConstsMap.public.size
let newPublicConsts := realizeEnv'.asyncConstsMap.public.revList.take numNewPublicConsts |>.reverse
let newPublicConsts := newPublicConsts.map fun c =>
let c := { c with isRealized := true }
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.base.private.extensions }
else c
let exts EnvExtension.envExtensionsRef.get
let replayKernel := replayConsts.replayKernel (skipExisting := true) realizeEnv realizeEnv' exts newPrivateConsts
let res : RealizeConstResult := { newConsts.private := newPrivateConsts, newConsts.public := newPublicConsts, replayKernel, dyn }
pure (.mk res)
let some res := res.get? RealizeConstResult | unreachable!
let exPromise IO.Promise.new
let env := { env with
asyncConstsMap := {
«private» := res.newConsts.private.foldl (init := env.asyncConstsMap.private) fun consts c =>
if consts.find? c.constInfo.name |>.isSome then
consts
else
consts.add c
«public» := res.newConsts.public.foldl (init := env.asyncConstsMap.public) fun consts c =>
if consts.find? c.constInfo.name |>.isSome then
consts
else
consts.add c
}
IO.setNumHeartbeats heartbeats
return (env, exPromise.result?, res.dyn)
checked := ( BaseIO.mapTask (t := env.checked) fun kenv => do
match res.replayKernel kenv with
| .ok kenv => return kenv
| .error e =>
exPromise.resolve e
return kenv)
allRealizations := env.allRealizations.map (sync := true) fun allRealizations =>
res.newConsts.private.foldl (init := allRealizations) fun allRealizations c =>
allRealizations.insert c.constInfo.name c
}
return (env, exPromise.result?, res.dyn)
end Environment

View File

@@ -288,7 +288,7 @@ def normLtAux : Level → Nat → Level → Nat → Bool
def normLt (l₁ l₂ : Level) : Bool :=
normLtAux l₁ 0 l₂ 0
private def isAlreadyNormalizedCheap : Level Bool
def isAlreadyNormalizedCheap : Level Bool
| zero => true
| param _ => true
| mvar _ => true

View File

@@ -232,7 +232,7 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
stx.isOfKind ``Lean.Parser.Command.optDeclSig ||
stx.isOfKind ``Lean.Parser.Command.declSig) &&
(stack[5]? |>.any fun (stx, _) => match stx[0] with
| `(Lean.Parser.Command.declModifiersT| $[$_:docComment]? @[$[$attrs:attr],*] $[$vis]? $[noncomputable]?) =>
| `(Lean.Parser.Command.declModifiersT| $[$_:docComment]? @[$[$attrs:attr],*] $[$vis]? $[protected]? $[noncomputable]?) =>
attrs.any (fun attr => attr.raw.isOfKind ``Parser.Attr.extern || attr matches `(attr| implemented_by $_))
| _ => false))

View File

@@ -70,7 +70,7 @@ partial def visit (e : Expr) : M Expr := do
lctx := lctx.modifyLocalDecl xFVarId fun _ => localDecl
withLCtx lctx localInstances k
checkCache { val := e : ExprStructEq } fun _ => do
if ( withoutExporting do isNonTrivialProof e) then
if ( isNonTrivialProof e) then
/- Ensure proofs nested in type are also abstracted -/
abstractProof e ( read).cache visit
else match e with

View File

@@ -295,6 +295,7 @@ structure FunInfo where
That is, the (0-indexed) position of parameters that the result type depends on.
-/
resultDeps : Array Nat := #[]
deriving TypeName
/--
Key for the function information cache.
@@ -1951,6 +1952,18 @@ def withErasedFVars [MonadLCtx n] [MonadLiftT MetaM n] (fvarIds : Array FVarId)
let localInsts' := localInsts.filter (!fvarIds.contains ·.fvar.fvarId!)
withLCtx lctx' localInsts' k
/--
Ensures that the user names of all local declarations after index `idx` have a macro scope.
-/
def withFreshUserNamesSinceIdx [MonadLCtx n] [MonadLiftT MetaM n] (idx : Nat) (k : n α) : n α := do
let mut lctx getLCtx
for i in [idx:lctx.numIndices] do
let some decl := lctx.decls[i]! | continue
let n := decl.userName
if !n.hasMacroScopes then
lctx := lctx.setUserName decl.fvarId ( liftMetaM <| mkFreshUserName n)
withLCtx' lctx k
private def withMVarContextImp (mvarId : MVarId) (x : MetaM α) : MetaM α := do
let mvarDecl mvarId.getDecl
withLocalContextImp mvarDecl.lctx mvarDecl.localInstances x
@@ -2457,8 +2470,86 @@ private partial def setAllDiagRanges (snap : Language.SnapshotTree) (pos endPos
open Language
private structure RealizeValueResult where
res? : Except Exception Dynamic
snap? : Option SnapshotTree
deriving TypeName
/--
Realizes and caches a value for a given key with all environment objects derived from calling
`enableRealizationsForConst forConst` (fails if not called yet). If
this is the first environment branch passing the specific `key`, `realize` is called with the
environment and options at the time of calling `enableRealizationsForConst` if `forConst` is from
the current module and the state just after importing otherwise, thus helping achieve deterministic
results despite the non-deterministic choice of which thread is tasked with realization. In other
words, the result of `realizeValue` is *as if* `realize` had been called immediately after
`enableRealizationsForConst forConst`, with most effects but the return value discarded (see below).
Whether two calls of `realizeValue` with different `forConst`s but the same `key` share the result
is undefined; in practice, the key should usually uniquely determine `forConst` by e.g. including it
as a field.
`realizeValue` cannot check what other data is captured in the `realize` closure,
so it is best practice to extract it into a separate function and pass only arguments uniquely
determined by `key`. Traces, diagnostics, and raw std stream
output of `realize` are reported at all callers via `Core.logSnapshotTask` (so that the location of
generated diagnostics is deterministic). Note that, as `realize` is run using the options at
declaration time of `forConst`, trace options must be set prior to that (or, for imported constants,
on the cmdline) in order to be active. If `realize` throws an exception, it is rethrown at all
callers.
-/
def realizeValue [BEq α] [Hashable α] [TypeName α] [TypeName β] (forConst : Name) (key : α) (realize : MetaM β) :
MetaM β := do
let env getEnv
if !env.areRealizationsEnabledForConst forConst then
return ( realize)
let coreCtx readThe Core.Context
let coreCtx := {
-- these fields should be invariant throughout the file
fileName := coreCtx.fileName, fileMap := coreCtx.fileMap
-- heartbeat limits inside `realizeAndReport` should be measured from this point on
initHeartbeats := ( IO.getNumHeartbeats)
}
let res env.realizeValue forConst key (realizeAndReport coreCtx)
let some res := res.get? RealizeValueResult | unreachable!
if let some snap := res.snap? then
let mut snap := snap
-- localize diagnostics
if let some range := ( getRef).getRange? then
let fileMap getFileMap
snap setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
Core.logSnapshotTask <| .finished (stx? := none) snap
match res.res? with
| .ok dyn => dyn.get? β |>.getDM (unreachable!)
| .error e => throw e
where
-- similar to `wrapAsyncAsSnapshot` but not sufficiently so to share code
realizeAndReport (coreCtx : Core.Context) env opts := do
let coreCtx := { coreCtx with options := opts }
let act :=
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get opts) (do
-- catch all exceptions
let _ : MonadExceptOf _ MetaM := MonadAlwaysExcept.except
observing do
realize)
<* addTraceAsMessages
let res? act |>.run' |>.run coreCtx { env } |>.toBaseIO
let res match res? with
| .ok ((output, err?), st) => pure {
snap? := ( Core.mkSnapshot? output coreCtx st)
res? := err?.map (.mk)
: RealizeValueResult
}
| _ =>
let _ : Inhabited RealizeValueResult := {
snap? := ( Core.mkSnapshot? "" coreCtx { env })
res? := default
: RealizeValueResult
}
unreachable!
return .mk (α := RealizeValueResult) res
private structure RealizeConstantResult where
snap : SnapshotTree
snap? : Option SnapshotTree
error? : Option Exception
deriving TypeName
@@ -2515,12 +2606,13 @@ def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
cancelTk? := none
}
if let some res := dyn.get? RealizeConstantResult then
let mut snap := res.snap
-- localize diagnostics
if let some range := ( getRef).getRange? then
let fileMap getFileMap
snap setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
Core.logSnapshotTask <| .finished (stx? := none) snap
if let some snap := res.snap? then
let mut snap := snap
-- localize diagnostics
if let some range := ( getRef).getRange? then
let fileMap getFileMap
snap setAllDiagRanges snap (fileMap.toPosition range.start) (fileMap.toPosition range.stop)
Core.logSnapshotTask <| .finished (stx? := none) snap
if let some e := res.error? then
throw e
setEnv env
@@ -2543,7 +2635,7 @@ where
let res? act |>.run' |>.run coreCtx { env } |>.toBaseIO
match res? with
| .ok ((output, err?), st) => pure (st.env, .mk {
snap := ( Core.mkSnapshot output coreCtx st)
snap? := ( Core.mkSnapshot? output coreCtx st)
error? := match err? with
| .ok () => none
| .error e => some e
@@ -2551,7 +2643,7 @@ where
})
| _ =>
let _ : Inhabited (Environment × Dynamic) := env, .mk {
snap := ( Core.mkSnapshot "" coreCtx { env })
snap? := ( Core.mkSnapshot? "" coreCtx { env })
error? := none
: RealizeConstantResult
}

View File

@@ -50,39 +50,50 @@ def getMVarsAtDecl (d : Declaration) : MetaM (Array MVarId) := do
let (_, s) (collectMVarsAtDecl d).run {}
pure s.result
end Lean.Meta
open Lean Meta
mutual
/-- Auxiliary definition for `getMVarDependencies`. -/
private partial def addMVars (e : Expr) (includeDelayed := false) : StateRefT (Std.HashSet MVarId) MetaM Unit := do
let mvars getMVars e
let mut s get
set ({} : Std.HashSet MVarId) -- Ensure that `s` is not shared.
for mvarId in mvars do
if pure includeDelayed <||> notM (mvarId.isDelayedAssigned) then
s := s.insert mvarId
set s
mvars.forM go
/-- Auxiliary definition for `getMVarDependencies`. -/
private partial def go (mvarId : MVarId) (includeDelayed := false) : StateRefT (Std.HashSet MVarId) MetaM Unit :=
withIncRecDepth do
let mdecl mvarId.getDecl
addMVars mdecl.type includeDelayed
for ldecl in mdecl.lctx do
addMVars ldecl.type includeDelayed
if let (some val) := ldecl.value? then
addMVars val includeDelayed
if let (some ass) getDelayedMVarAssignment? mvarId then
let pendingMVarId := ass.mvarIdPending
if notM pendingMVarId.isAssignedOrDelayedAssigned then
modify (·.insert pendingMVarId)
go pendingMVarId includeDelayed
end
/--
Collect the metavariables which `mvarId` depends on. These are the metavariables
which appear in the type and local context of `mvarId`, as well as the
metavariables which *those* metavariables depend on, etc.
-/
partial def _root_.Lean.MVarId.getMVarDependencies (mvarId : MVarId) (includeDelayed := false) :
def Lean.MVarId.getMVarDependencies (mvarId : MVarId) (includeDelayed := false) :
MetaM (Std.HashSet MVarId) :=
(·.snd) <$> (go mvarId).run {}
where
/-- Auxiliary definition for `getMVarDependencies`. -/
addMVars (e : Expr) : StateRefT (Std.HashSet MVarId) MetaM Unit := do
let mvars getMVars e
let mut s get
set ({} : Std.HashSet MVarId) -- Ensure that `s` is not shared.
for mvarId in mvars do
if pure includeDelayed <||> notM (mvarId.isDelayedAssigned) then
s := s.insert mvarId
set s
mvars.forM go
(·.snd) <$> (go mvarId includeDelayed).run {}
/-- Auxiliary definition for `getMVarDependencies`. -/
go (mvarId : MVarId) : StateRefT (Std.HashSet MVarId) MetaM Unit :=
withIncRecDepth do
let mdecl mvarId.getDecl
addMVars mdecl.type
for ldecl in mdecl.lctx do
addMVars ldecl.type
if let (some val) := ldecl.value? then
addMVars val
if let (some ass) getDelayedMVarAssignment? mvarId then
let pendingMVarId := ass.mvarIdPending
if notM pendingMVarId.isAssignedOrDelayedAssigned then
modify (·.insert pendingMVarId)
go pendingMVarId
end Lean.Meta
/-- Collect the metavariables appearing in the expression `e`,
including metavariables in the type or local context of any such metavariables, etc. -/
def Lean.Expr.getMVarDependencies (e : Expr) (includeDelayed := false) : MetaM (Std.HashSet MVarId) := do
(·.snd) <$> (addMVars e includeDelayed).run {}

View File

@@ -295,9 +295,7 @@ private partial def mkCast (fvarId : FVarId) (type : Expr) (deps : Array Nat) (e
mvarId := mvarId'
let fvarId := getFVarId s fvarId
mvarId.assign (mkFVar fvarId)
let r instantiateMVars mvar
trace[Meta.debug] "{r} : {← inferType r}"
return r
instantiateMVars mvar
/--
Creates a congruence theorem that is useful for the simplifier and `congr` tactic.

View File

@@ -101,6 +101,22 @@ builtin_dsimproc_decl normNatDivInst ((_ / _ : Nat)) := normInst 3 Nat.mkInstHDi
builtin_dsimproc_decl normNatModInst ((_ % _ : Nat)) := normInst 3 Nat.mkInstMod
builtin_dsimproc_decl normNatPowInst ((_ ^ _ : Nat)) := normInst 3 Nat.mkInstHPow
/--
Returns `true`, if `@OfNat.ofNat α n inst` is the standard way we represent `Nat` numerals in Lean.
-/
private def isNormNatNum (α n inst : Expr) : Bool := Id.run do
unless α.isConstOf ``Nat do return false
let .lit (.natVal _) := n | return false
unless inst.isAppOfArity ``instOfNatNat 1 do return false
return inst.appArg! == n
builtin_dsimproc_decl normNatOfNatInst ((OfNat.ofNat _: Nat)) := fun e => do
let_expr OfNat.ofNat α n inst := e | return .continue
if isNormNatNum α n inst then
return .done e
let some n getNatValue? e | return .continue
return .done (mkNatLit n)
builtin_dsimproc_decl normIntNegInst ((- _ : Int)) := normInst 1 Int.mkInstNeg
builtin_dsimproc_decl normIntAddInst ((_ + _ : Int)) := normInst 3 Int.mkInstHAdd
builtin_dsimproc_decl normIntMulInst ((_ * _ : Int)) := normInst 3 Int.mkInstHMul
@@ -109,6 +125,22 @@ builtin_dsimproc_decl normIntDivInst ((_ / _ : Int)) := normInst 3 Int.mkInstHDi
builtin_dsimproc_decl normIntModInst ((_ % _ : Int)) := normInst 3 Int.mkInstMod
builtin_dsimproc_decl normIntPowInst ((_ ^ _ : Int)) := normInst 3 Int.mkInstHPow
/--
Returns `true`, if `@OfNat.ofNat α n inst` is the standard way we represent `Int` numerals in Lean.
-/
private def isNormIntNum (α n inst : Expr) : Bool := Id.run do
unless α.isConstOf ``Int do return false
let .lit (.natVal _) := n | return false
unless inst.isAppOfArity ``instOfNat 1 do return false
return inst.appArg! == n
builtin_dsimproc_decl normIntOfNatInst ((OfNat.ofNat _: Int)) := fun e => do
let_expr OfNat.ofNat α n inst := e | return .continue
if isNormIntNum α n inst then
return .done e
let some n getIntValue? e | return .continue
return .done (mkIntLit n)
/-!
Add additional arithmetic simprocs
-/
@@ -122,6 +154,7 @@ def addSimproc (s : Simprocs) : CoreM Simprocs := do
let s s.add ``normNatDivInst (post := false)
let s s.add ``normNatModInst (post := false)
let s s.add ``normNatPowInst (post := false)
let s s.add ``normNatOfNatInst (post := false)
let s s.add ``normIntNegInst (post := false)
let s s.add ``normIntAddInst (post := false)
let s s.add ``normIntMulInst (post := false)
@@ -129,6 +162,7 @@ def addSimproc (s : Simprocs) : CoreM Simprocs := do
let s s.add ``normIntDivInst (post := false)
let s s.add ``normIntModInst (post := false)
let s s.add ``normIntPowInst (post := false)
let s s.add ``normIntOfNatInst (post := false)
return s
end Lean.Meta.Grind.Arith

View File

@@ -202,7 +202,6 @@ partial def canon (e : Expr) : GoalM Expr := do profileitM Exception "grind cano
where
visit (e : Expr) : StateRefT (Std.HashMap ExprPtr Expr) GoalM Expr := do
unless e.isApp || e.isForall do return e
if ( inShareCommon e) then return e
-- Check whether it is cached
if let some r := ( get).get? { expr := e } then
return r

View File

@@ -131,6 +131,33 @@ protected def _root_.Lean.Meta.Grind.GenPatternInfo.assign? (genInfo : GenPatter
let c assignDelayedEqProof? c genInfo.hIdx
return c
private def matchGroundPattern (pArg eArg : Expr) : GoalM Bool := do
/-
1) Remark:
We need to use `withReducibleAndInstances` because ground patterns are often instances.
Here is an example
```
instance : Max Nat where
max := Nat.max -- Redefined the instance
example (a : Nat) : max a a = a := by
grind
```
Possible future improvements:
- When `diagnostics` is true, try with `withDefault` and report issue if it succeeds.
- (minor) Only use `withReducibleAndInstances` if the argument is an implicit instance.
Potential issue: some user write `{_ : Class α}` when the instance can be inferred from
explicit arguments.
2) Remark:
If `pArg` contains universe metavariables, we use `withoutModifyingMCtx` to ensure the metavariables
are not assigned. These universe metavariables are created at `internalizePattern` for universe polymorphic
ground patterns. They are not common, but they occur in practice.
-/
if pArg.hasLevelMVar then
withoutModifyingMCtx <| withReducibleAndInstances <| isDefEq pArg eArg
else
isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg)
/-- Matches a pattern argument. See `matchArgs?`. -/
private def matchArg? (c : Choice) (pArg : Expr) (eArg : Expr) : OptionT GoalM Choice := do
if isPatternDontCare pArg then
@@ -138,23 +165,7 @@ private def matchArg? (c : Choice) (pArg : Expr) (eArg : Expr) : OptionT GoalM C
else if pArg.isBVar then
assign? c pArg.bvarIdx! eArg
else if let some pArg := groundPattern? pArg then
/-
We need to use `withReducibleAndInstances` because ground patterns are often instances.
Here is an example
```
instance : Max Nat where
max := Nat.max -- Redefined the instance
example (a : Nat) : max a a = a := by
grind
```
Possible future improvements:
- When `diagnostics` is true, try with `withDefault` and report issue if it succeeds.
- (minor) Only use `withReducibleAndInstances` if the argument is an implicit instance.
Potential issue: some user write `{_ : Class α}` when the instance can be inferred from
explicit arguments.
-/
guard ( isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg))
guard ( matchGroundPattern pArg eArg)
return c
else if let some (pArg, k) := isOffsetPattern? pArg then
assert! Option.isNone <| isOffsetPattern? pArg
@@ -165,7 +176,7 @@ private def matchArg? (c : Choice) (pArg : Expr) (eArg : Expr) : OptionT GoalM C
let c assign? c pArg.bvarIdx! eArg
genInfo.assign? c eArg
else if let some pArg := groundPattern? pArg then
guard ( isEqv pArg eArg <||> withReducibleAndInstances (isDefEq pArg eArg))
guard ( matchGroundPattern pArg eArg)
genInfo.assign? c eArg
else if let some (pArg, k) := isOffsetPattern? pArg then
return { c with cnstrs := .offset (some genInfo) pArg k eArg :: c.cnstrs }

View File

@@ -964,6 +964,9 @@ def addEMatchEqTheorem (declName : Name) : MetaM Unit := do
def getEMatchTheorems : CoreM EMatchTheorems :=
return ematchTheoremsExt.getState ( getEnv)
def EMatchTheorems.getOrigins (s : EMatchTheorems) : List Origin :=
s.origins.toList
/-- Returns the types of `xs` that are propositions. -/
private def getPropTypes (xs : Array Expr) : MetaM (Array Expr) :=
xs.filterMapM fun x => do

View File

@@ -12,6 +12,7 @@ public import Lean.Meta.LitValues
public import Lean.Meta.Match.MatcherInfo
public import Lean.Meta.Match.MatchEqsExt
public import Lean.Meta.Match.MatchEqs
public import Lean.Util.CollectLevelParams
public import Lean.Meta.Tactic.Grind.Types
public import Lean.Meta.Tactic.Grind.Util
public import Lean.Meta.Tactic.Grind.Canon
@@ -151,7 +152,7 @@ private def mkENode' (e : Expr) (generation : Nat) : GoalM Unit :=
mkENodeCore e (ctor := false) (interpreted := false) (generation := generation)
/-- Internalizes the nested ground terms in the given pattern. -/
private partial def internalizePattern (pattern : Expr) (generation : Nat) : GoalM Expr := do
private partial def internalizePattern (pattern : Expr) (generation : Nat) (origin : Origin) : GoalM Expr := do
-- Recall that it is important to ensure patterns are maximally shared since
-- we assume that in functions such as `getAppsOf` in `EMatch.lean`
go ( shareCommon pattern)
@@ -161,7 +162,21 @@ where
return pattern
else if let some e := groundPattern? pattern then
let e preprocessLight e
internalize e generation none
let e if e.hasLevelParam && origin matches .decl _ then
/-
If `e` has universe parameters and it is **not** local. That is,
it contains the universe parameters of some global theorem.
Then, we convert `e`'s universe parameters into universe meta-variables.
Remark: it is pointless to internalize the result because it contains these helper meta-variables.
Remark: universe polymorphic ground patterns are not common, but they do occur in the
core library.
-/
let ps := collectLevelParams {} e |>.params
let us ps.mapM fun _ => mkFreshLevelMVar
pure <| e.instantiateLevelParamsArray ps us
else
internalize e generation none
pure e
return mkGroundPattern e
else pattern.withApp fun f args => do
return mkAppN f ( args.mapM go)
@@ -203,7 +218,7 @@ def activateTheorem (thm : EMatchTheorem) (generation : Nat) : GoalM Unit := do
-- Recall that we use the proof as part of the key for a set of instances found so far.
-- We don't want to use structural equality when comparing keys.
let proof shareCommon thm.proof
let thm := { thm with proof, patterns := ( thm.patterns.mapM (internalizePattern · generation)) }
let thm := { thm with proof, patterns := ( thm.patterns.mapM (internalizePattern · generation thm.origin)) }
trace_goal[grind.ematch] "activated `{thm.origin.key}`, {thm.patterns.map ppPattern}"
modify fun s => { s with ematch.newThms := s.ematch.newThms.push thm }
@@ -364,7 +379,7 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
propagateEtaStruct e generation
where
go : GoalM Unit := do
trace_goal[grind.internalize] "{e}"
trace_goal[grind.internalize] "[{generation}] {e}"
match e with
| .bvar .. => unreachable!
| .sort .. => return ()

View File

@@ -44,7 +44,6 @@ partial def markNestedSubsingletons (e : Expr) : GrindM Expr := do profileitM Ex
visit e |>.run' {}
where
visit (e : Expr) : M Expr := do
if ( inShareCommon e) then return e
if isMarkedSubsingletonApp e then
return e -- `e` is already marked
-- check whether result is cached

View File

@@ -334,16 +334,6 @@ def shareCommon (e : Expr) : GrindM Expr := do
modify fun s => { s with scState }
return e
/--
Returns `true` if `e` has already been hash-consed.
Recall that we use `shareCommon` as the last step of the preprocessing
function `preprocess`.
Later, we create terms using new terms that have already been preprocessed,
and we skip preprocessing steps by checking whether `inShareCommon` returns `true`
-/
def inShareCommon (e : Expr) : GrindM Bool := do
return ( get).scState.map.contains { expr := e }
/-- Returns `true` if `e` is the internalized `True` expression. -/
def isTrueExpr (e : Expr) : GrindM Bool :=
return isSameExpr e ( getTrueExpr)

View File

@@ -83,7 +83,7 @@ Unfolds all `reducible` declarations occurring in `e`.
-/
def unfoldReducible (e : Expr) : MetaM Expr := do
if !( isUnfoldReducibleTarget e) then return e
Core.transform e (pre := unfoldReducibleStep)
Meta.transform e (pre := unfoldReducibleStep)
/--
Unfolds all `reducible` declarations occurring in the goal's target.
@@ -182,10 +182,18 @@ def foldProjs (e : Expr) : MetaM Expr := do
return .done e
Meta.transform e (post := post)
/-- Quick filter for checking whether we can skip `normalizeLevels`. -/
private def levelsAlreadyNormalized (e : Expr) : Bool :=
Option.isNone <| e.find? fun
| .const _ us => us.any (! ·.isAlreadyNormalizedCheap)
| .sort u => !u.isAlreadyNormalizedCheap
| _ => false
/--
Normalizes universe levels in constants and sorts.
-/
def normalizeLevels (e : Expr) : CoreM Expr := do
if levelsAlreadyNormalized e then return e
let pre (e : Expr) := do
match e with
| .sort u => return .done <| e.updateSort! u.normalize

View File

@@ -62,11 +62,11 @@ def namedPrio := leading_parser
def optNamedPrio := optional namedPrio
def «private» := leading_parser "private "
def «protected» := leading_parser "protected "
def «public» := leading_parser "public "
def visibility :=
withAntiquot (mkAntiquot "visibility" decl_name% (isPseudoKind := true)) <|
«private» <|> «protected» <|> «public»
«private» <|> «public»
def «protected» := leading_parser "protected "
def «meta» := leading_parser "meta "
def «noncomputable» := leading_parser "noncomputable "
def «unsafe» := leading_parser "unsafe "
@@ -76,7 +76,8 @@ def «nonrec» := leading_parser "nonrec "
/-- `declModifiers` is the collection of modifiers on a declaration:
* a doc comment `/-- ... -/`
* a list of attributes `@[attr1, attr2]`
* a visibility specifier, `private`, `protected`, or `public`
* a visibility specifier, `private` or `public`
* `protected`
* `noncomputable`
* `unsafe`
* `partial` or `nonrec`
@@ -90,6 +91,7 @@ such as inductive constructors, structure projections, and `let rec` / `where` d
optional docComment >>
optional (Term.«attributes» >> if inline then skip else ppDedent ppLine) >>
optional visibility >>
optional «protected» >>
optional («meta» <|> «noncomputable») >>
optional «unsafe» >>
optional («partial» <|> «nonrec»)
@@ -159,7 +161,7 @@ def whereStructInst := leading_parser
def «abbrev» := leading_parser
"abbrev " >> declId >> ppIndent optDeclSig >> declVal
def optDefDeriving :=
optional (ppDedent ppLine >> atomic ("deriving " >> notSymbol "instance") >> sepBy1 ident ", ")
optional (ppDedent ppLine >> atomic ("deriving " >> notSymbol "instance") >> sepBy1 termParser ", ")
def definition := leading_parser
"def " >> recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >> declVal >> optDefDeriving
def «theorem» := leading_parser
@@ -179,7 +181,7 @@ def «example» := leading_parser
def ctor := leading_parser
atomic (optional docComment >> "\n| ") >>
ppGroup (declModifiers true >> rawIdent >> optDeclSig)
def derivingClasses := sepBy1 ident ", "
def derivingClasses := sepBy1 (withForbidden "for" termParser) ", "
def optDeriving := leading_parser
optional (ppLine >> atomic ("deriving " >> notSymbol "instance") >> derivingClasses)
def computedField := leading_parser
@@ -251,7 +253,7 @@ def «structure» := leading_parser
(«abbrev» <|> definition <|> «theorem» <|> «opaque» <|> «instance» <|> «axiom» <|> «example» <|>
«inductive» <|> classInductive <|> «structure»)
@[builtin_command_parser] def «deriving» := leading_parser
"deriving " >> "instance " >> derivingClasses >> " for " >> sepBy1 (recover ident skip) ", "
"deriving " >> "instance " >> derivingClasses >> " for " >> sepBy1 (recover termParser skip) ", "
def sectionHeader := leading_parser
optional ("@[" >> nonReservedSymbol "expose" >> "] ") >>
optional ("public ") >>

View File

@@ -62,13 +62,17 @@ def delabMVarAux (m : MVarId) : DelabM Term := do
let mkMVar (n : Name) : DelabM Term := `(?$(mkIdent n))
withTypeAscription (cond := getPPOption getPPMVarsWithType) do
if getPPOption getPPMVars then
match ( m.getDecl).userName with
| .anonymous =>
if getPPOption getPPMVarsAnonymous then
mkMVar <| m.name.replacePrefix `_uniq `m
else
mkMVarPlaceholder
| n => mkMVar n
if let some decl m.findDecl? then
match decl.userName with
| .anonymous =>
if getPPOption getPPMVarsAnonymous then
mkMVar <| Name.num `m (decl.index + 1)
else
mkMVarPlaceholder
| n => mkMVar n
else
-- Undefined mvar, use internal name
mkMVar <| m.name.replacePrefix `_uniq `_mvar
else
mkMVarPlaceholder
@@ -1270,6 +1274,24 @@ def delabPProdMk : Delab := delabPProdMkCore ``PProd.mk
@[builtin_delab app.MProd.mk]
def delabMProdMk : Delab := delabPProdMkCore ``MProd.mk
@[builtin_delab app.Std.Range.mk]
def delabRange : Delab := do
-- Std.Range.mk : Nat → Nat → (step : Nat) → 0 < step → Std.Range
let_expr Std.Range.mk start _stop step _prf := ( getExpr) | failure
let start_zero := Lean.Expr.nat? start == some 0
let step_one := Lean.Expr.nat? step == some 1
withAppFn do -- skip the proof
let step withAppArg delab
withAppFn do
let stop withAppArg delab
withAppFn do
let start withAppArg delab
match start_zero, step_one with
| false, false => `([$start : $stop : $step])
| false, true => `([$start : $stop])
| true, false => `([: $stop : $step])
| true, true => `([: $stop])
partial def delabDoElems : DelabM (List Syntax) := do
let e getExpr
if e.isAppOfArity ``Bind.bind 6 then

View File

@@ -15,6 +15,8 @@ public import Init.Data.List.MinMax
public import Init.Data.List.Monadic
public import all Std.Data.Internal.List.Defs
public import Std.Classes.Ord.Basic
import Init.Data.Subtype.Order
import Init.Data.Order.Lemmas
public section
@@ -5545,22 +5547,31 @@ private theorem le_min_iff [Ord α] [TransOrd α] {a b c : (a : α) × β a} :
· simp only [Bool.not_eq_true, Ordering.isLE_eq_false, OrientedCmp.gt_iff_lt, iff_and_self] at *
exact fun h => Ordering.isLE_of_eq_lt <| TransCmp.lt_of_isLE_of_lt h _
theorem minEntry?_eq_some_iff [Ord α] [TransOrd α] [BEq α] [LawfulBEqOrd α] (a : (a : α) × β a) {l : List ((a : α) × β a)} (hd : DistinctKeys l) :
private theorem antisymm_subtype [Ord α] [TransOrd α] [BEq α] [LawfulBEqOrd α]
{l : List ((a : α) × β a)} (hd : DistinctKeys l) :
Antisymm (α := Subtype (· l)) (· ·) where
antisymm a b hab hba := by
exact Subtype.ext
<| hd.eq_of_mem_of_beq a.property b.property
<| compare_eq_iff_beq.mp
<| OrientedCmp.isLE_antisymm hab hba
theorem minEntry?_eq_some_iff [Ord α] [TransOrd α] [BEq α] [LawfulBEqOrd α] (a : (a : α) × β a)
{l : List ((a : α) × β a)} (hd : DistinctKeys l) :
minEntry? l = some a a l b : α, containsKey b l (compare a.fst b).isLE := by
rw [minEntry?, List.min?_eq_some_iff _ _ _ _]
· simp only [and_congr_right_iff]
intro hm
apply Iff.intro
· intro h k hk
obtain e, hel, hek := containsKey_eq_true_iff_exists_mem.mp hk
exact TransCmp.isLE_trans (h _ hel) <| Ordering.isLE_of_eq_eq <| compare_eq_iff_beq.mpr hek
· intro h e he
exact h _ <| containsKey_of_mem he
· exact fun _ => ReflCmp.isLE_rfl
· exact fun _ _ => min_eq_or
· exact fun a b c => le_min_iff
· intro e e' he he' hee' he'e
exact hd.eq_of_mem_of_beq he he' <| compare_eq_iff_beq.mp <| OrientedCmp.isLE_antisymm hee' he'e
haveI : LawfulOrderMin ((a : α) × β a) := .of_le (fun _ _ _ => le_min_iff) (fun _ _ => min_eq_or)
haveI : Refl (α := (a : α) × β a) (· ·) := fun _ => ReflCmp.isLE_rfl
haveI : Antisymm (α := Subtype (· l)) (· ·) := antisymm_subtype hd
haveI : IsLinearOrder (Subtype (· l)) := IsLinearOrder.of_refl_of_antisymm_of_lawfulOrderMin
rw [minEntry?, List.min?_eq_some_iff_subtype]
simp only [and_congr_right_iff]
intro hm
apply Iff.intro
· intro h k hk
obtain e, hel, hek := containsKey_eq_true_iff_exists_mem.mp hk
exact TransCmp.isLE_trans (h _ hel) <| Ordering.isLE_of_eq_eq <| compare_eq_iff_beq.mpr hek
· intro h e he
exact h _ <| containsKey_of_mem he
theorem minKey?_eq_some_iff_getKey?_eq_self_and_forall [Ord α] [TransOrd α] [BEq α] [LawfulBEqOrd α]
{k} {l : List ((a : α) × β a)} (hd : DistinctKeys l) :

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Control.Lawful.Basic
public import Init.Data.Subtype
public import Init.Data.Subtype.Basic
public import Init.PropLemmas
public import Init.Classical
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction

View File

@@ -72,69 +72,69 @@ abbrev Assertion (ps : PostShape.{u}) : Type u :=
/--
Encodes one continuation barrel for each `PostShape.except` in the given predicate shape.
```
example : FailConds (.pure) = Unit := rfl
example : FailConds (.except ε .pure) = ((ε → ULift Prop) × Unit) := rfl
example : FailConds (.arg σ (.except ε .pure)) = ((ε → ULift Prop) × Unit) := rfl
example : FailConds (.except ε (.arg σ .pure)) = ((ε → σ → ULift Prop) × Unit) := rfl
example : ExceptConds (.pure) = Unit := rfl
example : ExceptConds (.except ε .pure) = ((ε → ULift Prop) × Unit) := rfl
example : ExceptConds (.arg σ (.except ε .pure)) = ((ε → ULift Prop) × Unit) := rfl
example : ExceptConds (.except ε (.arg σ .pure)) = ((ε → σ → ULift Prop) × Unit) := rfl
```
-/
def FailConds : PostShape.{u} Type u
def ExceptConds : PostShape.{u} Type u
| .pure => PUnit
| .arg _ ps => FailConds ps
| .except ε ps => (ε Assertion ps) × FailConds ps
| .arg _ ps => ExceptConds ps
| .except ε ps => (ε Assertion ps) × ExceptConds ps
@[simp]
def FailConds.const {ps : PostShape.{u}} (p : Prop) : FailConds ps := match ps with
def ExceptConds.const {ps : PostShape.{u}} (p : Prop) : ExceptConds ps := match ps with
| .pure =>
| .arg _ ps => @FailConds.const ps p
| .except _ ps => (fun _ε => spred(p), @FailConds.const ps p)
| .arg _ ps => @ExceptConds.const ps p
| .except _ ps => (fun _ε => spred(p), @ExceptConds.const ps p)
def FailConds.true : FailConds ps := FailConds.const True
def ExceptConds.true : ExceptConds ps := ExceptConds.const True
def FailConds.false : FailConds ps := FailConds.const False
def ExceptConds.false : ExceptConds ps := ExceptConds.const False
instance : Inhabited (FailConds ps) where
default := FailConds.true
instance : Inhabited (ExceptConds ps) where
default := ExceptConds.true
def FailConds.entails {ps : PostShape.{u}} (x y : FailConds ps) : Prop :=
def ExceptConds.entails {ps : PostShape.{u}} (x y : ExceptConds ps) : Prop :=
match ps with
| .pure => True
| .arg _ ps => @entails ps x y
| .except _ ps => ( e, x.1 e y.1 e) @entails ps x.2 y.2
scoped infix:25 " ⊢ₑ " => FailConds.entails
scoped infix:25 " ⊢ₑ " => ExceptConds.entails
@[refl, simp]
theorem FailConds.entails.refl {ps : PostShape} (x : FailConds ps) : x x := by
theorem ExceptConds.entails.refl {ps : PostShape} (x : ExceptConds ps) : x x := by
induction ps <;> simp [entails, *]
theorem FailConds.entails.rfl {ps : PostShape} {x : FailConds ps} : x x := refl x
theorem ExceptConds.entails.rfl {ps : PostShape} {x : ExceptConds ps} : x x := refl x
theorem FailConds.entails.trans {ps : PostShape} {x y z : FailConds ps} : (x y) (y z) x z := by
theorem ExceptConds.entails.trans {ps : PostShape} {x y z : ExceptConds ps} : (x y) (y z) x z := by
induction ps
case pure => intro _ _; trivial
case arg σ s ih => exact ih
case except ε s ih => intro h₁ h₂; exact fun e => (h₁.1 e).trans (h₂.1 e), ih h₁.2 h₂.2
@[simp]
theorem FailConds.entails_false {x : FailConds ps} : FailConds.false x := by
theorem ExceptConds.entails_false {x : ExceptConds ps} : ExceptConds.false x := by
induction ps <;> simp_all [false, const, entails, SPred.false_elim]
@[simp]
theorem FailConds.entails_true {x : FailConds ps} : x FailConds.true := by
theorem ExceptConds.entails_true {x : ExceptConds ps} : x ExceptConds.true := by
induction ps <;> simp_all [true, const, entails]
@[simp]
def FailConds.and {ps : PostShape.{u}} (x : FailConds ps) (y : FailConds ps) : FailConds ps :=
def ExceptConds.and {ps : PostShape.{u}} (x : ExceptConds ps) (y : ExceptConds ps) : ExceptConds ps :=
match ps with
| .pure =>
| .arg _ ps => @FailConds.and ps x y
| .except _ _ => (fun e => SPred.and (x.1 e) (y.1 e), FailConds.and x.2 y.2)
| .arg _ ps => @ExceptConds.and ps x y
| .except _ _ => (fun e => SPred.and (x.1 e) (y.1 e), ExceptConds.and x.2 y.2)
infixr:35 " ∧ₑ " => FailConds.and
infixr:35 " ∧ₑ " => ExceptConds.and
@[simp]
theorem FailConds.and_true {x : FailConds ps} : x FailConds.true x := by
theorem ExceptConds.and_true {x : ExceptConds ps} : x ExceptConds.true x := by
induction ps
case pure => trivial
case arg ih => exact ih
@@ -143,7 +143,7 @@ theorem FailConds.and_true {x : FailConds ps} : x ∧ₑ FailConds.true ⊢ₑ x
constructor <;> simp only [SPred.and_true.mp, implies_true, ih]
@[simp]
theorem FailConds.true_and {x : FailConds ps} : FailConds.true x x := by
theorem ExceptConds.true_and {x : ExceptConds ps} : ExceptConds.true x x := by
induction ps
case pure => trivial
case arg ih => exact ih
@@ -152,7 +152,7 @@ theorem FailConds.true_and {x : FailConds ps} : FailConds.true ∧ₑ x ⊢ₑ x
constructor <;> simp only [SPred.true_and.mp, implies_true, ih]
@[simp]
theorem FailConds.and_false {x : FailConds ps} : x FailConds.false FailConds.false := by
theorem ExceptConds.and_false {x : ExceptConds ps} : x ExceptConds.false ExceptConds.false := by
induction ps
case pure => trivial
case arg ih => exact ih
@@ -161,7 +161,7 @@ theorem FailConds.and_false {x : FailConds ps} : x ∧ₑ FailConds.false ⊢ₑ
constructor <;> simp only [SPred.and_false.mp, implies_true, ih]
@[simp]
theorem FailConds.false_and {x : FailConds ps} : FailConds.false x FailConds.false := by
theorem ExceptConds.false_and {x : ExceptConds ps} : ExceptConds.false x ExceptConds.false := by
induction ps
case pure => trivial
case arg ih => exact ih
@@ -169,7 +169,7 @@ theorem FailConds.false_and {x : FailConds ps} : FailConds.false ∧ₑ x ⊢ₑ
simp_all only [and, false, const]
constructor <;> simp only [SPred.false_and.mp, implies_true, ih]
theorem FailConds.and_eq_left {ps : PostShape} {p q : FailConds ps} (h : p q) :
theorem ExceptConds.and_eq_left {ps : PostShape} {p q : ExceptConds ps} (h : p q) :
p = (p q) := by
induction ps
case pure => trivial
@@ -188,10 +188,10 @@ example : PostCond α (.arg ρ .pure) = ((αρ → Prop) × Unit) := rfl
example : PostCond α (.except ε .pure) = ((α → Prop) × (ε → Prop) × Unit) := rfl
example : PostCond α (.arg σ (.except ε .pure)) = ((ασ → Prop) × (ε → Prop) × Unit) := rfl
example : PostCond α (.except ε (.arg σ .pure)) = ((ασ → Prop) × (ε → σ → Prop) × Unit) := rfl
```
```
-/
abbrev PostCond (α : Type u) (ps : PostShape.{u}) : Type u :=
(α Assertion ps) × FailConds ps
(α Assertion ps) × ExceptConds ps
@[inherit_doc PostCond]
scoped macro:max "post⟨" handlers:term,+,? "" : term =>
@@ -204,12 +204,12 @@ A postcondition expressing total correctness.
That is, it expresses that the asserted computation finishes without throwing an exception
*and* the result satisfies the given predicate `p`.
-/
abbrev PostCond.total (p : α Assertion ps) : PostCond α ps :=
(p, FailConds.false)
abbrev PostCond.noThrow (p : α Assertion ps) : PostCond α ps :=
(p, ExceptConds.false)
@[inherit_doc PostCond.total]
@[inherit_doc PostCond.noThrow]
scoped macro:max ppAllowUngrouped "" xs:term:max+ " => " e:term : term =>
`(PostCond.total (by exact fun $xs* => spred($e)))
`(PostCond.noThrow (by exact fun $xs* => spred($e)))
/--
A postcondition expressing partial correctness.
@@ -217,35 +217,39 @@ That is, it expresses that *if* the asserted computation finishes without throwi
*then* the result satisfies the given predicate `p`.
Nothing is asserted when the computation throws an exception.
-/
abbrev PostCond.partial (p : α Assertion ps) : PostCond α ps :=
(p, FailConds.true)
abbrev PostCond.mayThrow (p : α Assertion ps) : PostCond α ps :=
(p, ExceptConds.true)
@[inherit_doc PostCond.mayThrow]
scoped macro:max ppAllowUngrouped "⇓?" xs:term:max+ " => " e:term : term =>
`(PostCond.mayThrow (by exact fun $xs* => spred($e)))
instance : Inhabited (PostCond α ps) where
default := PostCond.total fun _ => default
default := PostCond.noThrow fun _ => default
@[simp]
def PostCond.entails (p q : PostCond α ps) : Prop :=
( a, SPred.entails (p.1 a) (q.1 a)) FailConds.entails p.2 q.2
( a, SPred.entails (p.1 a) (q.1 a)) ExceptConds.entails p.2 q.2
scoped infix:25 " ⊢ₚ " => PostCond.entails
@[refl, simp]
theorem PostCond.entails.refl (Q : PostCond α ps) : Q Q := fun a => SPred.entails.refl (Q.1 a), FailConds.entails.refl Q.2
theorem PostCond.entails.refl (Q : PostCond α ps) : Q Q := fun a => SPred.entails.refl (Q.1 a), ExceptConds.entails.refl Q.2
theorem PostCond.entails.rfl {Q : PostCond α ps} : Q Q := refl Q
theorem PostCond.entails.trans {P Q R : PostCond α ps} (h₁ : P Q) (h₂ : Q R) : P R :=
fun a => (h₁.1 a).trans (h₂.1 a), h₁.2.trans h₂.2
@[simp]
theorem PostCond.entails_total (p : α Assertion ps) (q : PostCond α ps) : PostCond.total p q a, p a q.1 a := by
simp only [entails, FailConds.entails_false, and_true]
theorem PostCond.entails_noThrow (p : α Assertion ps) (q : PostCond α ps) : PostCond.noThrow p q a, p a q.1 a := by
simp only [entails, ExceptConds.entails_false, and_true]
@[simp]
theorem PostCond.entails_partial (p : PostCond α ps) (q : α Assertion ps) : p PostCond.partial q a, p.1 a q a := by
simp only [entails, FailConds.entails_true, and_true]
theorem PostCond.entails_mayThrow (p : PostCond α ps) (q : α Assertion ps) : p PostCond.mayThrow q a, p.1 a q a := by
simp only [entails, ExceptConds.entails_true, and_true]
abbrev PostCond.and (p : PostCond α ps) (q : PostCond α ps) : PostCond α ps :=
(fun a => SPred.and (p.1 a) (q.1 a), FailConds.and p.2 q.2)
(fun a => SPred.and (p.1 a) (q.1 a), ExceptConds.and p.2 q.2)
scoped infixr:35 " ∧ₚ " => PostCond.and
@@ -253,4 +257,4 @@ theorem PostCond.and_eq_left {p q : PostCond α ps} (h : p ⊢ₚ q) :
p = (p q) := by
ext
· exact (SPred.and_eq_left.mp (h.1 _)).to_eq
· exact FailConds.and_eq_left h.2
· exact ExceptConds.and_eq_left h.2

View File

@@ -116,6 +116,10 @@ theorem bind_apply (x : PredTrans ps α) (f : α → PredTrans ps β) (Q : PostC
theorem seq_apply (f : PredTrans ps (α β)) (x : PredTrans ps α) (Q : PostCond β ps) :
(f <*> x).apply Q = f.apply (fun g => x.apply (fun a => Q.1 (g a), Q.2), Q.2) := by rfl
@[simp]
theorem const_apply (p : Assertion ps) (Q : PostCond α ps) :
(PredTrans.const p : PredTrans ps α).apply Q = p := by rfl
theorem bind_mono {x y : PredTrans ps α} {f : α PredTrans ps β}
(h : x y) : x >>= f y >>= f := by intro Q; exact (h (_, Q.2))

View File

@@ -166,9 +166,14 @@ theorem and_right_comm : (P ∧ Q) ∧ R ⊣⊢ₛ (P ∧ R) ∧ Q := and_assoc.
theorem entails_pure_intro (P Q : Prop) (h : P Q) : entails P (σs := σs) Q := pure_elim' fun hp => pure_intro (h hp)
-- NB: We cannot currently make the following lemma @[grind =]; we are blocked on #9623.
-- However, the stronger SPred.entails_cons is already @[grind =].
@[simp] theorem entails_pure_elim_cons {σ : Type u} [Inhabited σ] (P Q : Prop) : entails P (σs := σ::σs) Q entails P (σs := σs) Q := by simp [entails]
@[simp, grind =] theorem entails_true_intro (P Q : SPred σs) : ( P Q) = (P Q) := propext <| Iff.intro (fun h => (and_intro true_intro .rfl).trans (imp_elim h)) (fun h => imp_intro (and_elim_r.trans h))
theorem entails_pure_elim_cons {σ : Type u} [Inhabited σ] (P Q : Prop) : entails P (σs := σ::σs) Q entails P (σs := σs) Q := by simp [entails]
@[simp] theorem entails_true_intro (P Q : SPred σs) : ( P Q) = (P Q) := propext <| Iff.intro (fun h => (and_intro true_intro .rfl).trans (imp_elim h)) (fun h => imp_intro (and_elim_r.trans h))
-- The following lemmas work around a DefEq incompleteness that would be fixed by #9015.
@[simp] theorem entails_1 {P Q : SPred [σ]} : SPred.entails P Q = ( s, (P s).down (Q s).down) := rfl
@[simp] theorem entails_2 {P Q : SPred [σ₁, σ₂]} : SPred.entails P Q = ( s₁ s₂, (P s₁ s₂).down (Q s₁ s₂).down) := rfl
@[simp] theorem entails_3 {P Q : SPred [σ₁, σ₂, σ₃]} : SPred.entails P Q = ( s₁ s₂ s₃, (P s₁ s₂ s₃).down (Q s₁ s₂ s₃).down) := rfl
@[simp] theorem entails_4 {P Q : SPred [σ₁, σ₂, σ₃, σ₄]} : SPred.entails P Q = ( s₁ s₂ s₃ s₄, (P s₁ s₂ s₃ s₄).down (Q s₁ s₂ s₃ s₄).down) := rfl
@[simp] theorem entails_5 {P Q : SPred [σ₁, σ₂, σ₃, σ₄, σ₅]} : SPred.entails P Q = ( s₁ s₂ s₃ s₄ s₅, (P s₁ s₂ s₃ s₄ s₅).down (Q s₁ s₂ s₃ s₄ s₅).down) := rfl
/-! # Tactic support -/
@@ -191,6 +196,7 @@ instance (σs) : IsPure (σs:=σs) spred(⌜φ⌝ ⌜ψ⌝) (φ ψ) wher
instance (σs) (P : α Prop) : IsPure (σs:=σs) spred( x, P x) ( x, P x) where to_pure := pure_exists
instance (σs) (P : α Prop) : IsPure (σs:=σs) spred( x, P x) ( x, P x) where to_pure := pure_forall
instance (σs) (P : SPred (σ::σs)) [inst : IsPure P φ] : IsPure (σs:=σs) spred(P s) φ where to_pure := (iff_of_eq bientails_cons).mp inst.to_pure s
instance (σs) (P : SPred σs) [inst : IsPure P φ] : IsPure (σs:=σ::σs) (fun _ => P) φ where to_pure := (iff_of_eq bientails_cons).mpr (fun _ => inst.to_pure)
instance (φ : Prop) : IsPure (σs:=[]) φ φ where to_pure := Iff.rfl
instance (P : SPred []) : IsPure (σs:=[]) P P.down where to_pure := Iff.rfl
@@ -262,6 +268,7 @@ class HasFrame (P : SPred σs) (P' : outParam (SPred σs)) (φ : outParam Prop)
reassoc : P P' φ
instance (σs) (P P' Q QP : SPred σs) [HasFrame P Q φ] [SimpAnd Q P' QP]: HasFrame (σs:=σs) spred(P P') QP φ where reassoc := ((and_congr_l HasFrame.reassoc).trans and_right_comm).trans (and_congr_l SimpAnd.simp_and)
instance (σs) (P P' Q' PQ : SPred σs) [HasFrame P' Q' φ] [SimpAnd P Q' PQ]: HasFrame (σs:=σs) spred(P P') PQ φ where reassoc := ((and_congr_r HasFrame.reassoc).trans and_assoc.symm).trans (and_congr_l SimpAnd.simp_and)
instance (σs) (P P' : Prop) (Q : SPred σs) [HasFrame spred(P P') Q φ] : HasFrame (σs:=σs) P P' Q φ where reassoc := and_pure.symm.trans HasFrame.reassoc
instance (σs) (P P' : SVal.StateTuple σs Prop) (Q : SPred σs) [HasFrame spred(SVal.curry (fun t => P t) SVal.curry (fun t => P' t)) Q φ] : HasFrame (σs:=σs) (SVal.curry fun t => P t P' t) Q φ where reassoc := and_curry.symm.trans HasFrame.reassoc
instance (σs) (P : SPred σs) : HasFrame (σs:=σs) spred(φ P) P φ where reassoc := and_comm
instance (σs) (P : SPred σs) : HasFrame (σs:=σs) spred(P φ) P φ where reassoc := .rfl

View File

@@ -76,16 +76,34 @@ theorem bientails.to_eq {P Q : SPred σs} (h : P ⊣⊢ₛ Q) : P = Q := by
/-! # Pure -/
@[simp, grind =] theorem down_pure {φ : Prop} : (φ : SPred []).down = φ := rfl
@[simp, grind =] theorem apply_pure {φ : Prop} : (φ : SPred (σ::σs)) s = φ := rfl
theorem pure_intro {φ : Prop} {P : SPred σs} : φ P φ := by
induction σs <;> simp_all [entails, SVal.curry]
induction σs <;> simp_all [entails]
theorem pure_elim' {φ : Prop} {P : SPred σs} : (φ True P) φ P := by
induction σs <;> simp_all [entails, SVal.curry]
induction σs <;> simp_all [entails]
-- Ideally, we'd like to prove the following theorem:
-- theorem pure_elim' {φ : Prop} : SPred.entails (σs:=σs) ⌜True⌝ ⌜φ⌝ → φ
-- Unfortunately, this is only true if all `σs` are Inhabited.
theorem and_pure {P Q : Prop} : P Q (P Q : SPred σs) := by
induction σs
case nil => rfl
case cons σ σs ih => intro s; simp only [and_cons]; exact ih
theorem or_pure {P Q : Prop} : P Q (P Q : SPred σs) := by
induction σs
case nil => rfl
case cons σ σs ih => intro s; simp only [or_cons]; exact ih
theorem imp_pure {P Q : Prop} : (P Q) (P Q : SPred σs) := by
induction σs
case nil => rfl
case cons σ σs ih => intro s; simp only [imp_cons]; exact ih
/-! # Conjunction -/
theorem and_intro {P Q R : SPred σs} (h1 : P Q) (h2 : P R) : P Q R := by

View File

@@ -50,14 +50,8 @@ partial def SPred.Notation.unpack [Monad m] [MonadRef m] [MonadQuotation m] : Te
/-! # Idiom notation -/
/-- Embedding of pure Lean values into `SVal`. -/
/-- Embedding of pure Lean values into `SVal`. An alias for `SPred.pure`. -/
scoped syntax "" term "" : term
/-- t in `SVal` idiom notation. Accesses the state of type `t`. -/
scoped syntax "" term "›ₛ" : term
/--
Use getter `t : SVal σs σ` in `SVal` idiom notation; sugar for `SVal.uncurry t (by assumption)`.
-/
scoped syntax:max "#" term:max : term
/-! # Sugar for `SPred` -/
@@ -69,9 +63,7 @@ scoped syntax:25 "⊢ₛ " term:25 : term
scoped syntax:25 term:25 " ⊣⊢ₛ " term:25 : term
macro_rules
| `($t) => ``(SVal.curry (fun tuple => ULift.up $t))
| `(#$t) => `(SVal.uncurry $t (by assumption))
| `($t) => `(#(SVal.getThe $t))
| `($t) => ``(SPred.pure $t)
| `($P $Q) => ``(SPred.entails spred($P) spred($Q))
| `(spred($P $Q)) => ``(SPred.and spred($P) spred($Q))
| `(spred($P $Q)) => ``(SPred.or spred($P) spred($Q))
@@ -94,20 +86,10 @@ macro_rules
namespace SPred.Notation
@[app_unexpander SVal.curry]
meta def unexpandCurry : Unexpander
@[app_unexpander SPred.pure]
meta def unexpandPure : Unexpander
| `($_ $t $ts*) => do
match t with
| `(fun $_ => { down := $e }) => if ts.isEmpty then ``($e) else ``($e $ts*)
| _ => throw ()
| _ => throw ()
@[app_unexpander SVal.uncurry]
meta def unexpandUncurry : Unexpander
| `($_ $f $ts*) => do
match f with
| `(SVal.getThe $t) => if ts.isEmpty then ``($t) else ``($t $ts*)
| `($t) => if ts.isEmpty then ``(#$t) else ``(#$t $ts*)
if ts.isEmpty then ``($t) else ``($t $ts*)
| _ => throw ()
@[app_unexpander SPred.entails]

View File

@@ -33,9 +33,6 @@ namespace SPred
universe u
variable {σs : List (Type u)}
/-- A pure proposition `P : Prop` embedded into `SPred`. For internal use in this module only; prefer to use idiom bracket notation `⌜P⌝. -/
abbrev pure (P : Prop) : SPred σs := SVal.curry (fun _ => P)
@[ext]
theorem ext_nil {P Q : SPred []} (h : P.down Q.down) : P = Q := by
cases P; cases Q; simp_all
@@ -43,12 +40,24 @@ theorem ext_nil {P Q : SPred []} (h : P.down ↔ Q.down) : P = Q := by
@[ext]
theorem ext_cons {P Q : SPred (σ::σs)} : ( s, P s = Q s) P = Q := funext
/--
A pure proposition `P : Prop` embedded into `SPred`.
Prefer to use idiom bracket notation `⌜P⌝.
-/
def pure {σs : List (Type u)} (P : Prop) : SPred σs := match σs with
| [] => ULift.up P
| _ :: _ => fun _ => pure P
theorem pure_nil : pure (σs:=[]) P = ULift.up P := rfl
theorem pure_cons : pure (σs:=σ::σs) P = fun _ => pure P := rfl
/-- Entailment in `SPred`. -/
def entails {σs : List (Type u)} (P Q : SPred σs) : Prop := match σs with
| [] => P.down Q.down
| σ :: _ => (s : σ), entails (P s) (Q s)
@[simp, grind =] theorem entails_nil {P Q : SPred []} : entails P Q = (P.down Q.down) := rfl
@[grind =] theorem entails_cons {P Q : SPred (σ::σs)} : entails P Q = ( s, entails (P s) (Q s)) := rfl
-- We would like to make `entails_cons` @[simp], but that has no effect until we merge #9015.
-- Until then, we have `entails_<n>` for n ∈ [1:5] in DerivedLaws.lean.
theorem entails_cons {P Q : SPred (σ::σs)} : entails P Q = ( s, entails (P s) (Q s)) := rfl
theorem entails_cons_intro {P Q : SPred (σ::σs)} : ( s, entails (P s) (Q s)) entails P Q := by simp only [entails_cons, imp_self]
-- Reducibility of entails must be semi-reducible so that entails_refl is useful for rfl
@@ -58,7 +67,7 @@ def bientails {σs : List (Type u)} (P Q : SPred σs) : Prop := match σs with
| [] => P.down Q.down
| σ :: _ => (s : σ), bientails (P s) (Q s)
@[simp, grind =] theorem bientails_nil {P Q : SPred []} : bientails P Q = (P.down Q.down) := rfl
@[grind =] theorem bientails_cons {P Q : SPred (σ::σs)} : bientails P Q = ( s, bientails (P s) (Q s)) := rfl
theorem bientails_cons {P Q : SPred (σ::σs)} : bientails P Q = ( s, bientails (P s) (Q s)) := rfl
theorem bientails_cons_intro {P Q : SPred (σ::σs)} : ( s, bientails (P s) (Q s)) bientails P Q := by simp only [bientails_cons, imp_self]
/-- Conjunction in `SPred`. -/
@@ -117,4 +126,4 @@ def conjunction {σs : List (Type u)} (env : List (SPred σs)) : SPred σs := ma
@[simp, grind =] theorem conjunction_nil : conjunction ([] : List (SPred σs)) = pure True := rfl
@[simp, grind =] theorem conjunction_cons {P : SPred σs} {env : List (SPred σs)} : conjunction (P::env) = P.and (conjunction env) := rfl
@[simp, grind =] theorem conjunction_apply {env : List (SPred (σ::σs))} : conjunction env s = conjunction (env.map (· s)) := by
induction env <;> simp [conjunction, *]
induction env <;> simp [conjunction, pure_cons, *]

View File

@@ -24,9 +24,10 @@ namespace Std.Do
abbrev SVal (σs : List (Type u)) (α : Type u) : Type u := match σs with
| [] => α
| σ :: σs => σ SVal σs α
/- Note about the reducibility of SVal:
We need SVal to be reducible, otherwise type inference fails for `Triple`.
(Begs for investigation. #8074.)
This is tracked in #8074. There is a fix in #9015, but it regresses Mathlib.
-/
namespace SVal
@@ -59,22 +60,19 @@ def uncurry {σs : List (Type u)} (f : SVal σs α) : StateTuple σs → α := m
@[simp, grind =] theorem uncurry_curry {σs : List (Type u)} {f : StateTuple σs α} : uncurry (σs:=σs) (curry f) = f := by induction σs <;> (simp[uncurry, curry, *]; rfl)
@[simp, grind =] theorem curry_uncurry {σs : List (Type u)} {f : SVal σs α} : curry (σs:=σs) (uncurry f) = f := by induction σs <;> simp[uncurry, curry, *]
/-- Embed a pure value into an `SVal`. -/
abbrev pure {σs : List (Type u)} (a : α) : SVal σs α := curry (fun _ => a)
instance [Inhabited α] : Inhabited (SVal σs α) where
default := pure default
default := curry fun _ => default
class GetTy (σ : Type u) (σs : List (Type u)) where
get : SVal σs σ
instance : GetTy σ (σ :: σs) where
get := fun s => pure s
get := fun s => curry (fun _ => s)
instance [GetTy σ₁ σs] : GetTy σ₁ (σ₂ :: σs) where
get := fun _ => GetTy.get
/-- Get the top-most state of type `σ` from an `SVal`. -/
def getThe {σs : List (Type u)} (σ : Type u) [GetTy σ σs] : SVal σs σ := GetTy.get
@[simp, grind =] theorem getThe_here {σs : List (Type u)} (σ : Type u) (s : σ) : getThe (σs := σ::σs) σ s = pure s := rfl
@[simp, grind =] theorem getThe_here {σs : List (Type u)} (σ : Type u) (s : σ) : getThe (σs := σ::σs) σ s = curry (fun _ => s) := rfl
@[simp, grind =] theorem getThe_there {σs : List (Type u)} [GetTy σ σs] (σ' : Type u) (s : σ') : getThe (σs := σ'::σs) σ s = getThe (σs := σs) σ := rfl

View File

@@ -60,14 +60,10 @@ theorem bind [Monad m] [WPMonad m ps] {α β : Type u} {P : Assertion ps} {Q :
apply SPred.entails.trans hx
simp only [WP.bind]
apply (wp x).mono _ _
simp only [PostCond.entails, Assertion, FailConds.entails.refl, and_true]
simp only [PostCond.entails, Assertion, ExceptConds.entails.refl, and_true]
exact hf
theorem and [WP m ps] (x : m α) (h₁ : Triple x P₁ Q₁) (h₂ : Triple x P₂ Q₂) : Triple x spred(P₁ P₂) (Q₁ Q₂) :=
(SPred.and_mono h₁ h₂).trans ((wp x).conjunctive Q₁ Q₂).mpr
theorem rewrite_program [WP m ps] {prog₁ prog₂ : m α}
(heq : prog₁ = prog₂) (hprf : Triple prog₂ P Q) :
Triple prog₁ P Q := heq hprf
end Triple

View File

@@ -8,6 +8,7 @@ module
prelude
public import Std.Do.Triple.Basic
public import Std.Do.WP
import Init.Data.Range.Polymorphic
@[expose] public section
@@ -303,22 +304,68 @@ theorem Spec.tryCatch_ExceptT_lift [WP m ps] [Monad m] [MonadExceptOf ε m] (Q :
/-! # `ForIn` -/
/--
The type of loop invariants used by the specifications of `for ... in ...` loops.
A loop invariant is a `PostCond` that takes as parameters
* A `List.Zipper xs` representing the iteration state of the loop. It is parameterized by the list
of elements `xs` that the `for` loop iterates over.
* A state tuple of type `β`, which will be a nesting of `MProd`s representing the elaboration of
`let mut` variables and early return.
The loop specification lemmas will use this in the following way:
Before entering the loop, the zipper's prefix is empty and the suffix is `xs`.
After leaving the loop, the zipper's suffix is empty and the prefix is `xs`.
During the induction step, the invariant holds for a suffix with head element `x`.
After running the loop body, the invariant then holds after shifting `x` to the prefix.
-/
abbrev Invariant {α : Type u} (xs : List α) (β : Type u) (ps : PostShape) :=
PostCond (List.Zipper xs × β) ps
/--
Helper definition for specifying loop invariants for loops with early return.
`for ... in ...` loops with early return of type `γ` elaborate to a call like this:
```lean
forIn (β := MProd (Option γ) ...) (b := ⟨none, ...⟩) collection loopBody
```
Note that the first component of the `MProd` state tuple is the optional early return value.
It is `none` as long as there was no early return and `some r` if the loop returned early with `r`.
This function allows to specify different invariants for the loop body depending on whether the loop
terminated early or not. When there was an early return, the loop has effectively finished, which is
encoded by the additional `⌜xs.suff = []⌝` assertion in the invariant. This assertion is vital for
successfully proving the induction step, as it contradicts with the assumption that
`xs.suff = x::rest` of the inductive hypothesis at the start of the loop body, meaning that users
won't need to prove anything about the bogus case where the loop has returned early yet takes
another iteration of the loop body.
-/
abbrev Invariant.withEarlyReturn
(onContinue : List.Zipper xs β Assertion ps)
(onReturn : γ β Assertion ps)
(onExcept : ExceptConds ps := ExceptConds.false) :
Invariant xs (MProd (Option γ) β) ps :=
fun xs, x, b => spred(
(x = none onContinue xs b)
( r, x = some r xs.suff = [] onReturn r b)),
onExcept
@[spec]
theorem Spec.forIn'_list {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : List α} {init : β} {f : (a : α) a xs β m (ForInStep β)}
(inv : PostCond (β × List.Zipper xs) ps)
(inv : Invariant xs β ps)
(step : b rpref x (hx : x xs) suff (h : xs = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f x hx b
(fun r => match r with
| .yield b' => inv.1 (b', x::rpref, suff, by simp [h])
| .done b' => inv.1 (b', xs.reverse, [], by simp), inv.2)}) :
inv.1 (init, [], xs, by simp)} forIn' xs init f (fun b => inv.1 (b, xs.reverse, [], by simp), inv.2)} := by
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs, by simp, init)} forIn' xs init f (fun b => inv.1 (xs.reverse, [], by simp, b), inv.2)} := by
suffices h : rpref suff (h : xs = rpref.reverse ++ suff),
inv.1 (init, rpref, suff, by simp [h])}
inv.1 (rpref, suff, by simp [h], init)}
forIn' (m:=m) suff init (fun a ha => f a (by simp[h,ha]))
(fun b => inv.1 (b, xs.reverse, [], by simp [h]), inv.2)}
(fun b => inv.1 (xs.reverse, [], by simp [h], b), inv.2)}
from h [] xs rfl
intro rpref suff h
induction suff generalizing rpref init
@@ -347,20 +394,20 @@ theorem Spec.forIn'_list_const_inv {α β : Type u}
f x hx b
(fun r => match r with | .yield b' => inv.1 b' | .done b' => inv.1 b', inv.2)}) :
inv.1 init} forIn' xs init f inv} :=
Spec.forIn'_list (fun p => inv.1 p.1, inv.2) (fun b _ x hx _ _ => step x hx b)
Spec.forIn'_list (fun p => inv.1 p.2, inv.2) (fun b _ x hx _ _ => step x hx b)
@[spec]
theorem Spec.forIn_list {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : List α} {init : β} {f : α β m (ForInStep β)}
(inv : PostCond (β × List.Zipper xs) ps)
(inv : Invariant xs β ps)
(step : b rpref x suff (h : xs = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f x b
(fun r => match r with
| .yield b' => inv.1 (b', x::rpref, suff, by simp [h])
| .done b' => inv.1 (b', xs.reverse, [], by simp), inv.2)}) :
inv.1 (init, [], xs, by simp)} forIn xs init f (fun b => inv.1 (b, xs.reverse, [], by simp), inv.2)} := by
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs, by simp, init)} forIn xs init f (fun b => inv.1 (xs.reverse, [], by simp, b), inv.2)} := by
simp only [ forIn'_eq_forIn]
exact Spec.forIn'_list inv (fun b rpref x _ suff h => step b rpref x suff h)
@@ -374,18 +421,18 @@ theorem Spec.forIn_list_const_inv {α β : Type u}
f hd b
(fun r => match r with | .yield b' => inv.1 b' | .done b' => inv.1 b', inv.2)}) :
inv.1 init} forIn xs init f inv} :=
Spec.forIn_list (fun p => inv.1 p.1, inv.2) (fun b _ hd _ _ => step hd b)
Spec.forIn_list (fun p => inv.1 p.2, inv.2) (fun b _ hd _ _ => step hd b)
@[spec]
theorem Spec.foldlM_list {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : List α} {init : β} {f : β α m β}
(inv : PostCond (β × List.Zipper xs) ps)
(inv : Invariant xs β ps)
(step : b rpref x suff (h : xs = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f b x
(fun b' => inv.1 (b', x::rpref, suff, by simp [h]), inv.2)}) :
inv.1 (init, [], xs, by simp)} List.foldlM f init xs (fun b => inv.1 (b, xs.reverse, [], by simp), inv.2)} := by
(fun b' => inv.1 (x::rpref, suff, by simp [h], b'), inv.2)}) :
inv.1 ([], xs, by simp, init)} List.foldlM f init xs (fun b => inv.1 (xs.reverse, [], by simp, b), inv.2)} := by
have : xs.foldlM f init = forIn xs init (fun a b => .yield <$> f b a) := by
simp only [List.forIn_yield_eq_foldlM, id_map']
rw[this]
@@ -403,20 +450,20 @@ theorem Spec.foldlM_list_const_inv {α β : Type u}
f b hd
(fun b' => inv.1 b', inv.2)}) :
inv.1 init} List.foldlM f init xs inv} :=
Spec.foldlM_list (fun p => inv.1 p.1, inv.2) (fun b _ hd _ _ => step hd b)
Spec.foldlM_list (fun p => inv.1 p.2, inv.2) (fun b _ hd _ _ => step hd b)
@[spec]
theorem Spec.forIn'_range {β : Type} {m : Type Type v} {ps : PostShape}
[Monad m] [WPMonad m ps]
{xs : Std.Range} {init : β} {f : (a : Nat) a xs β m (ForInStep β)}
(inv : PostCond (β × List.Zipper xs.toList) ps)
(inv : Invariant xs.toList β ps)
(step : b rpref x (hx : x xs) suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f x hx b
(fun r => match r with
| .yield b' => inv.1 (b', x::rpref, suff, by simp [h])
| .done b' => inv.1 (b', xs.toList.reverse, [], by simp), inv.2)}) :
inv.1 (init, [], xs.toList, by simp)} forIn' xs init f (fun b => inv.1 (b, xs.toList.reverse, [], by simp), inv.2)} := by
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.toList.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} forIn' xs init f (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
simp only [Std.Range.forIn'_eq_forIn'_range', Std.Range.size, Std.Range.size.eq_1]
apply Spec.forIn'_list inv (fun b rpref x hx suff h => step b rpref x (Std.Range.mem_of_mem_range' hx) suff h)
@@ -424,29 +471,69 @@ theorem Spec.forIn'_range {β : Type} {m : Type → Type v} {ps : PostShape}
theorem Spec.forIn_range {β : Type} {m : Type Type v} {ps : PostShape}
[Monad m] [WPMonad m ps]
{xs : Std.Range} {init : β} {f : Nat β m (ForInStep β)}
(inv : PostCond (β × List.Zipper xs.toList) ps)
(inv : Invariant xs.toList β ps)
(step : b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f x b
(fun r => match r with
| .yield b' => inv.1 (b', x::rpref, suff, by simp [h])
| .done b' => inv.1 (b', xs.toList.reverse, [], by simp), inv.2)}) :
inv.1 (init, [], xs.toList, by simp)} forIn xs init f (fun b => inv.1 (b, xs.toList.reverse, [], by simp), inv.2)} := by
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.toList.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} forIn xs init f (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
simp only [Std.Range.forIn_eq_forIn_range', Std.Range.size]
apply Spec.forIn_list inv step
open Std.PRange in
@[spec]
theorem Spec.forIn'_prange {α β : Type u}
[Monad m] [WPMonad m ps]
[UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
{xs : PRange sl, su α} {init : β} {f : (a : α) a xs β m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : b rpref x (hx : x xs) suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (rpref, x::suff, by simp [h], b)}
f x hx b
(fun r => match r with
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.toList.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} forIn' xs init f (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
simp only [forIn'_eq_forIn'_toList]
apply Spec.forIn'_list inv (fun b rpref x hx suff h => step b rpref x (mem_toList_iff_mem.mp hx) suff h)
open Std.PRange in
@[spec]
theorem Spec.forIn_prange {α β : Type u}
[Monad m] [WPMonad m ps]
[UpwardEnumerable α]
[SupportsUpperBound su α] [SupportsLowerBound sl α] [HasFiniteRanges su α]
[BoundedUpwardEnumerable sl α] [LawfulUpwardEnumerable α]
[LawfulUpwardEnumerableLowerBound sl α] [LawfulUpwardEnumerableUpperBound su α]
{xs : PRange sl, su α} {init : β} {f : α β m (ForInStep β)}
(inv : Invariant xs.toList β ps)
(step : b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (rpref, x::suff, by simp [h], b)}
f x b
(fun r => match r with
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.toList.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} forIn xs init f (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
simp only [forIn]
apply Spec.forIn'_prange inv (fun b rpref x _hx suff h => step b rpref x suff h)
@[spec]
theorem Spec.forIn'_array {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : Array α} {init : β} {f : (a : α) a xs β m (ForInStep β)}
(inv : PostCond (β × List.Zipper xs.toList) ps)
(inv : Invariant xs.toList β ps)
(step : b rpref x (hx : x xs) suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f x hx b
(fun r => match r with
| .yield b' => inv.1 (b', x::rpref, suff, by simp [h])
| .done b' => inv.1 (b', xs.toList.reverse, [], by simp), inv.2)}) :
inv.1 (init, [], xs.toList, by simp)} forIn' xs init f (fun b => inv.1 (b, xs.toList.reverse, [], by simp), inv.2)} := by
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.toList.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} forIn' xs init f (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
cases xs
simp
apply Spec.forIn'_list inv (fun b rpref x hx suff h => step b rpref x (by simp[hx]) suff h)
@@ -455,14 +542,14 @@ theorem Spec.forIn'_array {α β : Type u}
theorem Spec.forIn_array {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : Array α} {init : β} {f : α β m (ForInStep β)}
(inv : PostCond (β × List.Zipper xs.toList) ps)
(inv : Invariant xs.toList β ps)
(step : b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f x b
(fun r => match r with
| .yield b' => inv.1 (b', x::rpref, suff, by simp [h])
| .done b' => inv.1 (b', xs.toList.reverse, [], by simp), inv.2)}) :
inv.1 (init, [], xs.toList, by simp)} forIn xs init f (fun b => inv.1 (b, xs.toList.reverse, [], by simp), inv.2)} := by
| .yield b' => inv.1 (x::rpref, suff, by simp [h], b')
| .done b' => inv.1 (xs.toList.reverse, [], by simp, b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} forIn xs init f (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
cases xs
simp
apply Spec.forIn_list inv step
@@ -471,12 +558,12 @@ theorem Spec.forIn_array {α β : Type u}
theorem Spec.foldlM_array {α β : Type u}
[Monad m] [WPMonad m ps]
{xs : Array α} {init : β} {f : β α m β}
(inv : PostCond (β × List.Zipper xs.toList) ps)
(inv : Invariant xs.toList β ps)
(step : b rpref x suff (h : xs.toList = rpref.reverse ++ x :: suff),
inv.1 (b, rpref, x::suff, by simp [h])}
inv.1 (rpref, x::suff, by simp [h], b)}
f b x
(fun b' => inv.1 (b', x::rpref, suff, by simp [h]), inv.2)}) :
inv.1 (init, [], xs.toList, by simp)} Array.foldlM f init xs (fun b => inv.1 (b, xs.toList.reverse, [], by simp), inv.2)} := by
(fun b' => inv.1 (x::rpref, suff, by simp [h], b'), inv.2)}) :
inv.1 ([], xs.toList, by simp, init)} Array.foldlM f init xs (fun b => inv.1 (xs.toList.reverse, [], by simp, b), inv.2)} := by
cases xs
simp
apply Spec.foldlM_list inv step

View File

@@ -98,13 +98,13 @@ instance Except.instWP : WP (Except ε) (.except ε .pure) :=
inferInstanceAs (WP (ExceptT ε Id) (.except ε .pure))
theorem Id.by_wp {α : Type u} {x : α} {prog : Id α} (h : Id.run prog = x) (P : α Prop) :
( wpprog (PostCond.total (fun a => P a))) P x := h (· True.intro)
( wpprog (PostCond.noThrow (fun a => P a))) P x := h (· True.intro)
theorem StateM.by_wp {α} {x : α × σ} {prog : StateM σ α} (h : StateT.run prog s = x) (P : α × σ Prop) :
( wpprog (PostCond.total (fun a s' => P (a, s'))) s) P x := h (· True.intro)
( wpprog (PostCond.noThrow (fun a s' => P (a, s'))) s) P x := h (· True.intro)
theorem ReaderM.by_wp {α} {x : α} {prog : ReaderM ρ α} (h : ReaderT.run prog r = x) (P : α Prop) :
( wpprog (PostCond.total (fun a _ => P a)) r) P x := h (· True.intro)
( wpprog (PostCond.noThrow (fun a _ => P a)) r) P x := h (· True.intro)
theorem Except.by_wp {α} {x : Except ε α} (P : Except ε α Prop) :
( wpx postfun a => P (.ok a), fun e => P (.error e)) P x := by

View File

@@ -7,6 +7,7 @@ module
prelude
public import Init.Data.List.Nat.Basic
public import Init.Data.Nat.Order
public import Std.Sat.CNF.Relabel
@[expose] public section
@@ -24,7 +25,7 @@ def Clause.maxLiteral (c : Clause Nat) : Option Nat := (c.map (·.1)) |>.max?
theorem Clause.of_maxLiteral_eq_some (c : Clause Nat) (h : c.maxLiteral = some maxLit) :
lit, Mem lit c lit maxLit := by
intro lit hlit
simp only [maxLiteral, List.max?_eq_some_iff', List.mem_map, forall_exists_index, and_imp,
simp only [maxLiteral, List.max?_eq_some_iff, List.mem_map, forall_exists_index, and_imp,
forall_apply_eq_imp_iff₂] at h
simp only [Mem] at hlit
rcases h with _, hbar
@@ -57,7 +58,7 @@ def maxLiteral (f : CNF Nat) : Option Nat :=
theorem of_maxLiteral_eq_some' (f : CNF Nat) (h : f.maxLiteral = some maxLit) :
clause, clause f clause.maxLiteral = some localMax localMax maxLit := by
intro clause hclause1 hclause2
simp [maxLiteral, List.max?_eq_some_iff'] at h
simp [maxLiteral, List.max?_eq_some_iff] at h
rcases h with _, hclause3
apply hclause3 localMax clause hclause1 hclause2

View File

@@ -126,7 +126,14 @@ syntax (name := mstop) "mstop" : tactic
@[inherit_doc Lean.Parser.Tactic.mleaveMacro]
macro (name := mleave) "mleave" : tactic =>
`(tactic| (try simp only [
$(mkIdent ``Std.Do.SPred.entails_cons):term,
$(mkIdent ``Std.Do.SPred.down_pure):term,
$(mkIdent ``Std.Do.SPred.apply_pure):term,
-- $(mkIdent ``Std.Do.SPred.entails_cons):term, -- Ineffective until #9015 lands
$(mkIdent ``Std.Do.SPred.entails_1):term,
$(mkIdent ``Std.Do.SPred.entails_2):term,
$(mkIdent ``Std.Do.SPred.entails_3):term,
$(mkIdent ``Std.Do.SPred.entails_4):term,
$(mkIdent ``Std.Do.SPred.entails_5):term,
$(mkIdent ``Std.Do.SPred.entails_nil):term,
$(mkIdent ``Std.Do.SPred.and_cons):term,
$(mkIdent ``Std.Do.SPred.and_nil):term,
@@ -148,11 +155,12 @@ macro (name := mleave) "mleave" : tactic =>
$(mkIdent ``Std.Do.SVal.uncurry_nil):term,
$(mkIdent ``Std.Do.SVal.getThe_here):term,
$(mkIdent ``Std.Do.SVal.getThe_there):term,
$(mkIdent ``Std.Do.FailConds.entails.refl):term,
$(mkIdent ``Std.Do.FailConds.entails_true):term,
$(mkIdent ``Std.Do.FailConds.entails_false):term,
$(mkIdent ``Std.Do.ExceptConds.entails.refl):term,
$(mkIdent ``Std.Do.ExceptConds.entails_true):term,
$(mkIdent ``Std.Do.ExceptConds.entails_false):term,
$(mkIdent ``ULift.down_ite):term,
$(mkIdent ``ULift.down_dite):term,
$(mkIdent ``Std.List.Zipper.pref):term,
$(mkIdent ``and_imp):term,
$(mkIdent ``and_true):term,
$(mkIdent ``dite_eq_ite):term,
@@ -283,7 +291,7 @@ Like `mspec`, but does not attempt slight simplification and closing of trivial
```
mspec_no_simp $spec
all_goals
((try simp only [SPred.true_intro_simp, SVal.curry_cons, SVal.uncurry_nil, SVal.uncurry_cons, SVal.getThe_here, SVal.getThe_there]);
((try simp only [SPred.true_intro_simp, SPred.apply_pure]);
(try mpure_intro; trivial))
```
-/
@@ -310,11 +318,7 @@ macro (name := mspec) "mspec" spec:(ppSpace colGt term)? : tactic =>
`(tactic| (mspec_no_simp $[$spec]?
all_goals ((try simp only [
$(mkIdent ``Std.Do.SPred.true_intro_simp):term,
$(mkIdent ``Std.Do.SVal.curry_cons):term,
$(mkIdent ``Std.Do.SVal.uncurry_nil):term,
$(mkIdent ``Std.Do.SVal.uncurry_cons):term,
$(mkIdent ``Std.Do.SVal.getThe_here):term,
$(mkIdent ``Std.Do.SVal.getThe_there):term])
$(mkIdent ``Std.Do.SPred.apply_pure):term])
(try mpure_intro; trivial))))
@[inherit_doc Lean.Parser.Tactic.mvcgenMacro]

View File

@@ -129,10 +129,10 @@ in SQL databases to represent dates.
def sqlDate : GenericFormat .any := datespec("uuuu-MM-dd")
/--
The LongDateFormat, which follows the pattern `EEEE, MMMM D, uuuu HH:mm:ss` for
The LongDateFormat, which follows the pattern `EEEE, MMMM d, uuuu HH:mm:ss` for
representing a full date and time with the day of the week and month name.
-/
def longDateFormat : GenericFormat (.only .GMT) := datespec("EEEE, MMMM D, uuuu HH:mm:ss")
def longDateFormat : GenericFormat (.only .GMT) := datespec("EEEE, MMMM d, uuuu HH:mm:ss")
/--
The AscTime format, which follows the pattern `EEE MMM d HH:mm:ss uuuu`. This format

View File

@@ -126,6 +126,7 @@ abbrev InputFileDecl := KConfigDecl InputFile.configKind
/-- A inpurt directory declaration from a configuration written in Lean. -/
abbrev InputDirDecl := KConfigDecl InputDir.configKind
deriving instance TypeName for
LeanLibDecl, LeanExeDecl,
InputFileDecl, InputDirDecl
instance : TypeName LeanLibDecl := unsafe (.mk _ ``LeanLibDecl)
instance : TypeName LeanExeDecl := unsafe (.mk _ ``LeanExeDecl)
instance : TypeName InputFileDecl := unsafe (.mk _ ``InputFileDecl)
instance : TypeName InputDirDecl := unsafe (.mk _ ``InputDirDecl)

View File

@@ -87,8 +87,9 @@ abbrev LibraryFacetConfig := KFacetConfig LeanLib.facetKind
/-- A library facet declaration from a configuration file. -/
abbrev LibraryFacetDecl := NamedConfigDecl LibraryFacetConfig
deriving instance TypeName for
ModuleFacetDecl, PackageFacetDecl, LibraryFacetDecl
instance : TypeName ModuleFacetDecl := unsafe (.mk _ ``ModuleFacetDecl)
instance : TypeName PackageFacetDecl := unsafe (.mk _ ``PackageFacetDecl)
instance : TypeName LibraryFacetDecl := unsafe (.mk _ ``LibraryFacetDecl)
/-- A library facet's declarative configuration. -/
abbrev LeanLibFacetConfig := LibraryFacetConfig

View File

@@ -22,7 +22,7 @@ also equipped with information about the Lake configuration.
-/
abbrev ScriptFn := (args : List String) ScriptM ExitCode
deriving instance TypeName for ScriptFn
instance : TypeName ScriptFn := unsafe (.mk _ ``ScriptFn)
/--
A package `Script` is a `ScriptFn` definition that is

View File

@@ -347,6 +347,12 @@ instance : DecodeToml Dependency := ⟨fun v => do Dependency.decodeToml (← v.
/-! ## Package & Target Configuration Decoders -/
section
-- We automatically disable the following option for `macro`s but the subsequent `def` both contains
-- a quotation and is called only by `macro`s, so we disable the option for it manually. Note that
-- we can't use `in` as it is parsed as a single command and so the option would not influence the
-- parser.
set_option internal.parseQuotWithCurrentStage false
private def genDecodeToml
(cmds : Array Command)
(tyName : Name) [info : ConfigInfo tyName] (takesName : Bool)
@@ -366,6 +372,7 @@ private def genDecodeToml
let instId mkIdentFromRef <| `_root_ ++ tyName.str "instDecodeToml"
let cmds cmds.push <$> `(instance $instId:ident : DecodeToml $ty := decodeTableValue $decId)
return cmds
end
local macro "gen_toml_decoders%" : command => do
let cmds := #[]

Some files were not shown because too many files have changed in this diff Show More