Compare commits

...

593 Commits

Author SHA1 Message Date
Kim Morrison
66400f8e77 oops 2025-06-29 21:24:30 +10:00
Kim Morrison
e5315e0521 namespaces 2025-06-29 21:15:45 +10:00
Kim Morrison
2472bdbaf2 help theorem for Std.ReflCmp 2025-06-29 21:01:52 +10:00
Kim Morrison
732f55ec38 feat: support for ReflCmp in grind 2025-06-29 15:59:28 +10:00
Leonardo de Moura
f2e06ead54 feat: support for LawfulEqCmp in grind (#9069)
This PR implements support for the type class `LawfulEqCmp`. Examples:
```lean
example (a b c : Vector (List Nat) n)
    : b = c → a.compareLex (List.compareLex compare) b = o → o = .eq → a = c := by
  grind

example [Ord α] [Std.LawfulEqCmp (compare : α → α → Ordering)] (a b c : Array (Vector (List α) n))
    : b = c → o = .eq → a.compareLex (Vector.compareLex (List.compareLex compare)) b = o → a = c := by
  grind
```
2025-06-28 22:41:22 +00:00
Leonardo de Moura
f6bb524406 doc: grind docstring (#9067)
This PR adds a docstring for the `grind` tactic.
2025-06-28 20:20:55 +00:00
Cameron Zwarich
ef77322133 chore: fix a typo in an error message (#9066) 2025-06-28 19:48:50 +00:00
Leonardo de Moura
4247dcfea6 feat: improve counterexamples using ToInt.toInt in grind cutsat (#9065)
This PR improves the counterexamples produced by the `cutsat` procedure
in `grind` when using the `ToInt` gadget.
2025-06-28 19:30:25 +00:00
Cameron Zwarich
05978caa59 chore: move type lowering functions to CoreM (#9064) 2025-06-28 18:10:42 +00:00
Sebastian Ullrich
09a5b34931 feat: make private the default in module (#9044)
This PR adjusts the experimental module system to make `private` the
default visibility modifier in `module`s, introducing `public` as a new
modifier instead. `public section` can be used to revert the default for
an entire section, though this is more intended to ease gradual adoption
of the new semantics such as in `Init` (and soon `Std`) where they
should be replaced by a future decl-by-decl re-review of visibilities.
2025-06-28 16:30:53 +00:00
Cameron Zwarich
5144a3bf74 chore: rename lowerEnumToScalarType to lowerEnumToScalarType? (#9063) 2025-06-28 15:52:11 +00:00
Leonardo de Moura
5ca6eadd50 feat: equations <num> = 0 in grind ring (#9062)
This PR implements support for equations `<num> = 0` in rings and fields
of unknown characteristic. Examples:
```lean
example [Field α] (a : α) : (2 * a)⁻¹ = a⁻¹ / 2 := by grind

example [Field α] (a : α) : (2 : α) ≠ 0 → 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind

example [CommRing α] (a b : α) (h₁ : a + 2 = a) (h₂ : 2*b + a = 0) : a = 0 := by
  grind

example [CommRing α] (a b : α) (h₁ : a + 6 = a) (h₂ : b + 9 = b) (h₂ : 3*b + a = 0) : a = 0 := by
  grind

example [CommRing α] (a b : α) (h₁ : a + 6 = a) (h₂ : b + 9 = b) (h₂ : 3*b + a = 0) : a = 0 := by
  grind

example [CommRing α] (a b : α) (h₁ : a + 2 = a) (h₂ : b = 0) : 4*a + b = 0 := by
  grind

example [CommRing α] (a b c : α) (h₁ : a + 6 = a) (h₂ : c = c + 9) (h : b + 3*c = 0) : 27*a + b = 0 := by
  grind

```
2025-06-28 14:28:42 +00:00
Sebastian Ullrich
22152b8bfb chore: update stage0 2025-06-28 15:58:14 +02:00
Sebastian Ullrich
d7e35c77ca chore: reserve public import syntax (#9061)
This PR adds the `public import` syntax to be used by the experimental
module system
2025-06-28 13:11:34 +00:00
Leonardo de Moura
e844f9c82c feat: helper theorems for grind ring (#9059)
This PR adds helper theorems for normalizing coefficients in rings of
unknown characteristic.
2025-06-28 10:57:44 +00:00
Sebastian Ullrich
4f2d107b52 chore: update stage0 2025-06-28 12:09:50 +02:00
Paul Reichert
e86e978f26 feat: ToStream instance for ranges (#9058)
This PR provides a `ToStream` instance for slices so that they can be
used in `for i in xs, j in ys do` notation.
2025-06-28 09:38:37 +00:00
Paul Reichert
d380919fa3 feat: lemmas about toList, toListRev and toArray for slices (#9049)
This PR proves that the default `toList`, `toListRev` and `toArray`
functions on slices can be described in terms of the slice iterator.
Relying on new lemmas for the `uLift` and `attachWith` iterator
combinators, a more concrete description of said functions is given for
`Subarray`.
2025-06-28 08:29:09 +00:00
Leonardo de Moura
98b66ec373 feat: variable reordering heuristic for grind cutsat (#9057)
This PR introduces a simple variable-reordering heuristic for `cutsat`.
It is needed by the `ToInt` adapter to support finite types such as
`UInt64`. The current encoding into `Int` produces large coefficients,
which can enlarge the search space when an unfavorable variable order is
used. Example:
```lean
example (a b c : UInt64) : a ≤ 2 → b ≤ 3 → c - a - b = 0 → c ≤ 5 := by
  grind
```
2025-06-28 08:12:43 +00:00
Kim Morrison
291938c748 chore: generalize Array/Vector.extract_push (#9055)
This PR renames `Array/Vector.extract_push` to `extract_push_of_le`, and
replaces the lemma with one without a side condition.
2025-06-28 07:07:57 +00:00
Kim Morrison
c52605dfe3 fix: some inconsistencies in Map grind annotations (#9054)
This PR corrects some inconsistencies in `TreeMap`/`HashMap` grind
annotations, for `isSome_get?_eq_contains` and `empty_eq_emptyc`.
2025-06-28 06:41:19 +00:00
Leonardo de Moura
19fd1f060f feat: ToInt equality in grind cutsat (#9051)
This PR implements support for equalities and disequalities in `grind
cutsat`. We still have to improve the encoding. Examples:
```lean
example (a b c : Fin 11) : a ≤ 2 → b ≤ 3 → c = a + b → c ≤ 5 := by
  grind

example (a : Fin 2) : a ≠ 0 → a ≠ 1 → False := by
  grind
```
2025-06-27 21:52:23 +00:00
jrr6
a0e425748a chore: enable error explanation widget (#9043)
This PR enables the error explanation widget in named error messages.
Note that the displayed links won't work until the new manual version is
released (unless overriding `LEAN_MANUAL_ROOT` with a suitably recent
manual build).
2025-06-27 19:25:29 +00:00
Paul Reichert
6e538c35dd refactor: migrate all usages of old slice notation (#9000)
This PR replaces all usages of `[:]` slice notation in `src` with the
new `[...]` notation in production code, tests and comments. The
underlying implementation of the `Subarray` functions stays the same.

Notation cheat sheet:

* `*...*` is the doubly-unbounded range.
* `*...a` or `*...<a` contains all elements that are less than `a`.
* `*...=a` contains all elements that are less than or equal to `a`.
* `a...*` contains all elements that are greater than or equal to `a`.
* `a...b` or `a...<b` contains all elements that are greater than or
equal to `a` and less than `b`.
* `a...=b` contains all elements that are greater than or equal to `a`
and less than or equal to `b`.
* `a<...*` contains all elements that are greater than `a`.
* `a<...b` or `a<...<b` contains all elements that are greater than `a`
and less than `b`.
* `a<...=b` contains all elements that are greater than `a` and less
than or equal to `b`.

Benchmarks have shown that importing the iterator-backed parts of the
polymorphic slice library in `Init` impacts build performance. This PR
avoids this problem by separating those parts of the library that do not
rely on iterators from those those that do. Whereever the new slice
notation is used, only the iterator-independent files are imported.
2025-06-27 18:52:07 +00:00
Leonardo de Moura
422eb68f6f feat: assert ToInt bounds in grind cutsat (#9050)
This PR ensures the `ToInt` bounds are asserted for every `toInt a`
application internalized in `grind cutsat`.
2025-06-27 18:42:35 +00:00
Leonardo de Moura
7f5b47e831 feat: ToInt strict inequalities in grind cutsat (#9048)
This PR implements support for strict inequalities in the `ToInt`
adapter used in `grind cutsat`. Example:
```lean
example (a b c : Fin 11) : c ≤ 9 → a ≤ b → b < c → a < c + 1 := by
  grind
```
2025-06-27 17:34:12 +00:00
Sofia Rodrigues
bf2e91b6d1 feat: add system information functions to the standard library (#8109)
This PR adds system information functions to the standard library
2025-06-27 16:31:34 +00:00
Sebastian Graf
e886373dc8 fix: Avoid a type error in mvcgen and turn fewer natural goals into synthetic opaque ones (#9045)
This PR fixes a type error in `mvcgen` and makes it turn fewer natural
goals into synthetic opaque ones, so that tactics such as `trivial` may
instantiate them more easily.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-27 16:27:06 +00:00
Sebastian Ullrich
7ed1a4b576 perf: inline lean_inc_ref_cold (#4978)
The body is a single instruction
2025-06-27 15:58:00 +00:00
jrr6
4759506bcf chore: use note and hint' for message addenda (#8980)
This PR improves the consistency of error message formatting by
rendering addenda of several existing error messages as labeled notes
and hints.
2025-06-27 15:16:01 +00:00
Sebastian Ullrich
f5c389468f chore: update stage0 2025-06-27 08:13:31 -07:00
Sebastian Ullrich
aadc74bee2 perf: do not import non-meta IR 2025-06-27 08:13:31 -07:00
Sebastian Ullrich
bd16c0f87d chore: update stage0 2025-06-27 16:55:38 +02:00
Sebastian Graf
862a3dc552 fix: Use fullApproxDefEq in mspec to fix a bug reported by Rish (#9041)
This PR makes `mspec` detect more viable assignments by `rfl` instead of
generating a VC.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
Co-authored-by: Rishikesh Vaishnav <rishhvaishnav@gmail.com>
2025-06-27 14:31:39 +00:00
Markus Himmel
c3319f21ee chore: Grove: start on associative containers (#9039) 2025-06-27 13:34:10 +00:00
David Thrane Christiansen
2bb27af0d4 chore: automatically create reference manual PR branches (#9033)
This PR adds a Mathlib-like testing and feedback system for the
reference manual. Lean PRs will receive comments that reflect the status
of the language reference with respect to the PR.
2025-06-27 13:23:41 +00:00
Sebastian Graf
c79b89fb39 fix: More fixes for Std.Do accumulated while merging tests (#9038)
This PR adds test cases for the VC generator and implements a few small
and tedious fixes to ensure they pass.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-27 13:10:43 +00:00
Henrik Böving
7a1113ada3 feat: extend identifiers to french (#9035)
This PR extends the list of acceptable characters to all the french ones
as well as some others,
by adding characters from the Latin-1-Supplement add Latin-Extended-A
unicode block.
2025-06-27 12:50:09 +00:00
Sebastian Ullrich
7845af3105 chore: reserve public section syntax (#9032)
To be used in the experimental module system
2025-06-27 12:21:01 +00:00
Sebastian Ullrich
35c168cb13 feat: allow access to private names through import all (#8828)
This PR extends the experimental module system to support resolving
private names imported (transitively) through `import all`.
2025-06-27 12:13:46 +00:00
Sofia Rodrigues
fe1b407031 feat: add DNS resolution functions to the standard library (#8072)
This PR adds DNS functions to the standard library

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-06-27 11:11:47 +00:00
Sofia Rodrigues
0f2cb91336 feat: add lean_setup_libuv for initializing required LIBUV components (#8636)
This PR adds a function called `lean_setup_libuv` that initializes
required LIBUV components. It needs to be outside of
`lean_initialize_runtime_module` because it requires `argv` and `argc`
to work correctly.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
2025-06-27 11:11:17 +00:00
Sebastian Graf
08737054fc fix: A couple of bootstrapping hiccups related to Std.Do (#9030)
This PR fixes a couple of bootstrapping-related hiccups in the newly
added `Std.Do` module. More precisely,

* The `spec` attribute syntax was registered under the wrong name and
its implementation needed to use a different priority parser
* Elaborators and delaborators for `MGoal`, `Triple`, `PostCond` and
`PostCond.total` were broken and are now properly builtin
* `Std.Do` should not transitively import `Std.Tactic.Do.Syntax`

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-27 09:53:17 +00:00
Henrik Böving
56d3de5358 fix: bv_decide internal error (#9031)
This PR fixes a minor usability issue in bv_decide.
2025-06-27 09:10:21 +00:00
Marc Huisinga
1edb7632b5 fix: highlight keywords when keyword is actual identifier (#9019)
This PR fixes a bug where semantic highlighting would only highlight
keywords that started with an alphanumeric character. Now, it uses
`Lean.isIdFirst`.
2025-06-27 08:23:10 +00:00
Paul Reichert
1a6eae16ec feat: introduce uLift iterator combinator, make Subarray.iter universe-polymorphic (#9027)
This PR provides an iterator combinator that lifts the emitted values
into a higher universe level via `ULift`. This combinator is then used
to make the subarray iterators universe-polymorphic. Previously, they
were only available for `Subarray α` if `α : Type`.
2025-06-27 07:34:08 +00:00
Kim Morrison
8d40cf5157 chore: missing Option lemma (#9028) 2025-06-27 07:28:59 +00:00
Leonardo de Moura
0aca10b228 feat: Toint inequalities in cutsat (#9026)
This PR implements support for (non strict) `ToInt` inequalities in
`grind cutsat`. `grind cutsat` can solve simple problems such as:
```lean
example (a b c : Fin 11) : a ≤ b → b ≤ c → a ≤ c := by
  grind

example (a b c : Fin 11) : c ≤ 9 → a ≤ b → b ≤ c → a ≤ c + 1 := by
  grind

example (a b c : UInt8) : a ≤ b → b ≤ c → a ≤ c := by
  grind

example (a b c d : UInt32) : a ≤ b → b ≤ c → c ≤ d → a ≤ d := by
  grind
```
Next step: strict inequalities, and equalities.
2025-06-27 06:29:31 +00:00
Mac Malone
541ff1e287 feat: lake: local artifact cache (#8922)
This PR introduces a local artifact cache for Lake. When enabled, Lake
will shared build artifacts (built files) across different instances of
the same package using an input- and content-addressed cache.

To enable support for the local cache, packages must set
`enableArtifactCache := true` in their package configuration. The reason
for this is twofold. This feature is new and experimental, so it should
be opt-in. Also, some packages may need to disable it as the cache
entails that artifacts are no longer necessarily available within the
build directory, which can break custom build scripts.

The cache location is determined by the system configuration. Lake's
first preference is to store it under the Lean toolchain in a
`lake/cache` directory. If Elan is not available, Lake will store it in
common system location (e.g., `$XDG_CACHE_HOME/lake`, or
`~/.cache/lake`). On an exotic system where neither of these exist, the
cache will be disabled. Users can override this location through the
`LAKE_CACHE_DIR` environment variable. If set to empty, caching will be
disabled.

The cache is both input and content-addressed. Mappings from input hash
to output content hash(es) are stored in a per-package JSON Lines file
(e.g., `<cache-dir>/inputs/<pkg-name>.jsonl`). Thus, mappings are shared
across different instances of a package, but not between packages. The
output content hashes are also now stored in trace files in a new
`outputs` field. The value of this field can be either a single hash or
an object of multiple content hashes for targets which produce multiple
artifacts (e.g., Lean module builds). Separately, artifacts are stored
in a single flat content-addressed cache (e.g.,
`<cache-dir>/artifacts/<hash>.art`. Artifacts are therefore shared
across all cache-enabled packages.

Module `*.olean` and and `*.ilean` artifacts are cached. However, each
package will still copy the files to their build directory, as Lean and
the server currently expect them to be at a specific path. This will be
changed for `*.olean` files when the performance issues with
pre-resolving modules in Lake for `lean --setup` are solved.
2025-06-27 04:06:50 +00:00
Leonardo de Moura
0371509e49 refactor: remove foreignTypes leftover from cutsat (#9024)
We will not use it with the new `ToInt` infrastructure.
2025-06-27 02:47:34 +00:00
Kyle Miller
7abc9106d7 feat: optimized simp routine for let telescopes (#8968)
This PR adds the following features to `simp`:
- A routine for simplifying `have` telescopes in a way that avoids
quadratic complexity arising from locally nameless expression
representations, like what #6220 did for `letFun` telescopes.
Furthermore, simp converts `letFun`s into `have`s (nondependent lets),
and we remove the #6220 routine since we are moving away from `letFun`
encodings of nondependent lets.
- A `+letToHave` configuration option (enabled by default) that converts
lets into haves when possible, when `-zeta` is set. Previously Lean
would need to do a full typecheck of the bodies of `let`s, but the
`letToHave` procedure can skip checking some subexpressions, and it
modifies the `let`s in an entire expression at once rather than one at a
time.
- A `+zetaHave` configuration option, to turn off zeta reduction of
`have`s specifically. The motivation is that dependent `let`s can only
be dsimped by let, so zeta reducing just the dependent lets is a
reasonable way to make progress. The `+zetaHave` option is also added to
the meta configuration.
- When `simp` is zeta reducing, it now uses an algorithm that avoids
complexity quadratic in the depth of the let telescope.
- Additionally, the zeta reduction routines in `simp`, `whnf`, and
`isDefEq` now all are consistent with how they apply the `zeta`,
`zetaHave`, and `zetaUnused` configurations.

The `letToFun` option is addressing a TODO in `getSimpLetCase` ("handle
a block of nested let decls in a single pass if this becomes a
performance problem").

Performance should be compared to before #8804, which temporarily
disabled the #6220 optimizations for `letFun` telescopes.

Good kernel performance depends on carefully handling the `have`
encoding. Due to the way the kernel instantiates bvars (it does *not*
beta reduce when instantiating), we cannot use congruence theorems of
the form `(have x := v; f x) = (have x ;= v'; f' x)`, since the bodies
of the `have`s will not be syntactically equal, which triggers zeta
reduction in the kernel in `is_def_eq`. Instead, we work with `f v = f'
v'`, where `f` and `f'` are lambda expressions. There is still zeta
reduction, but only when converting between these two forms at the
outset of the generated proof.
2025-06-27 02:13:20 +00:00
jrr6
05948f19e4 fix: improve precision of synthesis failure spans in interpolated strings (#9004)
This PR ensures that type-class synthesis failure errors in interpolated
strings are displayed at the interpolant at which they occurred.
2025-06-27 01:47:32 +00:00
Leonardo de Moura
6b520ede08 feat: generic toInt for cutsat (#9022)
This PR completes the generic `toInt` infrastructure for embedding terms
implementing the `ToInt` type classes into `Int`.
2025-06-27 00:28:51 +00:00
jrr6
2fe6d8a70b feat: add word-level hint suggestion diffs (#8574)
This PR adds an additional diff mode to the error-message hint
suggestion widget that displays diffs per word rather than per
character.
2025-06-26 23:56:19 +00:00
Luisa Cicolini
b1a306cf69 feat: add BitVec.toFin_(sdiv, smod, srem) and BitVec.toNat_srem (#8950)
This PR adds `BitVec.toFin_(sdiv, smod, srem)` and `BitVec.toNat_srem`.
The strategy for the `rhs` of the `toFin_*` lemmas is to consider what
the corresponding `toNat_*` theorems do and push the `toFin` closerto
the operands. For the `rhs` of `BitVec.toNat_srem` I used the same
strategy as `BitVec.toNat_smod`.
2025-06-26 20:01:01 +00:00
Kyle Miller
b56ad5a7d2 fix: apply newlines before and after comments when formatting syntax (#8626)
This PR closes #3791, making sure that the Syntax formatter inserts
whitespace before and after comments in the leading and trailing text of
Syntax to avoid having comments comment out any following syntax, and to
avoid comments' lexical syntax from being interpreted as being part of
another syntax. If the text contains newlines before or after any
comments, they are formatted as hard newlines rather than soft newlines.
For example, `--` comments will have a hard newline after them. Note:
metaprograms generating Syntax with comments should be sure to include
newlines at the ends of `--` comments.
2025-06-26 19:23:35 +00:00
jrr6
7ed716f904 feat: improve projection and field-notation errors (#8986)
This PR improves the error messages produced by invalid projections and
field notation. It also adds a hint to the "function expected" error
message noting the argument to which the term is being applied, which
can be helpful for debugging spurious "function expected" messages
actually caused by syntax errors.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-06-26 18:36:47 +00:00
Lean stage0 autoupdater
928d37e4d4 chore: update stage0 2025-06-26 18:04:18 +00:00
Sebastian Graf
f87d05ad4e feat: Hoare logic for monadic programs and verification condition generation (#8995)
This PR introduces a Hoare logic for monadic programs in
`Std.Do.Triple`, and assorted tactics:

*  `mspec` for applying Hoare triple specifications
* `mvcgen` to turn a Hoare triple proof obligation `⦃P⦄ prog ⦃Q⦄` into
pure verification conditoins (i.e., without any traces of Hoare triples
or weakest preconditions reminiscent of `prog`). The resulting
verification conditions in the stateful logic of `Std.Do.SPred` can be
discharged manually with the tactics coming with its custom proof mode
or with automation such as `simp` and `grind`.

This is pre-release of a planned feature and not yet intended for
production use. We are grateful for feedback of early adopters, though.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-26 15:49:56 +00:00
Paul Reichert
83e226204d feat: introduce slices (#8947)
This PR introduces polymorphic slices in their most basic form. They
come with a notation similar to the new range notation. `Subarray` is
now also a slice and can produce an iterator now. It is intended to
migrate more operations of `Subarray` to the `Slice` wrapper type to
make them available for slices of other types, too.

The PR also moves the `filterMap` combinators into `Init` because they
are used internally to implement iterators on array slices.
2025-06-26 15:29:03 +00:00
Rob23oba
9bf5fc2fd3 feat: extensional tree maps (#8721)
This PR adds the types `Std.ExtDTreeMap`, `Std.ExtTreeMap` and
`Std.ExtTreeSet` of extensional tree maps and sets. These are very
similar in construction to the existing extensional hash maps with one
exception: extensional tree maps and sets provide all functions from
regular tree maps and sets. This is possible because in contrast to hash
maps, tree maps are always ordered.
2025-06-26 13:13:45 +00:00
Markus Himmel
2f43f02cb6 chore: Grove: high-level sections (#9011) 2025-06-26 13:06:56 +00:00
Markus Himmel
65ea45b17b chore: ci: fixes to Grove workflow (#9014) 2025-06-26 12:15:51 +00:00
Sebastian Graf
0d7fe9a196 feat: Upstream MPL.SPred.* from mpl (#8928)
This PR adds a logic of stateful predicates SPred to Std.Do in order to
support reasoning about monadic programs. It comes with a dedicated
proof mode the tactics of which are accessible by importing
Std.Tactic.Do.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-26 11:15:11 +00:00
Markus Himmel
790ae27f2b chore: ci: fixes to Grove workflow (#9013) 2025-06-26 11:13:19 +00:00
Markus Himmel
40d2c99463 chore: ci: fixes to Grove workflow (#9012) 2025-06-26 09:55:06 +00:00
Lean stage0 autoupdater
2c60f1a254 chore: update stage0 2025-06-26 09:48:45 +00:00
Markus Himmel
4f1d828541 chore: ci: build Linux toolchain for master commits (but not merge queue runs) (#9010) 2025-06-26 08:20:04 +00:00
Paul Reichert
70b4b2b36c feat: polymorphic ranges (#8784)
This PR introduces ranges that are polymorphic, in contrast to the
existing `Std.Range` which only supports natural numbers.

Breakdown of core changes:

* `Lean.Parser.Basic`: Modified the number parser (`Lean.Parser.Basic`)
so that it will only consider a *single* dot to be part of a decimal
number. `1..` will no longer be parsed as `1.` followed by `.`, but as
`1` followed by `..`.
* The test `ellipsisProjIssue` ensures that `#check Nat.add ...succ`
produces a syntax error. After introducing the new range notation (see
below), it returns a different (less nice) error message. I updated the
test to reflect the new error message. (The error message will become
nicer as soon as a delaborator for the ranges is implemented. This is
out of scope for this PR.)

Breakdown of standard library changes:

Modified modules: `Init.Data.Range.Polymorphic` (added),
`Init.Data.Iterators`, `Std.Data.Iterators`

* Introduced the type `Std.PRange` that is parameterized over the type
in which the range operates and the shapes of the lower and upper bound.
* Introduced a new notation for ranges. Examples for this notation are:
`1...*`, `1...=3`, `1...<3`, `1<...=2`, `*...=3`.
* Defined lots of typeclasses for different capabilities of ranges,
depending on their shape and underlying type.
* Introduced `Iter(M).size`.
* Introduced the `Iter(M).stepSize n` combinator, which iterates over an
iterator with the given step size `n`. It will drop `n - 1` values
between every value it emits.
* Replaced `LawfulPureIterator` with a new and better typeclass
`LawfulDeterministicIterator`.
* Simplified some lemma statements in the iterator library such as
`IterM.toList_eq_match`, which unnecessarily matched over a `Subtype`,
hindering rewrites due to type dependencies.

Reasons for the concrete choice of notation:

* `lean4-cli` uses `...`-based notation for the `Cmd` notation and it
clashes with `...a` range notation.
* test `2461` fails when using two-dot-based notation because of the
existing `{ a.. }` notation.
2025-06-26 08:18:11 +00:00
Paul Reichert
3695059504 feat: introduce MonadLiftT Id m (#8977)
This PR adds a generic `MonadLiftT Id m` instance. We do not implement a
`MonadLift Id m` instance because it would slow down instance resolution
and because it would create more non-canonical instances. This change
makes it possible to iterate over a pure iterator, such as `[1, 2,
3].iter`, in arbitrary monads.
2025-06-26 07:33:07 +00:00
Leonardo de Moura
b76bf44654 feat: infrastructure for cutsat generic ToInt (#9008)
This PR implements the basic infrastructure for the generic `ToInt`
support in `cutsat`.
2025-06-26 07:01:19 +00:00
Markus Himmel
d3dda9f6d4 chore: initial Grove setup (#8997) 2025-06-26 05:03:02 +00:00
Kim Morrison
561c18819c chore: typo (#9007) 2025-06-26 03:50:29 +00:00
David Thrane Christiansen
5ec3cc5df7 doc: review Repr and Format docstrings (#8998)
This PR makes the docstrings related to `Format` and `Repr` have
consistent formatting and style, and adds missing docstrings.
2025-06-26 03:20:23 +00:00
Kim Morrison
62e9d73f8b chore: revert BitVec/Lemmas grind proofs; too many bootstrapping difficulties (#9006) 2025-06-26 03:04:01 +00:00
Sofia Rodrigues
b15cfadde8 feat: monadic interface for asynchronous operations in Std (#8003)
This PR adds a new monadic interface for `Async` operations.

This is the design for the `Async` monad that I liked the most. The idea
was refined with the help of @tydeu. Before that, I had some
prerequisites in mind:

1. Good performance
2. Explicit `yield` points, so we could avoid using `bindTask` for every
lifted IO operation
3. A way to avoid creating an infinite chain of `Task`s during recursion

The 2 and 3 points are not covered in this PR, I wish I had a good
solution but right now only a few sketches of this.

### Explicit `yield` points

I thought this would be easy at first, but it actually turned out kinda
tricky. I ended up creating the `suspend` syntax, which is just a small
modification of the lift method (`<- ...`) syntax. It desugars to
`Suspend.suspend task fun _ => ...`. So something like:

```lean
do
  IO.println "a"
  IO.println "b"
  let result := suspend (client.recv? 1024)
  IO.println "c"
  IO.println "d"
```

Would become:

```lean
Bind.bind (IO.println "a") fun _ =>
Bind.bind (IO.println "b") fun _ =>
Suspend.suspend (client.recv? 1024) fun message =>
  Bind.bind (IO.println "c") fun _ =>
  IO.println "d"
```

This makes things a bit more efficient. When using `bind`, we would try
to avoid creating a `Task` chain, and the `suspend` would be the only
place we use `Task.bind`. But there's a problem if we use `bind` with
something that needs `suspend`, it’ll block the whole task. Blocking is
the only way to prevent task accumulation when using plain `bind` inside
a structure like that:

```
inductive AsyncResult (ε σ α : Type u) where
    | ok    : α → σ → AsyncResult ε σ α
    | error : ε → σ → AsyncResult ε σ α
    | ofTask  : Task (EStateM.Result ε σ α) → σ →AsyncResult ε σ α
```

Because we simply need to remove the `ofTask` and transform it into an
`ok`.

### Infinite chain of Tasks

If you create an infinite recursive function using `Task` (which is
super common in servers like HTTP ones), it can lead to a lot of memory
usage. Because those tasks get chained forever and won't be freed until
the function returns.

To get around that, I used CPS and instead of just calling `Task.bind`,
I’d spawn a new task and return an "empty" one like:

```lean
fun k => Task.bind (...) fun value => do k value; pure emptyTask
```

This works great with a CPS-style monad, but it generates a huge IR by
itself.

Just doing CPS alone was too much, though, because every lifted
operation created a new continuation and a `Task.bind`. So, I used it
with `suspend` and got a better performance, but the usage is not good
with `suspend`.

### The current monad

Right now, the monad I’m using is super simple. It doesn't solve the
earlier problems, but the API is clean, and the generated IR is small
enough. An example of how we should use it is:

```lean
-- A loop that repeatedly sends a message and waits for a reply.
partial def writeLoop (client : Socket.Client) (message : String) : Async (AsyncTask Unit) := async do
  IO.println s!"sending: {message}"
  await (← client.send (String.toUTF8 message))

  if let some mes ← await (← client.recv? 1024) then
    IO.println s!"received: {String.fromUTF8! mes}"
    -- use parallel to avoid building up an infinite task chain
    parallel (writeLoop client message)
  else
    IO.println "client disconnected from receiving"

-- Server’s main accept loop, keeps accepting and echoing for new clients.
partial def acceptLoop (server : Socket.Server) (promise : IO.Promise Unit) : Async (AsyncTask Unit) := async do
  let client ← await (← server.accept)
  await (← client.send (String.toUTF8 "tutturu "))

  -- allow multiple clients to connect at the same time
  parallel (writeLoop client "hi!!")

  -- and keep accepting more clients, parallel again to avoid building up an infinite task chain
  parallel (acceptLoop server promise)

-- A simple client that connects and sends a message.
def echoClient (addr : SocketAddress) (message : String) : Async (AsyncTask Unit) := async do
  let socket ← Client.mk
  await (← socket.connect addr)
  parallel (writeLoop socket message)

-- TCP setup: bind, listen, serve, and run a sample client.
partial def mainTCP : Async Unit := do
  let addr := SocketAddressV4.mk (.ofParts 127 0 0 1) 8080

  let server ← Server.mk
  server.bind addr
  server.listen 128

  -- promise exists since the server is (probably) never going to stop
  let promise ← IO.Promise.new
  let acceptAction ← acceptLoop server promise

  await (← echoClient addr "hi!")
  await acceptAction
  await promise

-- Entry point
def main : IO Unit := mainTCP.wait
```

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
Co-authored-by: Mac Malone <tydeu@hatpress.net>
2025-06-26 02:51:26 +00:00
Kim Morrison
1e135f2187 fix: refactor ToInt.OfNat (#9005)
This PR changes the definition of `Lean.Grind.ToInt.OfNat`, introducing
a `wrap` on the right-hand-side.
2025-06-26 02:27:15 +00:00
Cameron Zwarich
d6fdbe2b23 fix: implement main type validity check in the new compiler (#9003)
This PR implements the validity check for the type of `main` in the new
compiler. There were no tests for this, so it slipped under the radar.
2025-06-25 23:59:27 +00:00
Cameron Zwarich
567280cb41 chore: remove outdated comment (#9002) 2025-06-25 22:16:36 +00:00
jrr6
8da2f7105c chore: reword redundant alternative error explanation (#9001)
This PR adjusts the `lean.redundantMatchAlt` error explanation to remove
the word "unprefixed," which the reference manual's style linter does
not recognize.
2025-06-25 22:15:22 +00:00
Luisa Cicolini
25b1b46572 feat: add BitVec.msb_(smod, srem) (#8974)
This PR adds `BitVec.msb_(smod, srem)`. 

co-authored with @tobiasgrosser and @bollu

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-25 13:49:33 +00:00
Kim Morrison
0ddd9341d6 feat: refactor of Lean.Grind.ToInt and remaining instances (#8996)
This PR provides the remaining instances for the `Lean.Grind.ToInt`
typeclasses.
2025-06-25 13:32:38 +00:00
Joachim Breitner
b2a8d890c1 refactor: linearNoConfusionType: use PULift, not PUnit → (#8973)
This PR refactors the juggling of universes in the linear
`noConfusionType` construction: Instead of using `PUnit.{…} → ` in the
to get the branches of `withCtorType` to the same universe level, we use
`PULift`.

This fixes https://github.com/leanprover/lean4/issues/8962, although
probably doesn’t solve all issues of that kind while level equality
checking is incomplete.
2025-06-25 09:05:03 +00:00
Joachim Breitner
9641a9ac6c feat: PULift (#8992)
This PR adds `PULift`, a more general form of `ULift` and `PLift` that
subsumes both.

Needed in #8973
2025-06-25 09:04:52 +00:00
Wojciech Rozowski
15d1d38bd9 fix: add isDefEq check in the recursive call case of solveMonoStep inside monotonicity tactic (#8978)
This PR updates the `solveMonoStep` function used in the `monotonicity`
tactic to check for definitional equality between the current goal and
the monotonicity proof obtained from a recursive call. This ensures
soundness by preventing incorrect applications when
`Lean.Order.PartialOrder` instances differ—an issue that can arise with
`mutual` blocks defined using the `partial_fixpoint` keyword, where
different `Lean.Order.CCPO` structures may be involved.

Closes https://github.com/leanprover/lean4/issues/8894.
2025-06-25 08:40:15 +00:00
Kim Morrison
94f48c3cec feat: add ToInt typeclasses for grind (#8991)
This PR adds some missing `ToInt.X` typeclass instances for `grind`.

There are still several more to add (in particular, for `ToInt.Pow`),
but I am going to perform an intermediate refactor first.
2025-06-25 05:38:15 +00:00
Kim Morrison
58c69909a1 feat: doc-strings for grind algebra classes (#8990)
This PR adds missing doc-strings for grind's internal algebra
typeclasses, for inclusion in the reference manual.
2025-06-25 04:46:44 +00:00
Kim Morrison
708c5f1d9a chore: cleanup of grind in BitVec/Lemmas (#8989) 2025-06-25 03:00:31 +00:00
Kim Morrison
af22926d53 chore: updates to (failing) grind algebra tests (#8987) 2025-06-25 02:44:59 +00:00
Mac Malone
311ae6168d feat: lake: avoid use of Lean root directories (#8981)
This PR removes Lake's usage of `lean -R` and `moduleNameOfFileName` to
pass module names to Lean. For workspace names, it now relies on
directly passing the module name through `lean --setup`. For
non-workspace modules passed to `lake lean` or `lake setup-file`, it
uses a fixed module name of `_unknown`.

This means that `lake lean` and `lake setup-file` can be successfully
and consistently used on modules that do not lie under the working
directory or the workspace root.
2025-06-25 01:04:13 +00:00
Leonardo de Moura
f1021e4537 fix: congruence proof for over-applied terms (#8983)
This PR fixes a bug in congruence proof generation in `grind` for
over-applied functions.
2025-06-24 22:04:23 +00:00
Mac Malone
ddbba944d4 fix: pass Lean CMake CI options to the Lake build (#8823)
This PR passes Lean options configured via CMake variables onto the Lake
build. For example, this will ensure CI' setting of `warningAsError` via
`LEAN_EXTRA_MAKE_OPTS` reaches Lake.
2025-06-24 11:39:29 +00:00
Kim Morrison
3e8d28ae6b feat: use grind in BitVec/Lemmas (#8967)
This PR both adds initial `@[grind]` annotations for `BitVec`, and uses
`grind` to remove many proofs from `BitVec/Lemmas`.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-06-24 10:54:43 +00:00
Joachim Breitner
9d363e3541 fix: linter.simpUnusedSimpArgs to check syntax kind (#8971)
This PR fixes `linter.simpUnusedSimpArgs` to check the syntax kind, to
not fire on `simp` calls behind macros. Fixes #8969
2025-06-24 08:31:57 +00:00
Henrik Böving
a223e92f85 chore: remove use of deprecated API (#8970) 2025-06-24 08:22:50 +00:00
Luisa Cicolini
46a7c9108f feat: add BitVec.(getElem, getLsbD, getMsbD)_(smod, sdiv, srem) (#8941)
This PR adds `BitVec.(getElem, getLsbD, getMsbD)_(smod, sdiv, srem)`
theorems to complete the API for `sdiv`, `srem`, `smod`. Even though the
rhs is not particularly succint (it's hard to find a meaning for what it
means to have "the n-th bit of the result of a signed division/modulo
operation"), these lemmas prevent the need to `unfold` of operations.

---------

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-06-24 07:09:00 +00:00
Kyle Miller
a427a8264a chore: cleanup after stage0 update (#8966)
This PR cleans up the bootstrapping code added in #8957.
2025-06-24 05:34:57 +00:00
Kim Morrison
cc493e688b feat: embed a NatModule in its IntModule completion (#8963)
This PR embeds a NatModule into its IntModule completion, which is
injective when we have AddLeftCancel, and monotone when the modules are
ordered. Also adds some (failing) grind test cases that can be verified
once `grind` uses this embedding.
2025-06-24 05:30:43 +00:00
Kim Morrison
5a9d7ae925 feat: revise grind annotations for bitwise operations (#8965)
This PR revises @[grind] annotations on Nat bitwise operations.
2025-06-24 05:16:21 +00:00
Kim Morrison
e0c2263073 chore: add @[expose] in Grind/Ring/Poly.lean (#8964)
This PR adds `@[expose]` attributes to proof terms constructed by
`grind` that need to be evaluated in the kernel.
2025-06-24 05:14:12 +00:00
Lean stage0 autoupdater
e51d2d8747 chore: update stage0 2025-06-24 05:02:20 +00:00
Kim Morrison
449bc31832 chore: adds (failing) grind algebra tests (#8961) 2025-06-24 03:51:39 +00:00
Kim Morrison
8fe068ef68 feat: move lean-pr-testing-NNNN branches to a fork (#8933)
This PR changes the CI setup to generate `lean-pr-testing-NNNN` branches
for Mathlib on the `leanprover-community/mathlib4-nightly-testing` fork,
rather than on the main repo.
2025-06-24 03:30:43 +00:00
Kim Morrison
6970d77ae4 feat: the grothendieck envelope of an ordered semiring is an ordered ring (#8959)
This PR add instances showing that the Grothendieck (i.e. additive)
envelope of a semiring is an ordered ring if the original semiring is
ordered (and satisfies ExistsAddOfLE), and in this case the embedding is
monotone.
2025-06-24 03:23:18 +00:00
Leonardo de Moura
07662aafe3 fix: better case-split for match-conditions in grind (#8958)
This PR improves the case splitting strategy used in `grind`, and
ensures `grind` also considers simple `match`-conditions for
case-splitting. Example:

```lean
example (x y : Nat)
    : 0 < match x, y with
          | 0, 0   => 1
          | _, _ => x + y := by -- x or y must be greater than 0
  grind
```
2025-06-24 02:56:50 +00:00
Kyle Miller
b28dc8c5fb feat: add configuration for let/have tactics (#8957)
This PR adds configuration options to the `let`/`have` tactic syntaxes.
For example, `let (eq := h) x := v` adds `h : x = v` to the local
context. The configuration options are the same as those for the
`let`/`have` term syntaxes.
2025-06-24 02:49:02 +00:00
Cameron Zwarich
81740da50a fix: avoid caching uses of never_extract constants in toLCNF (#8956)
This PR changes `toLCNF` to stop caching translations of expressions
upon seeing an expression marked `never_extract`. This is more
coarse-grained than it needs to be, but it is difficult to do any
better, as the new compiler's `Expr` cache is based on structural
identity (rather than the pointer identity of the old compiler).

The newly added `tests/compiler/never_extract.lean` is also converted
into a `run` tests, because during development I found the order of the
output to `stderr` to be a bit finicky. The reason for making it a
`compiler` test in the first place is that closed term decls work
slightly differently between native code and the interpreter, and it
would be good to test both, but we already have separate tests for
`never_extract` and closed term extraction.

Fixes #8944.
2025-06-24 02:04:56 +00:00
Kyle Miller
32f8a95437 fix: Lean.MVarId.deltaLocalDecl (#8955)
This PR fixes `Lean.MVarId.deltaLocalDecl`, which previously replaced
the local definition with the target.
2025-06-24 01:37:18 +00:00
Kyle Miller
71cf266cd7 feat: add Meta.letToHave and the let_to_have tactic (#8954)
This PR adds a procedure that efficiently transforms `let` expressions
into `have` expressions (`Meta.letToHave`). This is exposed as the
`let_to_have` tactic.

It uses the `withTrackingZetaDelta` technique: the expression is
typechecked, and any `let` variables that don't enter the zeta delta set
are nondependent. The procedure uses a number of heuristics to limit the
amount of typechecking performed. For example, it is ok to skip
subexpressions that do not contain fvars, mvars, or `let`s.
2025-06-24 01:33:53 +00:00
Leonardo de Moura
0941d53f6a feat: semiring normalizer in grind (#8953)
This PR implements support for normalization for commutative semirings
that do not implement `AddRightCancel`. Examples:
```lean
variable (R : Type u) [CommSemiring R]

example (a b c : R) : a * (b + c) = a * c + b * a := by grind
example (a b : R) : (a + b)^2 = a^2 + 2 * a * b + b^2 := by grind
example (a b : R) : (a + 2 * b)^2 = a^2 + 4 * a * b + 4 * b^2 := by grind
example (a b : R) : (a + 2 * b)^2 = 4 * b^2 + b * 4 * a + a^2 := by grind
```
2025-06-24 01:09:22 +00:00
Leonardo de Moura
ba07e46368 refactor: simplify semiring normalization helper theorems (#8946)
This PR simplifies the semiring normalization theorem that will be used
by `grind`.
2025-06-23 23:20:20 +00:00
Cameron Zwarich
24cbd4efbe fix: correctly handle never_extract attribute in LCNF CSE (#8952)
This PR fixes the handling of the `never_extract` attribute in the
compiler's CSE pass. There is an interesting debate to be had about
exactly how hard the compiler should try to avoid duplicating anything
that transitively uses `never_extract`, but this is the simplest form
and roughly matches the check in the old compiler (although due to
different handling of local function decls in the two compilers, the
consequences might be slightly different).

This gets half of the way to #8944.
2025-06-23 23:03:10 +00:00
Cameron Zwarich
b0269d2875 chore: share leading prefix between then/else branches (#8951) 2025-06-23 22:17:54 +00:00
Wojciech Rozowski
22cd34c341 chore: rename keywords for (co)inductive predicates and the names of the associated (co)induction principles 2025-06-23 20:40:08 +02:00
Wojciech Rozowski
b4b68415e0 chore: update stage0 2025-06-23 20:40:08 +02:00
Wojciech Rozowski
07c398e441 chore: rename keywords for (co)inductive predicates and the names of their associated (co)induction principles
chore: rename `fixpoint_induct` to `induct` and `coinduct` for (co)inductive predicates
2025-06-23 20:40:08 +02:00
Mac Malone
dd64678f07 feat: server support for new module setup (#8699)
This PR adds support to the server for the new module setup process by
changing how `lake setup-file` is used.

In the new server setup, `lake setup-file` is invoked with the file name
of the edited module passed as a CLI argument and with the parsed header
passed to standard input in JSON form. Standard input is used to avoid
potentially exceeding the CLI length limits on Windows. Lake will build
the module's imports along with any other dependencies and then return
the module's workspace configuration via JSON (now in the form of
`ModuleSetup`). The server then post-processes this configuration a bit
and returns it back to the Lean language processor.

The server's header is currently only fully respected by Lake for
external modules (files that are not part of any workspace library). For
workspace modules, the saved module header is currently used to build
imports (as has been done since #7909). A follow-up Lake PR will align
both cases to follow the server's header.

Lean search paths (e.g., `LEAN_PATH`, `LEAN_SRC_PATH`) are no longer
negotiated between the server and Lake. These environment variables are
already configured during sever setup by `lake serve` and do not change
on a per-file basis. Lake can also pre-resolve the `.olean` files of
imports via the `importArts` field of `ModuleSetup`, limiting the
potential utility of communicating `LEAN_PATH`.
2025-06-23 18:00:14 +00:00
Mac Malone
e0a793ae20 feat: ignore lean -R if module name is in setup (#8874)
This PR skips attempting to compute a module name from the file name and
root directory (i.e., `lean -R`) if a name is already provided via `lean
--setup`.

This is accomplished by porting the rest of the frontend code in the
`try` block to Lean.
2025-06-23 17:55:52 +00:00
jrr6
32795911d2 feat: add initial error explanations (#8934)
This PR adds explanations for a few errors concerning noncomputability,
redundant match alternatives, and invalid inductive declarations.

These adopt a lower-case error naming style, which is also applied to
existing error explanation tests.
2025-06-23 17:24:09 +00:00
Anne Baanen
ecf670e08c feat: make math Lake template follow Mathlib standards (#8866)
This PR upgrades the `math` template for `lake init` and `lake new` to
configures the new project to meet rigorous Mathlib maintenance
standards. In comparison with the previous version (now available as
`lake new ... math-lax`), this automatically provides:

* Strict linting options matching Mathlib.
* GitHub workflow for automatic upgrades to newer Lean and Mathlib
releases.
* Automatic release tagging for toolchain upgrades.
* API documentation generated by
[doc-gen4](https://github.com/leanprover/doc-gen4) and hosted on
`github.io`.
* README with some GitHub-specific instructions.

The previous edition of the template is still available, renamed to
`math-lax`.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2025-06-23 13:28:47 +00:00
Leonardo de Moura
9a202a420b feat: semiring normalization theorems (#8943)
This PR adds helper theorems for normalizing semirings that do not
implement `AddRightCancel`.
2025-06-23 13:07:46 +00:00
Wojciech Rozowski
489d7b6d72 feat: add antitonicity lemmas for (co)inductive predicates (#8940)
This PR introduces antitonicity lemmas that support the elaboration of
mixed inductive-coinductive predicates defined using the
`least_fixpoint` / `greatest_fixpoint` constructs.

For instance, the following definition elaborates correctly because all
occurrences of the inductively defined predicate `tock `within the
coinductive definition of `tick` appear in negative positions. The dual
situation applies to the definition of `tock`:
```
  mutual
    def tick : Prop :=
      tock → tick
    greatest_fixpoint

    def tock : Prop :=
      tick → tock
    least_fixpoint
  end
```
2025-06-23 11:02:08 +00:00
Parth Shastri
8223a96bf5 fix: correct universe used in below/brecOn for non-reflexive inductive types (#8937)
This PR changes the output universe of the generated `below`
implementation for non-reflexive inductive types to match the
implementation for reflexive inductive types in #7639.

This fixes the `below`/`brecOn` implementations for certain nested
inductive types, as reported in
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Universes/near/525030149.
2025-06-23 09:42:31 +00:00
Joachim Breitner
29298c9f30 feat: linter.loopingSimpArgs (#8865)
This PR allows `simp` to recognize and warn about simp lemmas that are
likely looping in the current simp set. It does so automatically
whenever simplification fails with the dreaded “max recursion depth”
error fails, but it can be made to do it always with `set_option
linter.loopingSimpArgs true`. This check is not on by default because it
is somewhat costly, and can warn about simp calls that still happen to
work.

This closes #5111. In the end, this implemented much simpler logic than
described there (and tried in the abandoned #8688; see that PR
description for more background information), but it didn’t work as well
as I thought. The current logic is:

“Simplify the RHS of the simp theorem, complain if that fails”.

It is a reasonable policy for a Lean project to say that all simp
invocation should be so that this linter does not complain. Often it is
just a matter of explicitly disabling some simp theorems from the
default simp set, to make it clear and robust that in this call, we do
not want them to trigger. But given that often such simp call happen to
work, it’s too pedantic to impose it on everyone.
2025-06-23 07:36:21 +00:00
Cameron Zwarich
596a3034e7 chore: fix indentation (#8936) 2025-06-23 05:07:33 +00:00
Lean stage0 autoupdater
91a4e17b6d chore: update stage0 2025-06-23 03:43:45 +00:00
Kyle Miller
7b0a9bdadf feat: let +generalize (#8935)
This PR adds the `+generalize` option to the `let` and `have` syntaxes.
For example, `have +generalize n := a + b; body` replaces all instances
of `a + b` in the expected type with `n` when elaborating `body`. This
can be likened to a term version of the `generalize` tactic. One can
combine this with `eq` in `have +generalize (eq := h) n := a + b; body`
as an analogue of `generalize h : n = a + b`.
2025-06-23 02:21:57 +00:00
Kim Morrison
8f4b2909de chore: cleanup of grind's order typeclasses (#8913)
This PR cleans up `grind`'s internal order typeclasses, removing
unnecessary duplication.
2025-06-22 23:36:48 +00:00
Kyle Miller
bb0132e4b3 chore: for #8914 after stage0 update, part 2 (#8931)
This PR finishes post-stage0-cleanup after #8914 and #8929. Also:
- adds configuration options for `haveI` and `letI` terms.
- adds `letConfig` parser alias
2025-06-22 22:40:00 +00:00
Kyle Miller
02c8c2f9e1 feat: use nondep flag in Expr.letE and LocalContext.ldecl (#8804)
This PR implements first-class support for nondependent let expressions
in the elaborator; recall that a let expression `let x : t := v; b` is
called *nondependent* if `fun x : t => b` typechecks, and the notation
for a nondependent let expression is `have x := v; b`. Previously we
encoded `have` using the `letFun` function, but now we make use of the
`nondep` flag in the `Expr.letE` constructor for the encoding. This has
been given full support throughout the metaprogramming interface and the
elaborator. Key changes to the metaprogramming interface:
- Local context `ldecl`s with `nondep := true` are generally treated as
`cdecl`s. This is because in the body of a `have` expression the
variable is opaque. Functions like `LocalDecl.isLet` by default return
`false` for nondependent `ldecl`s. In the rare case where it is needed,
they take an additional optional `allowNondep : Bool` flag (defaults to
`false`) if the variable is being processed in a context where the value
is relevant.
- Functions such as `mkLetFVars` by default generalize nondependent let
variables and create lambda expressions for them. The
`generalizeNondepLet` flag (default true) can be set to false if `have`
expressions should be produced instead. **Breaking change:** Uses of
`letLambdaTelescope`/`mkLetFVars` need to use `generalizeNondepLet :=
false`. See the next item.
- There are now some mapping functions to make telescoping operations
more convenient. See `mapLetTelescope` and `mapLambdaLetTelescope`.
There is also `mapLetDecl` as a counterpart to `withLetDecl` for
creating `let`/`have` expressions.
- Important note about the `generalizeNondepLet` flag: it should only be
used for variables in a local context that the metaprogram "owns". Since
nondependent let variables are treated as constants in most cases, the
`value` field might refer to variables that do not exist, if for example
those variables were cleared or reverted. Using `mapLetDecl` is always
fine.
- The simplifier will cache its let dependence calculations in the
nondep field of let expressions.
- The `intro` tactic still produces *dependent* local variables. Given
that the simplifier will transform lets into haves, it would be
surprising if that would prevent `intro` from creating a local variable
whose value cannot be used.

Note that nondependence of lets is not checked by the kernel. To
external checker authors: If the elaborator gets the nondep flag wrong,
we consider this to be an elaborator error. Feel free to typecheck `letE
n t v b true` as if it were `app (lam n t b default) v` and please
report issues.

This PR follows up from #8751, which made sure the nondep flag was
preserved in the C++ interface.
2025-06-22 21:54:57 +00:00
Lean stage0 autoupdater
2ebc001dd1 chore: update stage0 2025-06-22 20:38:51 +00:00
Kyle Miller
f4f664e1ed fix: update Parser.Term.letIdDeclNoBinders to use new letIdDecl format (#8929)
This PR is a followup to #8914, fixing an oversight where
`letIdDeclBinders` is was not updated with the new format. This relies
on some bootstrapping code to stay in place, but we do bootstrap cleanup
that is currently possible.
2025-06-22 19:28:46 +00:00
Mac Malone
ded8a0cb57 feat: IO.FS.Stream.readToEnd (#8886)
This PR adds `IO.FS.Stream.readToEnd` which parallels
`IO.FS.Handle.readToEnd` along with its upstream definitions (i.e.,
`readBinToEndInto` and `readBinToEnd`). It also removes an unnecessary
`partial` from `IO.FS.Handle.readBinToEnd`.

This function is useful for reading, for example, all of standard input.
2025-06-22 15:39:10 +00:00
Mac Malone
52bdc9bcbd feat: IO.FS.Stream.lines & IO.FS.Handle.lines (#8887)
This PR generalizes `IO.FS.lines` with `IO.FS.Handle.lines` and adds the
parallel `IO.FS.Stream.lines` for streams.

The stream version is useful for reading, for example, the lines of
standard input.
2025-06-22 14:57:17 +00:00
Joachim Breitner
6092561f93 refactor: SimpM.run (#8843)
This PR factors out the common code for running `SimpM` from `mainCore`
and `dsimpMainCore`, and make it available separately (e.g. for #8865).
2025-06-22 13:50:44 +00:00
Joachim Breitner
117f73fc84 feat: linter.unusedSimpArgs (#8901)
This PR adds a linter (`linter.unusedSimpArgs`) that complains when a
simp argument (`simp [foo]`) is unused. It should do the right thing if
the `simp` invocation is run multiple times, e.g. inside `all_goals`. It
does not trigger when the `simp` call is inside a macro. The linter
message contains a clickable hint to remove the simp argument.

I chose to display a separate warning for each unused argument. This
means that the user has to click multiple times to remove all of them
(and wait for re-elaboration in between). But this just means multiple
endorphine kicks, and the main benefit over a single warning that would
have to span the whole argument list is that already the squigglies tell
the users about unused arguments.

This closes #4483.

Making Init and Std clean wrt to this linter revealed close to 1000
unused simp args, a pleasant experience for anyone enjoying tidying
things: #8905
2025-06-22 09:10:21 +00:00
Sebastian Graf
1e78207d3a chore: Revert "feat: Upstream MPL.SPred.* from mpl" (#8927)
Reverts leanprover/lean4#8745 until I take a closer look on its breakage
in Mathlib on Monday
2025-06-22 09:02:54 +00:00
Lean stage0 autoupdater
16c918a652 chore: update stage0 2025-06-22 08:08:57 +00:00
Kyle Miller
239534cbb7 chore: for #8914 after stage0 update (#8925)
This PR does a first pass at cleaning things up for #8914 after a stage0
update.
2025-06-22 06:52:11 +00:00
Cameron Zwarich
85e061bed5 chore: remove unused impure LCNF Phase (#8924)
The `.impure` LCNF `Phase` is not currently used, but was intended for a
potential future where the current `IR` passes (which operate on a
highly impure representation) were rewritten to operate on LCNF instead.
For several reasons, I don't think this is very likely to happen, and
instead we are more likely to remove some of the unnecessary differences
between LCNF and IR while keeping them distinct.
2025-06-22 05:38:16 +00:00
Cameron Zwarich
d41b9f004a feat: support casesOn for Thunk and Task (#8923)
This PR implements `casesOn` for `Thunk` and `Task`. Since these are
builtin types, this needs to be special-cased in `toMono`.

Fixes #8659.
2025-06-22 05:24:33 +00:00
Lean stage0 autoupdater
c63618b7b8 chore: update stage0 2025-06-22 05:33:59 +00:00
Kyle Miller
219f8214d3 feat: make let and have term syntaxes be consistent (#8914)
This PR modifies `let` and `have` term syntaxes to be consistent with
each other. Adds configuration options; for example, `have` is
equivalent to `let +nondep`, for *nondependent* lets. Other options
include `+usedOnly` (for `let_tmp`), `+zeta` (for `letI`/`haveI`), and
`+postponeValue` (for `let_delayed)`. There is also `let (eq := h) x :=
v; b` for introducing `h : x = v` when elaborating `b`. The `eq` option
works for pattern matching as well, for example `let (eq := h) (x, y) :=
p; b`.

Future PRs will add these options to tactic syntax, once a stage0 update
has been done.
2025-06-22 04:22:47 +00:00
Leonardo de Moura
7531d16112 feat: (commutative) semiring support in grind (#8921)
This PR implements support for (commutative) semirings in `grind`. It
uses the Grothendieck completion to construct a (commutative) ring
`Lean.Grind.Ring.OfSemiring.Q α` from a (commutative) semiring `α`. This
construction is mostly useful for semirings that implement
`AddRightCancel α`. Otherwise, the function `toQ` is not injective.
Examples:
```lean
example (x y : Nat) : x^2*y = 1 → x*y^2 = y → y*x = 1 := by
  grind 

example [CommSemiring α] [AddRightCancel α] (x y : α) : x^2*y = 1 → x*y^2 = y → y*x = 1 := by
  grind

example (a b : Nat) : 3 * a * b = a * b * 3 := by grind

example (k z : Nat) : k * (z * 2 * (z * 2 + 1)) = z * (k * (2 * (z * 2 + 1))) := by grind

example [CommSemiring α] [AddRightCancel α] [IsCharP α 0] (x y : α) 
    : x^2*y = 1 → x*y^2 = y → x + y = 1 → False := by
  grind
```
2025-06-21 23:00:16 +00:00
Joachim Breitner
61518e4357 chore: remove more unused simp args (#8920)
This PR uses the linter from #8901 to clean up more simp arguments,
completing #8905.
2025-06-21 18:34:17 +00:00
Joachim Breitner
2441bf1f76 perf: check simp cache in simpLoop (#8880)
This PR makes `simp` consult its own cache more often, to avoid
replicating work.

Before, the simp cache was checked upon entry of `simpImpl` only, which
then calls `simpLoop`, which recursively iterates the `pre`-lemmas,
without checking the cache again.

Now, `simpLoop` itself checks the cache. This seems more principled,
given that `simpLoop` is actually putting entries into the cache for
each of its calls, so it’s more uniform if it checks the cache itself.

This avoids repeated rewrites. For example given
```
theorem ab : a = b := testSorry
theorem bc : b = c := testSorry
example (h : P c) : P b ∧ P a := by simp [ab, bc, h]
```
simp would rewrite `b ==> c` twice (once as part of `b ==> c` and then
again as part of `a ==> b ==> c`). And it’d be order dependent: With
```
example (h : P c) : P a ∧ P b := by simp [ab, bc, h]
```
the `a ==> b ==> c` chain would insert `b ==> c` into the cache, and
picked up by `simpImpl` when rewriting `P b`.

With this change, `b ==> c` is performed only once in both examples.

Instruction counts on stdlib and mathlib both show a mild improvement
across the board (0.5%), with individual modules improving by up to 4%
in stdlib and even more in mathlib.


(This does not check the cache before applying `post`, which explains
where there are still some repeated rewrites in the trace logs. But I’m
less sure about inserting a cache check here and so I am treading
carefully here. It’s also going to be at most one `post` application
that’s duplicated, because if `post` returns `.visit`, we go back to
`pre` and thus a cache check.)
2025-06-21 17:58:05 +00:00
Joachim Breitner
4d697874b7 refactor: simp arg elaboration (#8815)
This PR refactors the way simp arguments are elaborated: Instead of
changing the `SimpTheorems` structure as we go, this elaborates each
argument to a more declarative description of what it does, and then
apply those. This enables more interesting checks of simp arguments that
need to happen in the context of the eventually constructed simp context
(the checks in #8688), or after simp has run (unused argument linter
#8901).

The new data structure describing an elaborated simp argument isn’t the
most elegant, but follows from the code.

While I am at it, move handling of `[*]` into `elabSimpArgs`. Downstream
adaption branches exist (but may not be fully up to date because of the
permission changes).

While I am at it, I cleaned up `SimpTheorems.lean` file a bit (sorting
declarations, mild renaming) and added documentation.
2025-06-21 17:55:53 +00:00
Cameron Zwarich
85992757e7 fix: check guard_msgs.diff using .get rather than Options.getBool (#8918)
This PR fixes the `guard_msgs.diff` default behavior so that the default
specified in the option definition is actually used everywhere.
2025-06-21 16:03:31 +00:00
Cameron Zwarich
7d82dd99c9 chore: add test for #4278, which was fixed by the new compiler (#8916) 2025-06-21 15:05:46 +00:00
Kyle Miller
3878432ac7 fix: make sure local instance detection sees through reductions (#8903)
This PR make sure that the local instance cache calculation applies more
reductions. In #2199 there was an issue where metavariables could
prevent local variables from being considered as local instances. We use
a slightly different approach that ensures that, for example, `let`s at
the ends of telescopes do not cause similar problems. These reductions
were already being calculated, so this does not require any additional
work to be done.

Metaprogramming interface addition: the various forall telescope
functions that do reduction now have a `whnfType` flag (default false).
If it's true, then the callback `k` is given the WHNF of the type. This
is a free operation, since the telescope function already computes it.
2025-06-21 06:26:32 +00:00
Kim Morrison
5198a3fbb7 feat: refactor grind's typeclasses for ordered algebra (#8855)
This PR refactors `Lean.Grind.NatModule/IntModule/Ring.IsOrdered`.

We ensure the the diamond from `Ring` to `NatModule` via either
`Semiring` or `IntModule` is defeq, which was not previously the case.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-06-21 04:49:13 +00:00
Leonardo de Moura
921453e3e6 feat: NoNatZeroDivisors for Semiring envelope (#8910)
This PR adds the `NoNatZeroDivisors` instance for `OfSemiring.Q α`
2025-06-21 03:56:37 +00:00
Leonardo de Moura
9ece4e463a refactor: NoNatZeroDivisors (#8909)
This PR refactors the `NoNatZeroDivisors` to make sure it will work with
the new `Semiring` support.
2025-06-21 03:01:05 +00:00
Sebastian Ullrich
c38c0898a3 chore: allow module in tests (#8881)
This PR adjusts the test scripts and adds a simple test-only lakefile so
that `experimental.module` is set both when editing and running tests.
2025-06-21 02:49:22 +00:00
Leonardo de Moura
12a8f1b5f8 chore: remove staging workarounds (#8908) 2025-06-21 02:38:09 +00:00
Lean stage0 autoupdater
7050dc6d38 chore: update stage0 2025-06-21 01:59:13 +00:00
Kim Morrison
376ae32c7c feat: fix pretty printing of grind attributes (#8892)
This PR corrects the pretty printing of `grind` modifiers. Previously
`@[grind →]` was being pretty printed as `@[grind→ ]` (Space on the
right of the symbol, rather than left.) This fixes the pretty printing
of attributes, and preserves the presence of spaces after the symbol in
the output of `grind?`.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-06-21 00:50:25 +00:00
Cameron Zwarich
0c44aab811 chore: add a test for #4716, which is fixed by the new compiler (#8907) 2025-06-20 23:43:25 +00:00
Kim Morrison
a5eeed4f2c chore: a few missing grind typeclass docstrings (#8906) 2025-06-20 23:35:58 +00:00
Joachim Breitner
be80a23281 chore: remove unused simp args (#8905)
This PR uses the linter from
https://github.com/leanprover/lean4/pull/8901 to clean up simp
arguments.
2025-06-20 22:34:30 +00:00
Sebastian Ullrich
92ac564f3c fix: make mkHCongrWithArityForConst? compatible with parallelism (#8899)
This PR ensures the helper is compatible with using `grind` in
asynchronous proofs
2025-06-20 21:55:14 +00:00
Cameron Zwarich
0fcb6495d6 chore: add a test for #6957, fixed by the new compiler (#8904) 2025-06-20 21:44:09 +00:00
Lean stage0 autoupdater
e7c8baaef5 chore: update stage0 2025-06-20 18:52:57 +00:00
Cameron Zwarich
8d8c73416a chore: add a test for #2602, which was fixed by the new compiler (#8902) 2025-06-20 17:37:19 +00:00
Sebastian Graf
cf527e05bd feat: where ... finally section to assign leftover goals (#8723)
This PR implements a `finally` section following a (potentially empty)
`where` block. `where ... finally` opens a tactic sequence block in
which the goals are the unassigned metavariables from the definition
body and its auxiliary definitions that arise from use of `let rec` and
`where`.

This can be useful for discharging multiple proof obligations in the
definition body by a single invocation of a tactic such as `all_goals`:
```lean
example (i j : Nat) (xs : Array Nat) (hi : i < xs.size) (hj: j < xs.size) :=
  match i with
  | 0 => x
  | _ => xs[i]'?_ + xs[j]'?_
where x := 13
finally all_goals assumption
```

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-20 15:51:28 +00:00
Sebastian Graf
61ee83f73b feat: Upstream MPL.SPred.* from mpl (#8745)
This PR adds a logic of stateful predicates `SPred` to `Std.Do` in order
to support reasoning about monadic programs. It comes with a dedicated
proof mode the tactics of which are accessible by importing
`Std.Tactic.Do`.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-20 15:13:40 +00:00
Cameron Zwarich
26b7e49c05 chore: update stage0 2025-06-20 17:29:10 +02:00
Cameron Zwarich
466c9b56ba chore: add new tests for noncomputable 2025-06-20 17:29:10 +02:00
Cameron Zwarich
00474e17ff chore: add an extra test case to lean/run/noncomp.lean 2025-06-20 17:29:10 +02:00
Cameron Zwarich
891a2c6590 chore: reenable subset of new-compiler tests and delete others 2025-06-20 17:29:10 +02:00
Cameron Zwarich
d489c6196c chore: update expected test outputs
This makes it easier to distinguish tests that are actually
failing while we work on the new codegen.
2025-06-20 17:29:10 +02:00
Cameron Zwarich
6703af1ea0 chore: rename closed term suffix from _closedTerm to _closed
The longer name was chosen to avoid clashes with the old compiler.
2025-06-20 17:29:10 +02:00
Cameron Zwarich
7f8ccd8425 feat: enable the new compiler 2025-06-20 17:29:10 +02:00
Joachim Breitner
a8d5982fce chore: Init: clean up some simp calls (#8897)
This PR simplifies some `simp` calls.

These are the good parts of #8896.
2025-06-20 13:26:04 +00:00
Henrik Böving
50cfe354be chore: remove old LEAN_AUTO_THREAD_FINALIZATION workaround (#8885)
This PR removes an old workaround around non-implemented C++11 features
in the thread finalization.

This `ifdef` dates back to approximately 2015 as can be seen
[here](https://github.com/leanprover/lean3/blame/master/src/util/thread.cpp#L177),
the comments mention that it was originally implemented because not all
compilers at the time were able to support the C++11 `thread_local`
keyword. 10 years later this is hopefully the case and we can remove
this workaround.

There is an additional motivation for doing this,
`lean::initialize_thread` contains the following allocation:
```cpp
    g_thread_finalizers_mgr = new thread_finalizers_manager;
```
this is supposed to be freed at some point but:
```cpp
// TODO(gabriel): race condition with thread finalizers
void delete_thread_finalizer_manager() {
    // delete g_thread_finalizers_mgr;
    // g_thread_finalizers_mgr = nullptr;
}
```
so `g_thread_finalizers_mgr` leaks upon repeated invocation of
`lean::initialize_thread`.

Note that Windows has already been using this alternative implementation
for a while so the alternative implementation has (hopefully) not rotten
away in the meantime.
2025-06-20 08:52:17 +00:00
Kim Morrison
a750da5a7f chore: convert DHashMap to a structure (#8761)
This PR changes the definition of `DHashMap` to a structure. This makes
it more consistent with the other map types, which are generally defined
as structures. It also ensures that the type `DHashMap α β` is already
in weak head normal form, making it easier for `grind` to successfully
generate patterns for `DHashMap` lemmas.
2025-06-20 08:16:46 +00:00
Leonardo de Moura
588df4612a fix: missing isEqFalse (#8893)
This PR fixes a bug in the `dvd` propagation function in cutsat.
2025-06-20 08:16:08 +00:00
Miyahara Kō
dd78012ddd style: replace HEq x y with x ≍ y (#8872)
Although `HEq` was abbreviated as `≍` in #8503, many instances of the
form `HEq x y` still remain.
Therefore, I searched for occurrences of `HEq x y` using the regular
expression `(?<![A-Za-z/@]|``)HEq(?![A-Za-z.])` and replaced as many as
possible with the form `x ≍ y`.
2025-06-20 07:47:33 +00:00
Kim Morrison
db499e96aa feat: add doc-string to grind algebra typeclasses (#8890)
This PR adds doc-strings to the `Lean.Grind` algebra typeclasses, as
these will appear in the reference manual explaining how to extend
`grind` algebra solvers to new types. Also removes some redundant
fields.
2025-06-20 04:05:47 +00:00
jrr6
f416143fbc feat: improve error behavior of end command (#8387)
This PR improves the error messages produced by `end` and prevents
invalid `end` commands from closing scopes on failure.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-06-20 03:05:51 +00:00
Kim Morrison
743c60224a chore: minimize grind panic (#8889) 2025-06-20 01:07:14 +00:00
Kim Morrison
8af3b89203 chore: @[expose] defs that appear in grind proof terms (#8882)
This PR adds `@[expose]` annotations to terms that appear in `grind`
proof certificates, so `grind` can be used in the module system. It's
possible/likely that I haven't identified all of them yet.
2025-06-19 22:39:50 +00:00
Siddharth
da9a536ffd feat: BitVec.msb_sdiv (#8178)
This PR provides a compact formula for the MSB of the sdiv. Most of the
work in the PR involves handling the corner cases of division
overflowing (e.g. `intMin / -1 = intMin`)

---------

Co-authored-by: Luisa Cicolini <48860705+luisacicolini@users.noreply.github.com>
Co-authored-by: Tobias Grosser <github@grosser.es>
2025-06-19 09:08:04 +00:00
Kim Morrison
0077dd3d55 chore: remove redundant field from Lean.Grind.IntModule (#8879) 2025-06-19 06:03:14 +00:00
Kim Morrison
63cfe908c5 feat: add grind annotations for List/Array/Vector monadic functions (#8878)
This PR adds grind annotations for List/Array/Vector monadic functions.
2025-06-19 05:10:43 +00:00
Kim Morrison
c796609159 feat: grind annotations for List/Array/Vector.attach/pmap (#8877)
This PR adds grind annotations for
`List/Array/Vector.attach/attachWith/pmap`.
2025-06-19 05:00:35 +00:00
Kim Morrison
827c69e46e feat: generalize Lean.Grind.IsCharP to semirings (#8847)
This PR relaxes the assumptions for `Lean.Grind.IsCharP` from `Ring` to
`Semiring`, and provides an alternative constructor for rings.
2025-06-19 04:39:53 +00:00
Cameron Zwarich
19d9f6c450 chore: remove brittle new compiler tests that depend on internal decls (#8875) 2025-06-19 03:56:14 +00:00
Cameron Zwarich
bec538cc57 chore: delete disabled new-compiler tests that are no longer very useful (#8873) 2025-06-18 21:18:58 +00:00
Kyle Miller
e74d3a2f1c chore: address stage0 update TODOs (#8869)
This PR addresses a few TODOs left in comments for things to do after a
stage0 update.
2025-06-18 20:52:50 +00:00
Paul Reichert
1b5a9be785 feat: ForIn' and size for iterators (#8768)
This PR introduces a `ForIn'` instance and a `size` function for
iterators in a minimal fashion. The `ForIn'` instance is not marked as
an instance because it is unclear which `Membership` relation is
sufficiently useful. The `ForIn'` instance existing as a `def` and
inducing the `ForIn` instance, it becomes possible to provide more
specialized `ForIn'` instances, with nice `Membership` relations, for
various types of iterators. The `size` function has no lemmas yet.
2025-06-18 19:41:20 +00:00
Marc Huisinga
aea8e11d4b fix: restore code action incrementality (#8868)
This PR ensures that code actions do not have to wait for the full file
to elaborate. This regression was accidentally introduced in #7665.
2025-06-18 18:00:20 +00:00
Lean stage0 autoupdater
935aa38603 chore: update stage0 2025-06-18 18:08:31 +00:00
jrr6
e5c6fe1dac feat: add elaborators, completions, and hovers for named errors (#8730)
This PR adds support for throwing named errors with associated error
explanations. In particular, it adds elaborators for the syntax defined
in #8649, which use the error-explanation infrastructure added in #8651.
This includes completions, hovers, and jump-to-definition for error
names.

Note that another stage0 rebuild will be required to define explanations
using `register_error_explanation`.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
Co-authored-by: Marc Huisinga <mhuisi@protonmail.com>
2025-06-18 15:51:34 +00:00
Luisa Cicolini
62f3ee2887 feat: add leading zero counter BitVec.clz and bitblaster circuit/infrastructure (#8546)
This PR adds a new `BitVec.clz` operation and a corresponding `clz`
circuit to `bv_decide`, allowing to bitblast the count leading zeroes
operation. The AIG circuit is linear in the number of bits of the
original expression, making the bitblasting convenient wrt. rewriting.
`clz` is common in numerous compiler intrinsics (see
[here](https://clang.llvm.org/docs/LanguageExtensions.html#intrinsics-support-within-constant-expressions))
and architectures (see
[here](https://en.wikipedia.org/wiki/Find_first_set)).

Co-authored by @bollu.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-18 15:50:04 +00:00
Sebastian Ullrich
e8c82610cd refactor: make syntax covering snapshot tasks more precise on the top level (#8744) 2025-06-18 13:23:21 +00:00
Paul Reichert
86eded35db refactor: partially move iterators to Init (#8767)
This PR moves parts of the iterator library from `Std` to `Init`. The
reason is that the polymorphic range API must be in `Init` and it
depends on the iterators.
2025-06-18 10:08:04 +00:00
Lean stage0 autoupdater
f0fdab86bb chore: update stage0 2025-06-18 10:07:05 +00:00
Kim Morrison
d58e253671 chore: add missing List.eraseIdx_insertIdx deprecation (#8863) 2025-06-18 08:48:00 +00:00
Kim Morrison
d0c1053903 chore: add test case for grind panic (#8861)
This PR adds a (failing) test case for a panic caused by grind.
2025-06-18 08:00:17 +00:00
Kim Morrison
48a0e742d8 chore: Lean.Grind.IntModule instances (#8859)
This PR shows the equivalence between `Lean.Grind.NatModule.IsOrdered`
and `Lean.Grind.IntModule.IsOrdered` over an `IntModule`.
2025-06-18 07:30:37 +00:00
Sebastian Ullrich
d131cf39c1 fix: set public aux decl prefix in init_grind_norm (#8856)
This PR ensures simp theorems generated by `init_grind_norm` are
accessible in other `module`s
2025-06-18 07:19:37 +00:00
Markus Himmel
c16204615d chore: add a failing grind test (#8858) 2025-06-18 07:14:56 +00:00
Mac Malone
e83b768140 feat: lake: reintroduce lean --setup basics (#8846)
This PR reintroduces the basics of `lean --setup` integration into Lake
without the module computation which is still undergoing performance
debugging in #8787.

Partially reverts #8736 and partially reimplements #8447.
2025-06-18 06:12:39 +00:00
Kyle Miller
6240cd5aa9 feat: make sure clear_value preserves local context order (#8792)
This PR makes the `clear_value` tactic preserve the order of variables
in the local context. This is done by adding
`Lean.MVarId.withRevertedFrom`, which reverts all local variables
starting from a given variable, rather than only the ones that depend on
it.

Note: an alternative implementation might convert the ldecl to a cdecl
and then reset the meta cache. This assumes that there are no other
caches that might still remember the value of the ldecl.
2025-06-18 04:40:20 +00:00
Kim Morrison
cf47e5f6a7 feat: generalize grind IsCharP instance (#8848)
This PR generalizes the internal `grind` instance 
```
instance [Field α] [LinearOrder α] [Ring.IsOrdered α] : IsCharP α 0
```
to 
```
instance [Ring α] [Preorder α] [Ring.IsOrdered α] : IsCharP α 0
```
2025-06-18 02:49:26 +00:00
Kim Morrison
16e67dc738 feat: grind annotations for Nat.Bitwise (#8852)
This PR adds grind annotations for `Nat.testBit` and bitwise operations
on `Nat`.

(Also includes some in-progress tests for `BitVec`.)
2025-06-18 02:42:43 +00:00
Kim Morrison
5810f8907d feat: grind annotations relating Nat/Fin fold operations to List (#8853)
This PR adds `grind` annotations relating `Nat.fold/foldRev/any/all` and
`Fin.foldl/foldr/foldlM/foldrM` to the corresponding operations on
`List.finRange`.
2025-06-18 02:42:32 +00:00
Kim Morrison
22e8b476ba feat: grind annotations for Function.(un)curry (#8851)
This PR adds grind annotations for `Function.curry`/`uncurry`.
2025-06-18 02:41:00 +00:00
Kim Morrison
3a8258b2d5 feat: grind annotations for Prod (#8850)
This PR adds `grind` annotations for `Prod`.
2025-06-18 02:40:23 +00:00
Kim Morrison
aa9f966aee feat: grind annotations for Sum (#8849)
This PR adds `grind` annotations for `Sum`.
2025-06-18 02:21:17 +00:00
Sebastian Ullrich
e129e75e66 chore: CI: temporarily disable .olean cache 2025-06-18 11:12:56 +09:00
Lean stage0 autoupdater
04c273dbc6 chore: update stage0 2025-06-18 02:09:11 +00:00
Leonardo de Moura
2b39b453e7 feat: proof-by-reflection support for converting semiring terms into ring ones (#8845)
This PR implements the proof-by-reflection infrastructure for embedding
semiring terms as ring ones.
2025-06-17 19:24:15 +00:00
Luisa Cicolini
43aaae7348 feat: add BitVec.(toNat, toInt, toFin)_shiftLeftZeroExtend (#8811)
This PR adds theorems `BitVec.(toNat, toInt,
toFin)_shiftLeftZeroExtend`, completing the API for
`BitVec.shiftLeftZeroExtend`.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-06-17 17:43:40 +00:00
Parth Shastri
92dec7e864 feat: allow structures to have non-bracketed binders (#8671)
This PR allow structures to have non-bracketed binders, making it
consistent with `inductive`.

The change allows the following to be written instead of having to write
`S (n)`:
```lean
structure S n where
  field : Fin n
```
2025-06-17 17:40:18 +00:00
Kim Morrison
b3a53d5d01 feat: generalize embedding of CommSemiring into its CommRing envelope to the noncommutative case (#8836)
This PR generalizes #8835 to the noncommutative case, allowing us to
embed a `Lean.Grind.Semiring` into a `Lean.Grind.Ring`.
2025-06-17 09:09:05 +00:00
Sebastian Ullrich
3b2990b381 chore: CI: work around test-speedcenter breaking on ubuntu-latest 2025-06-17 18:14:12 +09:00
Parth Shastri
17b133369d refactor: remove binductionOn, use brecOn instead (#8820)
This PR removes the auto-generated `binductionOn` and `ibelow`
implementations for inductive types in favor of the improved `brecOn`
implementation from #7639.
2025-06-17 07:07:24 +00:00
Kim Morrison
259e2ec3e8 feat: define the CommRing envelope of a CommSemiring (#8835)
This PR defines the embedding of a `CommSemiring` into its `CommRing`
envelope, injective when the `CommSemiring` is cancellative. This will
be used by `grind` to prove results in `Nat`.
2025-06-17 05:39:14 +00:00
Cameron Zwarich
c9d0af1d7e chore: delete tests/lean/run/CompilerProbe.lean (#8833)
This test is essentially disabled on `master`, because it prints
nothing. With the new compiler enabled, it prints names of functions
throughout the Lean codebase satisfying certain conditions. Even just
maintaining this on the new compiler branch got old pretty quickly, so I
can't imagine we'd ever want to deal with this on `master`.
2025-06-17 05:01:12 +00:00
Cameron Zwarich
471553102c chore: delete redundant copy of unhygienicCode test (#8832)
This copy in `lean/new-compiler` is the same as the copy In `lean`, just
with different IR printing settings.
2025-06-17 04:57:07 +00:00
Cameron Zwarich
18caad9756 fix: cache scalar type info in toIR (#8831)
This PR caches the result of `lowerEnumToScalarType`, which is used
heavily in LCNF to IR conversion.
2025-06-17 04:31:33 +00:00
Kim Morrison
f557bf6024 chore: move grind algebra instances into Init.GrindInstances (#8830)
This PR rearranges files under `Init.Grind`, moving out instances for
concrete algebraic types in `Init.GrindInstances`.
2025-06-17 03:59:15 +00:00
Kim Morrison
548cc4e555 chore: reorganize BitVec files (#8829)
This PR avoids importing all of `BitVec.Lemmas` and `BitVec.BitBlast`
into `UInt.Lemmas`. (They are still imported into `SInt.Lemmas`; this
seems much harder to avoid.)
2025-06-17 03:30:35 +00:00
Kim Morrison
38fb9c5328 chore: rename BitVec.getLsb' back to BitVec.getLsb (#8827)
This PR renames `BitVec.getLsb'` to `BitVec.getLsb`, now that older
deprecated definition occupying that name has been removed. (Similarly
for `BitVec.getMsb'`.)
2025-06-17 01:28:07 +00:00
Kim Morrison
ba39fd3ca8 fix: correct Lean.Grind.NatModule (#8826)
This PR corrects the definition of `Lean.Grind.NatModule`, which wasn't
previously useful.
2025-06-17 01:00:48 +00:00
Kim Morrison
fcb3b2ec66 chore: raise internal grind limits to allow examples (#8807) 2025-06-17 01:00:17 +00:00
Cameron Zwarich
e1408d29bc fix: improve IR for inductive types represented as scalars (#8825)
This PR improves IR generation for constructors of inductive types that
are represented by scalars. Surprisingly, this isn't required for
correctness, because the boxing pass will fix it up. The extra `unbox`
operation it inserts shouldn't matter when compiling to native code,
because it's trivial for a C compiler to optimize, but it does matter
for the interpreter.
2025-06-16 23:52:50 +00:00
Cameron Zwarich
9e913a29de chore: remove redundant headBeta call (#8824) 2025-06-16 23:13:07 +00:00
Cameron Zwarich
46c3eaece9 fix: add a cache for constructor info in toIR (#8822)
This PR adds a cache for constructor info in toIR. This is called for
all constructors, projections, and cases alternatives, so it makes sense
to cache.
2025-06-16 22:56:27 +00:00
Joachim Breitner
47c294b3a9 chore: make Linux Release CI job secondary (#8818)
Follow-up to #8817.
2025-06-16 21:29:07 +00:00
Joachim Breitner
5568e06160 chore: Run “Linux release” for PRs as secondary job (#8817)
This PR reifnes #8739 to make sure we get a linux release in the PR
release.
2025-06-16 20:45:10 +00:00
Parth Shastri
ed4195778d chore: remove unused syntax (#8760)
Removes unused `tailrecursion` syntax.
2025-06-16 20:36:56 +00:00
Cameron Zwarich
997892d49a fix: constant fold Char.ofNat in LCNF simp (#8816)
This PR adds constant folding for Char.ofNat in LCNF simp. This
implicitly relies on the representation of `Char` as `UInt32` rather
than making a separate `.char` literal type, which seems reasonable as
`Char` is erased by the trivial structure optimization in `toMono`.
2025-06-16 17:48:55 +00:00
Parth Shastri
e07ed1ae5c chore: add missing instance (#8772)
Changes `ReverseImplicationOrder.instCompleteLattice` to be an
`instance`.
2025-06-16 15:44:56 +00:00
Kim Morrison
d247297214 feat: lemmas about ordered modules (#8813)
This PR adds some basic lemmas about `grind` internal notions of
modules.
2025-06-16 13:05:38 +00:00
Sebastian Ullrich
242429a262 chore: CI: provide more than 8GB RAM (#8812)
We started running into OOMs in the test suite. This is the faster
alternative to lowering test parallelism.
2025-06-16 11:58:06 +00:00
Kim Morrison
d9b2a5e9f7 feat: additional grind annotations for List/Array/Vector lemmas (#8805)
This PR continues adding `grind` annotations for `List/Array/Vector`
lemmas.
2025-06-16 11:00:51 +00:00
Leonardo de Moura
4e96a4ff45 feat: eliminate equations in grind linarith (#8810)
This PR implements equality elimination in `grind linarith`. The current
implementation supports only `IntModule` and `IntModule` +
`NoNatZeroDivisors`
2025-06-16 09:31:13 +00:00
Kim Morrison
7b67727067 feat: do not report metaprogramming declarations via exact? and rw? (#6672)
This PR filters out all declarations from `Lean.*`, `*.Tactic.*`, and
`*.Linter.*` from the results of `exact?` and `rw?`.

---------

Co-authored-by: damiano <adomani@gmail.com>
Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-06-16 09:20:49 +00:00
David Thrane Christiansen
8ed6824b75 chore: follow up on #8173 post-stage0 update (#8722)
This PR un-does the temporary changes made in #8173 for bootstrapping
purposes.
2025-06-16 09:08:35 +00:00
Kim Morrison
fdf6d2ea3b feat: basic theory of ordered modules over Nat (#8809)
This PR introduces the basic theory of ordered modules over Nat (i.e.
without subtraction), for `grind`. We'll solve problems here by
embedding them in the `IntModule` envelope.
2025-06-16 06:46:03 +00:00
Kim Morrison
dc531a1740 feat: missing Nat lemmas (#8808)
This PR adds the missing `le_of_add_left_le {n m k : Nat} (h : k + n ≤
m) : n ≤ m` and `le_add_left_of_le {n m k : Nat} (h : n ≤ m) : n ≤ k +
m`.
2025-06-16 06:43:37 +00:00
Kim Morrison
ddff851294 chore: cleanup of grind tests (#8806) 2025-06-16 02:47:46 +00:00
Cameron Zwarich
db414957a0 chore: fix if/else indentation (#8803) 2025-06-15 23:03:52 +00:00
Kim Morrison
114fa440f0 feat: grind annotations for List.Perm (#8765)
This PR adds grind annotations for `List.Perm`; involves a revision of
grind annotations for `List.countP/count` as well.
2025-06-15 23:01:29 +00:00
Cameron Zwarich
aa988bb892 fix: prevent floatLetIn from artificially blocking code motion (#8802)
This PR fixes a bug in `floatLetIn` where if one decl (e.g. a join
point) is floated into a case arm and it uses another decl (e.g. another
join point) that does not have any other existing uses in that arm, then
the second decl does not get floated in despite this being perfectly
legal. This was causing artificial array linearity issues in
`Lean.Elab.Tactic.BVDecide.LRAT.trim.useAnalysis`.
2025-06-15 22:19:38 +00:00
Leonardo de Moura
e2a947c2e6 feat: track occurrences in linarith (#8801)
This PR implements the infrastructure for variable elimination in the
`grind linarith` procedure.
2025-06-15 18:21:50 +00:00
Leonardo de Moura
26946ddc7f feat: Inv.lean for grind linarith (#8800) 2025-06-15 17:50:43 +00:00
Cameron Zwarich
0bfd95dd20 chore: improve readability of map/fold calls (#8799) 2025-06-15 14:15:11 +00:00
Sebastian Ullrich
957b904ef9 chore: revert "fix: add terminfo for structure fields (#8568)"
This reverts commit 021c21a273 because of a stage 2 linter failure.
2025-06-15 13:39:01 +02:00
Leonardo de Moura
1835f190c7 feat: add instance IsCharP R 0 for a linear ordered field R (#8798)
This PR adds the following instance
```
instance [Field α] [LinearOrder α] [Ring.IsOrdered α] : IsCharP α 0
```
The goal is to ensure we do not perform unnecessary case-splits in our
test suite.
2025-06-15 05:04:58 +00:00
Leonardo de Moura
f86560d134 fix: grind bogus warning and missing normalization rule (#8797)
This PR adds small fixes
2025-06-15 03:44:53 +00:00
Leonardo de Moura
cc3dafe67a fix: grind linarith internalization and HSMul support (#8796)
This PR fixes `grind linarith` term internalization and support for
`HSMul`.
2025-06-15 02:34:42 +00:00
Leonardo de Moura
5bc5d31fd9 fix: grind ring + linarith internalization (#8795)
This PR ensures that auxliary terms are not internalized by the ring and
linarith modules.
2025-06-15 01:49:37 +00:00
Leonardo de Moura
ac0c59caae feat: enable linarith even if no order is available (#8791)
This PR ensures the `grind linarith` module is activated for any type
that implements only `IntModule`. That is, the type does not need to be
a preorder anymore.
2025-06-15 01:20:49 +00:00
Mac Malone
2a8cd373ca feat: respect lean --setup module name in code generation (#8780)
This PR makes Lean code generation respect the module name provided
through `lean --setup`.

This is accomplished by porting to Lean the portion of `shell.cpp` that
spans running the frontend to exiting the process. This makes it easier
to load the module setup and control how its name is passed to the code
generation functions. This port attempts to minimize the changes made to
Lean. It marks the new Lean functions `private` and tries to preserve as
faithfully as possible the behavior of the original C++ code. Exposing
the new Lean interface publicly and/or further improving the code now
that is written in Lean is left for the future.
2025-06-15 01:11:58 +00:00
Kyle Miller
ef6386b8a9 feat: Expr.collectLooseBVars (#8794)
This PR adds a module `Lean.Util.CollectLooseBVars` with a function
`Expr.collectLooseBVars` that collects the set of loose bound variables
in an expression. That is, it computes the set of all `i` such that
`e.hasLooseBVar i` is true.
2025-06-15 00:16:43 +00:00
Kyle Miller
021c21a273 fix: add terminfo for structure fields (#8568)
This PR modifies the `structure` elaborator to add local terminfo for
structure fields and explicit parent projections, enabling "go to
definition" when there are dependent fields.

Terminfo for inherited fields is still missing.
2025-06-15 00:11:47 +00:00
Cameron Zwarich
286ddf5e28 chore: fix confusing indentation (#8793) 2025-06-15 00:07:48 +00:00
Kyle Miller
97bc609e77 feat: add have forms of let_* simp lemmas (#8790)
This PR adds `have` forms of simp lemmas that will be used in a future
`have` simplifier. This depends on #8751 and future elaboration changes,
since these are meant to elaborate using `Expr.letE (nondep := true) ..`
expressions; for now they are duplicates of the `letFun_*` lemmas.
2025-06-14 23:15:10 +00:00
Kyle Miller
cdc923167e feat: add the nondep field of Expr.letE to the C++ data model (#8751)
This PR adds the `nondep` field of `Expr.letE` to the C++ data model.
Previously this field has been unused, and in followup PRs the
elaborator will use it to encode `have` expressions (non-dependent
`let`s). The kernel does not verify that `nondep` is correctly applied
during typechecking. The `letE` delaborator now prints `have`s when
`nondep` is true, though `have` still elaborates as `letFun` for now.
Breaking change: `Expr.updateLet!` is renamed to `Expr.updateLetE!`.

This PR also fixes a bug in `Expr.letFun?` and `Expr.letFunAppArgs?`
when the body is not a lambda. In any case, these functions will be
removed once the `Expr.letE (nondep := true)` encoding of `have`
expressions is complete.
2025-06-14 23:10:27 +00:00
Leonardo de Moura
1d971c8735 feat: Rabinowitsch transformation in grind (#8789)
This PR implements the Rabinowitsch transformation for `Field`
disequalities in `grind`. For example, this transformation is necessary
for solving:
```lean
example [Field α] (a : α) : a^2 = 0 → a = 0 := by
  grind
```
2025-06-14 22:22:40 +00:00
Kyle Miller
82c2c4cd51 feat: add zetaHave/letToHave simp options (#8788)
This PR adds the `zetaHave` and `letToHave` options to `simp`.
Implementations will appear in future PRs.
2025-06-14 21:26:36 +00:00
Leonardo de Moura
019ea2a74b feat: improve support for Field in grind (#8786)
This PR improves the support for fields in `grind`. New supported
examples:
```lean
example [Field α] [IsCharP α 0] (x : α) : x ≠ 0 → (4 / x)⁻¹ * ((3 * x^3) / x)^2 * ((1 / (2 * x))⁻¹)^3 = 18 * x^8 := by grind
example [Field α] (a : α) : 2 * a ≠ 0 → 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] [IsCharP α 0] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] [IsCharP α 0] (a b : α) : 2*b - a = a + b → 1 / a + 1 / (2 * a) = 3 / b := by grind
example [Field α] [NoNatZeroDivisors α] (a : α) : 1 / a + 1 / (2 * a) = 3 / (2 * a) := by grind
example [Field α] {x y z w : α} : x / y = z / w → y ≠ 0 → w ≠ 0 → x * w = z * y := by grind
example [Field α] (a : α) : a = 0 → a ≠ 1 := by grind
example [Field α] (a : α) : a = 0 → a ≠ 1 - a := by grind
```
2025-06-14 19:29:02 +00:00
Sebastian Ullrich
ec9ff12fc6 fix: meta tag can be added async (#8783) 2025-06-14 11:19:35 +00:00
Cameron Zwarich
444595878b chore: improve clarity in a match expression (#8781) 2025-06-14 00:53:12 +00:00
Cameron Zwarich
3d3aa98c83 chore: use FVarIdHashSet for the visited set in LCNF closure computation (#8779) 2025-06-13 23:39:16 +00:00
Cameron Zwarich
27080dca35 chore: use FVarIdHashSet in LCNF collectUsed (#8778) 2025-06-13 22:55:15 +00:00
Leonardo de Moura
aef4a29148 feat: Field support in grind ring (#8777)
This PR implements basic `Field` support in the commutative ring module
in `grind`. It is just division by numerals for now. Examples:
```lean
open Lean Grind

example [Field α] [IsCharP α 0] (a b c : α) : a/3 = b → c = a/3 → a/2 + a/2 = b + 2*c  := by
  grind

example [Field α] (a b : α) : b = 0 → (a + a) / 0 = b := by
  grind

example [Field α] [IsCharP α 3] (a b : α) : a/3 = b → b = 0 := by
  grind

example [Field α] [IsCharP α 7] (a b c : α) : a/3 = b → c = a/3 → a/2 + a/2 = b + 2*c + 7 := by
  grind

example [Field R] [IsCharP R 0] (x : R) (cos : R → R) :
    (cos x ^ 2 + (2 * cos x ^ 2 - 1) ^ 2 + (4 * cos x ^ 3 - 3 * cos x) ^ 2 - 1) / 4 =
      cos x * (cos x ^ 2 - 1 / 2) * (4 * cos x ^ 3 - 3 * cos x) := by
  grind
```
2025-06-13 22:42:49 +00:00
Parth Shastri
5d50433e6a fix: allow arbitrary sorts in structural recursion over reflexive inductive types (#7639)
This PR changes the generated `below` and `brecOn` implementations for
reflexive inductive types to support motives in `Sort u` rather than
`Type u`.

Closes #7638
2025-06-13 21:51:09 +00:00
Rob23oba
812bab6910 chore: convert ExtDHashMap into a one-field structure (#8770) 2025-06-13 20:22:20 +00:00
Leonardo de Moura
ff6eb56f5c fix: natCast in grind cutsat (#8776)
This PR ensures that user provided `natCast` application are properly
internalized in the grind cutsat module.
2025-06-13 17:56:00 +00:00
Leonardo de Moura
4b7ea26d91 fix: add grind normalization theorem for Int.negSucc (#8775)
This PR adds a `grind` normalization theorem for `Int.negSucc`. Example:

```lean
example (p : Int) (n : Nat) (hmp : Int.negSucc (n + 1) + 1 = p)
    (hnm : Int.negSucc (n + 1 + 1) + 1 = Int.negSucc (n + 1)) : p = Int.negSucc n := by
  grind
```
2025-06-13 16:53:42 +00:00
Leonardo de Moura
32eedc2c22 feat: grind -cutsat (#8774)
This PR adds an option for disabling the cutsat procedure in `grind`.
The linarith module takes over linear integer/nat constraints. Example:

```lean
set_option trace.grind.cutsat.assert true in -- cutsat should **not** process the following constraints
example (x y z : Int) (h1 : 2 * x < 3 * y) (h2 : -4 * x + 2 * z < 0) : ¬ 12*y - 4* z < 0 := by
  grind -cutsat -- `linarith` module solves it
```
2025-06-13 16:40:44 +00:00
Leonardo de Moura
95e532a536 feat: heterogeneous (k : Nat) * (a : R) support in grind linarith (#8773)
This PR implements support for the heterogeneous `(k : Nat) * (a : R)`
in ordered modules. Example:
```lean
variable (R : Type u) [IntModule R] [LinearOrder R] [IntModule.IsOrdered R]

example (x y z : R) (hx : x ≤ 3 * y) (h2 : y ≤ 2 * z) (h3 : x ≥ 6 * z) : x = 3 * y := by
  grind

example (x y z : Int) (h1 : 2 * x < 3 * y) (h2 : -4 * x + 2 * z < 0) (h3 : x * y < 5) : ¬ 12*y - 4* z < 0 := by
  grind
```
2025-06-13 16:18:24 +00:00
plp127
cceabbbe7e fix: quoting single quote Char (''') (#8742)
This PR fixes a bug where the single-quote character `Char.ofNat 39`
would delaborate as `'''`, which causes a parse error if pasted back in
to the source code.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
2025-06-13 15:29:11 +00:00
Cameron Zwarich
8019c6cc32 chore: add .dSYM files (Mac debug symbols) to tests .gitignore files (#8771) 2025-06-13 15:27:46 +00:00
Parth Shastri
5390cdbee1 fix: correctly handle explicit monotonicity proofs in mutual definitions (#8763)
This PR corrects the handling of explicit `monotonicity` proofs for
mutual `partial_fixpoint` definitions.
2025-06-13 15:04:13 +00:00
Rob23oba
e713232623 fix: resolve symbolic links through IO.FS.realPath on windows (#8534)
This PR fixes `IO.FS.realPath` on windows to take symbolic links into
account.

Closes #810
2025-06-13 13:16:13 +00:00
Justin King
0d0da768d8 perf: update free_sized declaration to be compatible with glibc (#8661)
glibc adds `__attribute__((nothrow))` to its declarations, at least for
those related to malloc. glibc has yet to introduce `free_sized`, but
when it does it would cause compilation errors. This is due to the fact
that if a function declarations has `__attribute__((nothrow))` and it is
re-declared or implemented in C++ it must also have
`__attribute__((nothrow))` or `noexcept`, otherwise the compilation will
fail.

This is a follow up to https://github.com/leanprover/lean4/pull/6598.

Signed-off-by: Justin King <jcking@google.com>
2025-06-13 13:13:00 +00:00
Sebastian Ullrich
3feb63231e chore: merge-checkout test fixed and removed from exclusions on master 2025-06-13 15:30:58 +02:00
Sebastian Ullrich
121ce56506 chore: CI: make "Linux Lake" primary PR CI job (#8739)
Comes with .olean caching and module system-powered short-circuiting
2025-06-13 11:27:49 +00:00
Luisa Cicolini
300c22a4e6 feat: associativity lemmas for BitVec.(umul, smul, uadd, sadd)Overflow (#8740)
This PR introduces associativity rules and preservation of `(umul, smul,
uadd, sadd)Overflow`flags.

---------

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-13 09:07:09 +00:00
Cameron Zwarich
f247f2bdd0 fix: run LCNF checks less often by default (#8764)
This PR changes the LCNF pass pipeline so checks are no longer run by
default after every pass, only after `init`, `saveBase`, `toMono` and
`saveMono`. This is a compile time improvement, and the utility of these
checks is decreased a bit after the decision to no longer attempt to
preserve types throughout compilation. They have not been a significant
way to discover issues during development of the new compiler.
2025-06-13 05:39:21 +00:00
Kim Morrison
db5bd5a205 chore: missing easy Int lemmas (#8762) 2025-06-13 04:20:47 +00:00
Leonardo de Moura
140a633589 feat: model based theory combination for grind mbtc (#8759)
This PR implements model-based theory combination for grind linarith.
Example:
```lean
example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (f : α → α → α) (x y z : α)
    : z ≤ x → x ≤ 1 → z = 1 → f x y = 2 → f 1 y = 2 := by
  grind
```
2025-06-13 01:20:45 +00:00
Cameron Zwarich
3aa479fd8c fix: cache TrivialStructureInfo in LCNF toMono (#8758)
This PR adds caching for the `hasTrivialStructure?` function for LCNF
types. This is one of the hottest small functions in the new compiler,
so adding a cache makes a lot of sense.
2025-06-13 01:07:38 +00:00
Kim Morrison
b280b83c98 chore: add test case with bad grind pattern (#8757) 2025-06-13 01:06:02 +00:00
Kyle Miller
84f15ac93a fix: refine how simp tracks unfolded local definitions (#8753)
This PR fixes a bug in `simp` where it was not resetting the set of
zeta-delta reduced let definitions between `simp` calls. It also fixes a
bug where `simp` would report zeta-delta reduced let definitions that
weren't given as simp arguments (these extraneous let definitions appear
due to certain processes temporarily setting `zetaDelta := true`). This
PR also modifies the metaprogramming interface for the zeta-delta
tracking functions to be re-entrant and to prevent this kind of no-reset
bug from occurring again. Closes #6655.

Re-entrance of this metaprogramming interface is not needed to fix
#6655, but it is needed for some future PRs.

The `tests/lean/run/6655.lean` file has an example of a deficiency of
`simp?`, where `simp?` still over-reports unfolded let declarations.
This is likely due to `withInferTypeConfig` setting `zetaDelta := true`
from within `isDefEq`, but I did not verify this.

This PR supersedes #7539. The difference is that this PR has
`withResetZetaDeltaFVarIds` save and restore `zetaDeltaFVarIds`, but
that PR saves and then extends `zetaDeltaFVarIds` to persist unfolded
fvars. The behavior in this PR lets metaprograms control whether they
want to persist any of the unfolded fvars in this context themselves. In
practice, metaprograms that use `withResetZetaDeltaFVarIds` are creating
many temporary fvars and are doing dependence computations. These
temporary fvars shouldn't be persisted, and also dependence shouldn't be
inferred from the fact that a dependence calculation was done. (Concrete
example: the let-to-have transformation in an upcoming PR can be run
from within simp. Just because let-to-have unfolds an fvar while
calculating dependencies of lets doesn't mean that this fvar should be
included by `simp?`.)
2025-06-13 00:57:57 +00:00
Leonardo de Moura
d4b17b9fd2 feat: counterexamples for grind linarith module (#8756)
This PR implements counterexamples for grind linarith. Example:
```lean
example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (a b c d : α)
    : b ≥ 0 → c > b → d > b → a ≠ b + c → a > b + c → a < b + d →  False := by
  grind
```
produces the counterexample
```
a := 7/2
b := 1
c := 2
d := 3
```

```lean
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
    : a ≤ b → a - c ≥ 0 + d → d ≤ 0 → b = c → a ≠ b → False := by
  grind
```
generates the counterexample
```
a := 0
b := 1
c := 1
d := -1
```
2025-06-13 00:21:35 +00:00
Cameron Zwarich
4694aaad02 chore: rewrite mkFieldParamsForCtorType in a more readable style (#8755) 2025-06-12 23:54:30 +00:00
Rob23oba
e450a02621 fix: change show tactic to work as documented (#7395)
This PR changes the `show t` tactic to match its documentation.
Previously it was a synonym for `change t`, but now it finds the first
goal that unifies with the term `t` and moves it to the front of the
goal list.
2025-06-12 23:54:09 +00:00
Cameron Zwarich
deda28e6e3 fix: enable more optimizations on inductives with computed fields in the new compiler (#8754)
This PR changes the implementation of computed fields in the new
compiler, which should enable more optimizations (and remove a
questionable hack in `toLCNF` that was only suitable for bringup). We
convert `casesOn` to `cases` like we do for other inductive types, all
constructors get replaced by their real implementations late in the base
phase, and then the `cases` expression is rewritten to use the real
constructors in `toMono`.

In the future, it might be better to move to a model where the `cases`
expression gets rewritten earlier or the constructors get replaced
later, so that both are done at the same time.
2025-06-12 23:28:09 +00:00
Cameron Zwarich
8aa003bdfc fix: move structProjCases pass before extendJoinPointContext (#8752)
This PR fixes an issue where the `extendJoinPointContext` pass can lift
join points containing projections to the top level, as siblings of
`cases` constructs matching on other projections of the same base value.
This prevents the `structProjCases` pass from projecting both at once,
extending the lifetime of the parent value and breaking linearity at
runtime.

This would theoretically be possible to fix in `structProjCases`, but it
would require some better infrastructure for handling join points. It's
also likely that the IR passes dealing with reference counting would
have similar bugs that pessimize the code. For this reason, the simplest
thing is to just perform the `structProjCases` pass earlier, which
prevents `extendJoinPointContext` from lifting these join points.
2025-06-12 21:52:02 +00:00
Kim Morrison
6a698c1c22 feat: grind annotations for List/Array/Vector.zip functions (#8750)
This PR adds grind annotations for the
`List/Array/Vector.zipWith/zipWithAll/unzip` functions.
2025-06-12 18:41:24 +00:00
Kim Morrison
b4660c96a9 feat: grind annotations for List/Array/Vector.ofFn theorems and List.Impl (#8749)
This PR adds grind annotations for `List/Array/Vector.ofFn` theorems and
additional `List.Impl` find operations.

The annotations are added to theorems that correspond to those already
annotated in the List implementation, ensuring consistency across all
three container types (List, Array, Vector) for ofFn operations and
related functionality.

Key theorems annotated include:
- Element access theorems (`getElem_ofFn`, `getElem?_ofFn`)
- Construction and conversion theorems (`ofFn_zero`, `toList_ofFn`,
`toArray_ofFn`)
- Membership theorems (`mem_ofFn`)
- Head/tail operations (`back_ofFn`)
- Monadic operations (`ofFnM_zero`, `toList_ofFnM`, `toArray_ofFnM`,
`idRun_ofFnM`)
- List.Impl find operations (`find?_singleton`, `find?_append`,
`findSome?_singleton`, `findSome?_append`)
2025-06-12 18:09:08 +00:00
Kim Morrison
2cddf2394b feat: grind annotations for List/Array/Vector.mapIdx theorems (#8748)
This PR adds grind annotations for `Array/Vector.mapIdx` and `mapFinIdx`
theorems.

The annotations are added to theorems that correspond to those already
annotated in the List implementation, ensuring consistency across all
three container types (List, Array, Vector) for indexed mapping
operations.

Key theorems annotated include:
- Size and element access theorems (`size_mapIdx`, `getElem_mapIdx`,
`getElem?_mapIdx`)
- Construction theorems (`mapIdx_empty`, `mapIdx_push`, `mapIdx_append`)
- Membership and equality theorems (`mem_mapIdx`, `mapIdx_mapIdx`)
- Conversion theorems (`toList_mapIdx`, `mapIdx_toArray`, etc.)
- Reverse and composition operations
- Similar annotations for `mapFinIdx` variants
2025-06-12 18:06:01 +00:00
Kim Morrison
75fe50a33e feat: grind annotations for List/Array/Vector.finRange theorems (#8747)
This PR adds grind annotations for \`List/Array/Vector.finRange\`
theorems.
2025-06-12 17:49:58 +00:00
Sebastian Ullrich
c2876a1a6a chore: update stage0 2025-06-12 16:36:08 +02:00
Sebastian Ullrich
9f6846a343 chore: work around old compiler bug 2025-06-12 16:36:08 +02:00
Sebastian Ullrich
64e105c121 feat: meta phase restrictions 2025-06-12 16:36:08 +02:00
Kim Morrison
d10a85539a feat: grind annotations for List/Array/Vector.find?/findSome?/idxOf?/findIdx? (#8741)
This PR adds annotations for
`List/Array/Vector.find?/findSome?/idxOf?/findIdx?`.
2025-06-12 11:06:18 +00:00
Sebastian Ullrich
f0347ee719 chore: lean --stats gives number of imported bytes (#8725)
Thanks to `mmap`, startup time is not necessarily related to this
figure, but it can be used as a rough measure for that and how much data
the module depends on, i.e. the rebuild chance.

Also adds new cumulative benchmarks for this metric as well as the
number of imported constants and env ext entries.
2025-06-12 08:29:42 +00:00
Kim Morrison
faffe86334 chore: add failing grind tests from Mathlib (#8737) 2025-06-12 05:57:32 +00:00
Mac Malone
c168d06edf chore: partially revert "feat: lake: use lean --setup" (#8736)
This PR partially reverts #8024 which introduced a significant Lake
performance regression during builds. Once the cause is discovered and
fixed, a similar PR will be made to revert this.
2025-06-12 05:53:59 +00:00
Kim Morrison
abfc49d0f7 chore: cleanup of grind tests (#8735) 2025-06-12 04:42:25 +00:00
Kim Morrison
34e98c2efc feat: add Decidable (∃ i, P i) (#8734)
This PR adds the missing instance
```
instance decidableExistsFin (P : Fin n → Prop) [DecidablePred P] : Decidable (∃ i, P i)
```
2025-06-12 02:58:37 +00:00
Leonardo de Moura
e7549b5651 feat: diseq splitting and non-chronological backtracking for linarith (#8733)
This PR implements disequality splitting and non-chronological
backtracking for the `grind` linarith procedure.
```lean
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
    : a ≤ b → a - c ≥ 0 + d → d ≤ 0 → d ≥ 0 → b = c → a ≠ b → False := by
  grind
```
2025-06-12 02:49:35 +00:00
Cameron Zwarich
9f65d0251a chore: remove comments about missing functionality now implemented elsewhere (#8732) 2025-06-12 00:38:42 +00:00
Cameron Zwarich
a7af9f7d5f chore: fix a typo in a doc comment (#8731) 2025-06-11 20:41:32 +00:00
Cameron Zwarich
39cbe04946 fix: use Arg in LCNF FVarSubst rather than Expr (#8729)
This PR changes LCNF's `FVarSubst` to use `Arg` rather than `Expr`. This
enforces the requirements on substitutions, which match the requirements
on `Arg`.
2025-06-11 18:08:30 +00:00
Lean stage0 autoupdater
77fd1ba6b9 chore: update stage0 2025-06-11 16:51:07 +00:00
jrr6
0002ea8a37 feat: pre-stage0 groundwork for named error messages (#8649)
This PR adds the pre-stage0-update infrastructure for named error
messages. It adds macro syntax for registering and throwing named errors
(without elaborators), mechanisms for displaying error names in the
Infoview and at the command line, and the ability to link to error
explanations in the manual (once they are added).
2025-06-11 14:52:08 +00:00
jrr6
7bd82b103a feat: pre-stage0 groundwork for error explanations (#8651)
This PR adds the pre-stage0-update infrastructure for error
explanations. It adds the environment-extension machinery for
registering and accessing explanations, and it provides a cursory parser
that validates that the high-level structure of error explanations
matches the prescribed format.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-06-11 14:51:44 +00:00
Sebastian Ullrich
2c9c58b1f7 fix: allow mixing modules and non-modules when root is not a module (#8724) 2025-06-11 14:39:49 +00:00
Sebastian Ullrich
54c12df950 refactor: environment extension state splitting (#8653)
Replaces the previous `export/saveEntriesFn` split with a stricly more
general function such that `exportEntriesFn` could be deprecated at a
later point. Also gives the new function access to the `Environment`
while we're at it. Also gives `getModuleEntries` access to more olean
levels in preparation for `meta import`.
2025-06-11 12:52:04 +00:00
Sebastian Ullrich
01a0524749 chore: move benchmarking script to this repo (#8718)
Corresponding to
d3f39f8343
2025-06-11 12:27:06 +00:00
Lean stage0 autoupdater
551e755d23 chore: update stage0 2025-06-11 11:06:17 +00:00
Kim Morrison
082ca94d3b feat: add grind annotations for List/Array/Vector.eraseP/erase/eraseIdx (#8719)
This PR adds grind annotations for
List/Array/Vector.eraseP/erase/eraseIdx. It also adds some missing
lemmas.
2025-06-11 09:44:47 +00:00
Rob23oba
ee5b652136 doc: add documentation for builtin attributes (#8173)
This PR adds documentation to builtin attributes like `@[refl]` or
`@[implemented_by]`.

Closes #8432

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
Co-authored-by: David Thrane Christiansen <david@lean-fro.org>
2025-06-11 09:04:37 +00:00
Marc Huisinga
91b5e19833 feat: server-side for module hierarchy (#8654)
This PR adds server-side support for a new module hierarchy component in
VS Code that can be used to navigate both the import tree of a module
and the imported-by tree of a module. Specifically, it implements new
requests `$/lean/prepareModuleHierarchy`,
`$/lean/moduleHierarchy/imports` and
`$/lean/moduleHierarchy/importedBy`. These requests are not supported by
standard LSP. Companion PR at
[leanprover/vscode-lean4#620](https://github.com/leanprover/vscode-lean4/pull/620).


![Imports](https://github.com/user-attachments/assets/5ef650e7-3b0e-4a33-9ecb-f442bff88006)
![Imported
by](https://github.com/user-attachments/assets/d98e7a2c-3c4f-4509-afdf-08134a97aa78)

### Breaking changes
This PR augments the .ilean format with the direct imports of a file in
order to implement the `$/lean/moduleHierarchy/importedBy` request and
bumps the .ilean format version.
2025-06-11 08:02:18 +00:00
Paul Reichert
cf8315ed96 fix: restrict the IteratorLoop instance on DropWhile, which was accidentally more general (#8703)
This PR corrects the `IteratorLoop` instance in `DropWhile`, which
previously triggered for arbitrary iterator types.
2025-06-11 07:35:46 +00:00
Eric Wieser
44e36dec6f feat: strengthen finIdxOf? lemmas (#8678)
This PR makes the LHS of `isSome_finIdxOf?` and `isNone_finIdxOf?` more
general.
2025-06-11 07:32:01 +00:00
Cameron Zwarich
a92890ec84 fix: use the fvar subst for erased code in LCNF simp (#8717)
This PR uses the fvar substitution mechanism to replace erased code.
This isn't entirely satisfactory, since LCNF's `.return` doesn't support
a general `Arg` (which has a `.erased` constructor), it only supports an
`FVarId`. This is in contrast to the IR `.ret`, which does support a
general `Arg`.
2025-06-11 05:46:39 +00:00
Kim Morrison
eccc472e8d chore: remove set_option grind.warning false (#8714)
This PR removes the now unnecessary `set_option grind.warning false`
statements, now that the warning is disabled by default.
2025-06-11 05:09:19 +00:00
Cameron Zwarich
d8c54fb93d fix: consider any type application of an erased term to be erased (#8716)
This PR makes any type application of an erased term to be erased. This
comes up a bit more than one would expect in the implementation of Lean
itself.
2025-06-11 04:58:21 +00:00
Leonardo de Moura
aab65f595d feat: infrastructure for disequality constraints in grind linarith (#8715)
This PR implements the basic infrastructure for processing disequalities
in the `grind linarith` module. We still have to implement backtracking.
2025-06-11 04:04:41 +00:00
Lean stage0 autoupdater
0a9c246497 chore: update stage0 2025-06-11 02:42:58 +00:00
Leonardo de Moura
2a63b392dd fix: ring module in grind (#8713)
This PR fixes a bug in the commutative ring module used in `grind`. It
was missing simplification opportunities.
2025-06-11 01:20:50 +00:00
Cameron Zwarich
0b2884bfa3 fix: erase code of an erased type in LCNF simp (#8712)
This PR optimizes let decls of an erased type to an erased value.
Specialization can create local functions that produce a Prop, and
there's no point in keeping them around.
2025-06-11 00:58:55 +00:00
Johan Commelin
c53ab2835c fix: pin version of softprops/action-gh-release (#8710)
This PR pins the precise hash of softprops/action-gh-release to

    softprops/action-gh-release@da05d55257

because the latest version is broken.
See https://github.com/softprops/action-gh-release/issues/628 for more
details.
2025-06-11 00:08:18 +00:00
Anne Baanen
54dd7aae8c chore: improvements to release checklist and scripts (#8586)
This PR improves the release checklist and scripts:

* Check that the release's commit hash is not all-numeric starting with
0 (this can break SemVer, which [required us to release
v4.21.0-rc2](https://github.com/leanprover/lean4/releases/tag/v4.21.0-rc2)).
* Check that projects being bumped to a release tag do not reference
`nightly-testing` anymore.
* Clarify how to create subsequent release candidates if an `-rc1`
already exists.
* Fix typos in the release checklist documentation.
2025-06-10 22:56:06 +00:00
euprunin
52e0742108 chore: fix spelling mistakes (#8711)
Co-authored-by: euprunin <euprunin@users.noreply.github.com>
2025-06-10 20:24:28 +00:00
Sebastian Ullrich
614e6122f7 chore: fix LEAN_PATH for building stage2+ Leanc.lean (#8705)
It would accidentally fall back to stage 1 otherwise
2025-06-10 17:11:23 +00:00
Cameron Zwarich
1a9de502f2 fix: handle constants with erased types in toMonoType (#8709)
This PR handles constants with erased types in `toMonoType`. It is much
harder to write a test case for this than you would think, because most
references to such types get replaced with `lcErased` earlier.
2025-06-10 16:27:33 +00:00
Leonardo de Moura
085c4ed3f9 fix: internalization issue in the interface between linarith and ring (#8708)
This PR fixes an internalization bug in the interface between linarith
and ring modules in `grind`. The `CommRing` module may create new terms
during normalization.
2025-06-10 16:06:47 +00:00
Rob23oba
be4ebb8ac3 feat: equivalence of tree maps (#8210)
This PR adds an equivalence relation to tree maps akin to the existing
one for hash maps. In order to get many congruence lemmas to eventually
use for defining functions on extensional tree maps, almost all of the
remaining tree map functions have also been given lemmas to relate them
to list functions, although these aren't currently used to prove lemmas
other than congruence lemmas.
2025-06-10 14:49:52 +00:00
Kim Morrison
2344e3f254 chore: minor fixes to grind_indexmap test case (#8706) 2025-06-10 11:35:48 +00:00
Anne Baanen
48f394b1d4 chore: begin development cycle for v4.22.0 (#8642)
This PR bumps the version number of the Lean project to 4.22.0, since
v4.21.0 is now in the release candidate stage.
2025-06-10 11:29:41 +00:00
Sebastian Ullrich
2629921c01 fix: import completion after meta import (#8704)
The details of `identWithPartialTrailingDot` prevent a robust approach
using quotations.
2025-06-10 09:06:58 +00:00
Marc Huisinga
e123b327a5 feat: enable auto-implicits in lake math template (#8656)
This PR enables auto-implicits in the Lake math template. This resolves
an issue where new users sometimes set up a new project for math
formalization and then quickly realize that none of the code samples in
our official books and docs that use auto-implicits work in their
projects. With the introduction of [inlay hints for
auto-implicits](https://github.com/leanprover/lean4/pull/6768), we
consider the auto-implicit UX to be sufficiently usable that they can be
enabled by default in the math template.
Notably, this change does not affect Mathlib itself, which will proceed
to disable auto-implicits.

This change was previously discussed with and agreed to by the Mathlib
maintainer team.
2025-06-10 08:08:21 +00:00
Kim Morrison
e904314742 feat: add SHA-suffixed PR release tags (#8702)
This PR enhances the PR release workflow to create both short format and
SHA-suffixed release tags. Creates both pr-release-{PR_NUMBER} and
pr-release-{PR_NUMBER}-{SHORT_SHA} tags, generates separate releases for
both formats, adds separate GitHub status checks, and updates
Batteries/Mathlib testing branches to use SHA-suffixed tags for exact
commit traceability.

This removes the need for downstream repositories to deal with the
toolchain changing without the toolchain name changing.
2025-06-10 07:09:08 +00:00
Mac Malone
0ebd320940 fix: lake: export LeanOption in Lean from Lake (#8701)
This PR exports `LeanOption` in the `Lean` namespace from the `Lake`
namespace. `LeanOption` was moved from `Lean` to `Lake` in #8447, which
can cause unnecessary breakage without this.
2025-06-10 04:09:40 +00:00
Kim Morrison
b1980ef871 chore: cleanup notes about grind in LRAT (#8623)
This PR cleans up some notes about `grind` failures in the LRAT checker,
now that some `grind` bugs have been fixed.
2025-06-10 03:47:28 +00:00
Kim Morrison
8fce30e7cb chore: change grind.warning default to false (#8698)
This PR turns off the default warning when using `grind`, in preparation
for v4.22. I'll removing all the `set_option grind.warning false` in our
codebase in a second PR, after an update-stage0.
2025-06-10 03:40:45 +00:00
Kim Morrison
308a383079 chore: fix grind annotation on DHashMap.contains_iff_mem (#8700)
The original annotations produced patterns that matched too often.
2025-06-10 03:26:54 +00:00
Leonardo de Moura
2d67524e42 feat: equality in grind linarith (#8697)
This PR implements support for inequalities in the `grind` linear
arithmetic procedure and simplifies its design. Some examples that can
already be solved:
```lean
open Lean.Grind
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (a b c d : α)
    : a + d < c → b = a + (2:Int)*d → b - d > c → False := by
  grind

example [CommRing α] [LinearOrder α] [Ring.IsOrdered α] (a b : α)
    : a = 0 → b = 1 → a + b ≤ 2 := by
  grind

example [CommRing α] [Preorder α] [Ring.IsOrdered α] (a b c d e : α) :
    2*a + b ≥ 1 → b ≥ 0 → c ≥ 0 → d ≥ 0 → e ≥ 0
    → a ≥ 3*c → c ≥ 6*e → d - e*5 ≥ 0
    → a + b + 3*c + d + 2*e < 0 → False := by
  grind
```
2025-06-09 23:39:24 +00:00
Leonardo de Moura
41c41e455a feat: One.one support in linarith (#8694)
This PR implements special support for `One.one` in linarith when the
structure is a ordered ring. It also fixes bugs during initialization.
2025-06-09 20:17:48 +00:00
Cameron Zwarich
f61a412801 fix: make unsafeBaseIO noinline (#8669)
This PR makes `unsafeBaseIO` `noinline`. The new compiler is better at
optimizing `Result`-like types, which can cause the final operation in
an `unsafeBaseIO` block to be dropped, since `unsafeBaseIO` is
discarding the state.
2025-06-09 14:48:37 +00:00
Leonardo de Moura
00f6b1e70a fix: denotation functions for interfacing CommRing and linarith (#8693)
This PR fixes the denotation functions used to interface the ring and
linarith modules in grind.
2025-06-09 14:43:13 +00:00
Sebastian Ullrich
8422d936cf chore: revert "fix LEAN_PATH for building stage2+ Leanc.lean" (#8692)
Reverts leanprover/lean4#8685 pending Windows fix
2025-06-09 08:50:34 +00:00
Leonardo de Moura
dd1d3e6a3a feat: model search procedure for grind linarith (#8690)
This PR implements the main framework of the model search procedure for
the linarith component in grind. It currently handles only inequalities.
It can already solve simple goals such as
```lean
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (a b c : α)
    : a < b → b < c → c < a → False := by
  grind

example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c : α)
    : a < b → b < c + d → a - d < c := by
  grind
```
2025-06-09 04:31:28 +00:00
Leonardo de Moura
e38b8a0a7a feat: proof terms generation for CommRing and linarith interface (#8689)
This PR implements proof term generation for the `CommRing` and
`linarith` interface. It also fixes the `CommRing` helper theorems.
2025-06-08 23:38:03 +00:00
Leonardo de Moura
3e0168df58 feat: proof term construction infrastructure for linarith in grind (#8687)
This PR implements the infrastructure for constructing proof terms in
the linarith procedure in `grind`. It also adds the `ToExpr` instances
for the reified objects.
2025-06-08 19:58:48 +00:00
Mac Malone
fcaae1dc58 feat: lake: use lean --setup (#8447)
This PR makes use of `lean --setup` in Lake builds of Lean modules and
adds Lake support for the new `.olean` artifacts produced by the module
system.

Lake now computes the entire transitive import graph of a module for
Lean, allowing it eagerly provide the artifacts managed by Lake to Lean
via the `modules` field of `lean --setup`.

`lake setup-file` no longer respects the imports passed to it and
instead just parses the file's header for imports. This is necessary
because import statements are now more complex than a simple module
name.
2025-06-08 17:42:45 +00:00
Sebastian Ullrich
8cc6a4a028 chore: fix LEAN_PATH for building stage2+ Leanc.lean (#8685)
It would accidentally fall back to stage 1 otherwise
2025-06-08 16:17:05 +00:00
Cameron Zwarich
4ec5dad05f fix: only mark single-alt cases discriminant as used if any param is used (#8683)
This PR adds an optimization to the LCNF simp pass where the
discriminant of a single-alt cases is only marked as used if any param
is used.
2025-06-08 06:20:38 +00:00
Leonardo de Moura
7e1d0cc125 feat: use CommRing to normalize linarith expressions (#8682)
This PR uses the `CommRing` module to normalize linarith inequalities.
2025-06-08 05:41:00 +00:00
Cameron Zwarich
2ae066fdc0 fix: only mark a cases discriminant used if it has non-default alt (#8681)
This PR adds an optimization to the LCNF simp pass where the
discriminant of a `cases` construct will only be mark used if it has a
non-default alternative.
2025-06-08 05:07:02 +00:00
Leonardo de Moura
c9c794ee8a feat: reification and denotation for linarith module in grind (#8680)
This PR adds the `reify?` and `denoteExpr` for the new linarith module
in `grind`.
2025-06-08 02:53:28 +00:00
Leonardo de Moura
106708ee78 feat: grind linarith module infrastructure (#8677)
This PR adds the basic infrastructure for the linarith module in
`grind`.
2025-06-08 00:19:52 +00:00
Cameron Zwarich
666fb5c571 fix: update maxHeartbeats in tests/lean/run/match_expr_perf.lean (#8676)
This PR updates `maxHeartbeats` in the match_expr_perf.lean test, since
with the new compiler this also includes the allocations made by the
compiler.
2025-06-07 23:27:16 +00:00
Cameron Zwarich
8d8fd0715f fix: increase precision of new compiler's noncomputable check (#8675)
This PR increases the precision of the new compiler's non computable
check, particularly around irrelevant uses of `noncomputable` defs in
applications.

There are no tests included because they don't pass with the old
compiler. They are on the new compiler's branch and they will be merged
when it is enabled.
2025-06-07 22:20:55 +00:00
Leonardo de Moura
4abc4430dc refactor: ENodeKey => ExprPtr (#8674) 2025-06-07 19:30:02 +00:00
Lean stage0 autoupdater
d46188de54 chore: update stage0 2025-06-07 14:27:00 +00:00
Sebastian Ullrich
de57b77feb chore: support meta in ParseImportsFast (#8672) 2025-06-07 13:08:20 +00:00
Lean stage0 autoupdater
f0eae3b879 chore: update stage0 2025-06-07 11:04:28 +00:00
Sebastian Ullrich
1abf6fe1f5 chore: do not interpret meta as noncomputable (#8668)
To be replaced by actual handling of `meta`
2025-06-07 09:45:04 +00:00
Mac Malone
f917951745 fix: lake: ensure Lake versions are SemVer compatible (#8613)
This PR changes the Lake version syntax (to `5.0.0-src+<commit>`) to
ensure it is a well-formed SemVer,
2025-06-07 07:17:06 +00:00
Mac Malone
8904e5c070 feat: lake: builtin facet memoize toggle (#7738)
This PR makes memoization of built-in facets toggleable through a
`memoize` option on the facet configuration. Built-in facets which are
essentially aliases (e.g., `default`, `o`) have had memoization
disabled.
2025-06-07 06:00:05 +00:00
Leonardo de Moura
ef9094d7f8 feat: CommRing interface for grind linarith (#8670)
This PR adds helper theorems that will be used to interface the
`CommRing` module with the linarith procedure in `grind`.
2025-06-07 00:35:14 +00:00
Lean stage0 autoupdater
d50292d31b chore: update stage0 2025-06-06 20:02:08 +00:00
Joachim Breitner
24cb133eb2 feat: explicit defeq attribute (#8419)
This PR introduces an explicit `defeq` attribute to mark theorems that
can be used by `dsimp`. The benefit of an explicit attribute over the
prior logic of looking at the proof body is that we can reliably omit
theorem bodies across module boundaries. It also helps with intra-file
parallelism.

If a theorem is syntactically defined by `:= rfl`, then the attribute is
assumed and need not given explicitly. This is a purely syntactic check
and can be fooled, e.g. if in the current namespace, `rfl` is not
actually “the” `rfl` of `Eq`. In that case, some other syntax has be
used, such as `:= (rfl)`. This is also the way to go if a theorem can be
proved by `defeq`, but one does not actually want `dsimp` to use this
fact.

The `defeq` attribute will look at the *type* of the declaration, not
the body, to check if it really holds definitionally. Because of
different reduction settings, this can sometimes go wrong. Then one
should also write `:= (rfl)`, if one does not want this to be a defeq
theorem. (If one does then this is currently not possible, but it’s
probably a bad idea anyways).

The `set_option debug.tactic.simp.checkDefEqAttr true`, `dsimp` will
warn if could not apply a lemma due to a missing `defeq` attribute.

With `set_option backward.dsimp.useDefEqAttr.get false` one can revert
to the old behavior of inferring rfl-ness based on the theorem body.

Both options will go away eventually (too bad we can’t mark them as
deprecated right away, see #7969)

Meta programs that generate theorems (e.g. equational theorems) can use
`inferDefEqAttr` to set the attribute based on the theorem body of the
just created declaration.

This builds on #8501 to update Init to `@[expose]` a fair amount of
definitions that, if not exposed, would prevent some existing `:= rfl`
theorems from being `defeq` theorems. In the interest of starting
backwards compatible, I exposed these function. Hopefully many can be
un-exposed later again.

A mathlib adaption branch exists that includes both the meta programming
fixes and changes to the theorems (e.g. changing `:= by rfl` to `:=
rfl`).

With the module system there is now no special handling for `defeq`
theorem bodies, because we don’t look at the body anymore. The previous
hack is removed. The `defeq`-ness of the theorem needs to be checked in
the context of the theorem’s *type*; the error message contains a hint
if the defeq check fails because of the exported context.
2025-06-06 18:40:06 +00:00
Henrik Böving
eddbe08118 refactor: AIG doesn't need to be modified for constants (#8663) 2025-06-06 15:32:38 +00:00
Paul Reichert
d16c4052c2 feat: introduce empty iterator (#8615)
This PR provides a special empty iterator type. Although its behavior
can be emulated with a list iterator (for example), having a special
type has the advantage of being easier to optimize for the compiler.
2025-06-06 14:26:52 +00:00
tonneaus
febad6a380 doc: typo in IO.lean (#8657) 2025-06-06 13:12:12 +00:00
Marc Huisinga
257cd15a00 fix: wrong signature help after map/filter/etc (#8655)
This PR fixes a bug in the signature help where it would be displayed
for higher-order-functions that are the last argument of another
function.
2025-06-06 13:07:01 +00:00
Paul Reichert
5963bc8b8a fix: remove IteratorLoop instances without associated LawfulIteratorLoop instances (#8629)
This PR replaces special, more optimized `IteratorLoop` instances, for
which no lawfulness proof has been made, with the verified default
implementation. The specialization of the loop/collect implementations
is low priority, but having lawfulness instances for all iterators is
important for verification.
2025-06-06 08:06:59 +00:00
Paul Reichert
ec9b00996f feat: equivalence of iterators (#8545)
This PR provides the means to reason about "equivalent" iterators.
Simply speaking, two iterators are equivalent if they behave the same as
long as consumers do not introspect their states.
2025-06-06 08:06:39 +00:00
Kim Morrison
50474fef78 chore: cleanup after renaming get_elem_tactic_trivial 2025-06-06 13:10:18 +10:00
Kim Morrison
a5567618ac chore: update stage0 2025-06-06 13:10:18 +10:00
Kim Morrison
a3caf60f6a feat: rename get_elem_tactic_trivial to get_elem_tactic_extensible 2025-06-06 13:10:17 +10:00
Leonardo de Moura
c3d31cf24b feat: helper theorems for equality detection and coefficent normalization (#8650)
This PR adds helper theorems for coefficient normalization and equality
detection. This theorems are for the linear arithmetic procedure in
`grind`.
2025-06-06 02:42:57 +00:00
Leonardo de Moura
f7ecf06234 feat: normalization and ordered IntModule helper theorems (#8645)
This PR adds many helper theorems for the future `IntModule` linear
arithmetic procedure in `grind`.
It also adds helper theorems for normalizing input atoms and support for
disequality in the new linear arithmetic procedure in `grind`.
2025-06-05 23:39:10 +00:00
Cameron Zwarich
b97d35d879 fix: improve precision of the new compiler's noncomputable check for proj (#8647)
This PR improves the precision of the new compiler's `noncomputable`
check for projections. There is no test included because while this was
reduced from Mathlib, the old compiler does not correctly handle the
reduced test case. It's not entirely clear to me if the check is passing
with the old compiler for correct reasons. A test will be added to the
new compiler's branch.
2025-06-05 22:44:02 +00:00
Kim Morrison
ebf5fbd294 feat: complete grind's ToInt framework (#8639)
This PR completes the `ToInt` family of typeclasses which `grind` will
use to embed types into the integers for `cutsat`. It contains instances
for the usual concrete data types (`Fin`, `UIntX`, `IntX`, `BitVec`),
and is extensible (e.g. for Mathlib's `PNat`).
2025-06-05 11:25:04 +00:00
Luisa Cicolini
74d8746356 feat: add BitVec.setWidth'_eq to bv_normalize (#8640)
This PR adds `BitVec.setWidth'_eq` to `bv_normalize` such that
`bv_decide` can reduce it and solve lemmas involving `setWidth'_eq`
2025-06-05 09:42:47 +00:00
Joachim Breitner
1d9dd33bec feat: #print sig (#8641)
This PR adds the `#print sig $ident` variant of the `#print` command,
which omits the body. This is useful for testing meta-code, in the
```
#guard_msgs (drop trace, all) in #print sig foo
```
idiom. The benefit over `#check` is that it shows the declaration kind,
reducibility attributes (and in the future more built-in attributes,
like `@[defeq]` in #8419). (One downside is that `#check` shows unused
function parameter names, e.g. in induction principles; this could
probably be refined.)
2025-06-05 09:02:19 +00:00
Siddharth
9b9dd8546a feat: simplify T-division into E-division when numerator is positive (#8205)
This PR adds a simp lemma that simplifies T-division where the numerator
is a `Nat` into an E-division:


```lean
@[simp] theorem ofNat_tdiv_eq_ediv {a : Nat} {b : Int} : (a : Int).tdiv b = a / b :=
   tdiv_eq_ediv_of_nonneg (by simp)
```

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
2025-06-05 06:20:49 +00:00
Siddharth
de7d43865e feat: bitvector trichotomy lemmas (#8203)
This PR adds trichotomy lemmas for unsigned and signed comparisons,
stating that only one of three cases may happen: either `x < y`, `x =
y`, or `x > y` (for both signed and unsigned comparsions). We use
explicit arguments so that users can write `rcases slt_trichotomy x y
with hlt | heq | hgt`.
2025-06-05 05:28:44 +00:00
Leonardo de Moura
3ce7dd318d feat: sort equivalence classes in grind diagnostics (#8638)
This PR improves the diagnostic information produced by `grind`. It now
sorts the equivalence classes by generation and then `Expr. lt`.
2025-06-05 04:35:59 +00:00
Leonardo de Moura
b1709d1fc1 feat: background theorems for IntModule (#8637)
This PR adds background theorems for normalizing `IntModule` expressions
using reflection.
2025-06-05 02:32:53 +00:00
Cameron Zwarich
6ebf39d0fc chore: fix formatting (#8635) 2025-06-04 22:43:45 +00:00
Cameron Zwarich
a6e2df6250 fix: don't treat types with erased constructor types as having trivial structure (#8634)
This PR makes `hasTrivialStructure?` return false for types whose
constructors have types that are erased, e.g. if they construct a
`Prop`.
2025-06-04 22:33:44 +00:00
Leonardo de Moura
e08b2a1f62 feat: track case-split source in grind (#8633)
This PR implements case-split tracking in `grind`. The information is
displayed when `grind` fails or diagnostic information is requested.
Examples:

- Failure

![image](https://github.com/user-attachments/assets/b10516c3-d205-4e08-80a4-daca195c1d8a)

- Success with `set_option diagnostics true`

![image](https://github.com/user-attachments/assets/15ee31e0-27d8-473f-a469-12b424ce6d24)
2025-06-04 16:59:36 +00:00
Sebastian Ullrich
2f4e56b5d2 chore: fixes after rebootstrap 2025-06-04 18:26:05 +02:00
Sebastian Ullrich
a487bb8d63 chore: update stage0 2025-06-04 18:26:05 +02:00
Sebastian Ullrich
8457342d33 feat: meta syntax 2025-06-04 18:26:05 +02:00
Siddharth
596e65d7df feat: AIG.relabel(Nat)_unsat_iff for AIGs with empty variable types (#8631)
This PR generalizes `Std.Sat.AIG. relabel(Nat)_unsat_iff` to allow the
AIG type to be empty. We generalize the proof, by showing that in the
case when `α` is empty, the environment doesn't matter, since all
environments `α → Bool` are isomorphic.

This showed up when reusing the AIG primitives for building a
k-induction based model checker to prove arbitrary width bitvector
identities.
2025-06-04 15:10:48 +00:00
Kim Morrison
7c76dbf6be feat: typeclasses for grind to extensibly embed types into Int (#8543)
This PR adds typeclasses for `grind` to embed types into `Int`, for
cutsat. This allows, for example, treating `Fin n`, or Mathlib's `ℕ+` in
a uniform and extensible way.

There is a primary typeclass that carries the `toInt` function, and a
description of the interval the type embeds in. There are then
individual typeclasses describing how arithmetic/order operations
interact with the embedding.
2025-06-04 13:04:19 +00:00
Lean stage0 autoupdater
6b102c91e3 chore: update stage0 2025-06-04 13:21:17 +00:00
Joachim Breitner
b9243e19be feat: make equational theorems of non-exposed defs private (#8519)
This PR makes the equational theorems of non-exposed defs private. If
the author of a module chose not to expose the body of their function,
then they likely don't want that implementation to leak through
equational theorems. Helps with #8419.

There is some amount of incidential complexity due to how `private`
works in lean, by mangling the name: lots of code paths that need now do
the right thing™ about private and non-private names, including the
whole reserved name machinery.

So this includes a number of refactorings:

* The logic for calculating an equational theorem name (or similar) is
now done by a single function, `mkEqLikeNameFor`, rather than all over
the place.

* Since the name of the equational theorem now depends on the current
context (in particular whether it’s a proper module, or a non-module
file), the forward map from declaration to equational theorem doesn’t
quite work anymore. This map is deleted; the list of equational theorems
are now always found by looking for declaration of the expected names
(`alreadyGenerated). If users define such theorems themselves (and make
it past the “do not allow reserved names to be declared”) they get to
keep both pieces.

* Because this map was deleted, mathlib’s `eqns` command can no longer
easily warn if equational lemmas have already been generated too early
(adaption branch exists). But in general I think lean could provide a
more principled way of supporting custom unfold lemmas, and ideally the
whole equational theorem machinery is just using that.

* The ReservedNamePredicate is used by `resolveExact`, so we need to
make sure that it returns the right name, including privateness. It is
not ok to just reserve both the private and non-private name but then
later in the ReservedNameAction produce just one of the two.
 
* We create `foo.def_eq` eagerly for well-founded recursion. This is
needed because we need feed in the proof of the rewriting done by
`wf_preprocess`. But if `foo.def_eq` is private in a module, then a
non-module importing it will still expect a non-private `foo.def_eq` to
exist. To patch that, we install a `copyPrivateUnfoldTheorem :
GetUnfoldEqnFn` that declares a theorem aliasing the private one. Seems
to work.
2025-06-04 11:52:08 +00:00
Kim Morrison
d6478e15c7 chore: remove slow and unnecessary @[grind] annotations (#8630) 2025-06-04 10:57:25 +00:00
Leonardo de Moura
1629440cb8 feat: improve grind diagnostics for successful case (#8625)
This PR improves the diagnostic information produced by `grind` when it
succeeds. We now include the list of case-splits performed, and the
number of application per function symbol. Example:


![image](https://github.com/user-attachments/assets/109f3f80-85a1-4368-8958-fdf56707ea7d)
2025-06-04 09:34:48 +00:00
Kim Morrison
4500a7f02b fix: remove global NatCast (Fin n) instance (#8620)
This PR removes the `NatCast (Fin n)` global instance (both the direct
instance, and the indirect one via `Lean.Grind.Semiring`), as that
instance causes causes `x < n` (for `x : Fin k`, `n : Nat`) to be
elaborated as `x < ↑n` rather than `↑x < n`, which is undesirable. Note
however that in Mathlib this happens anyway!
2025-06-04 06:58:39 +00:00
Leonardo de Moura
c12159b519 refactor: move read-only data to Grind.Context (#8624) 2025-06-04 02:50:43 +00:00
Kim Morrison
1260059a59 feat: add grind use case example IndexMap (#8622)
This PR adds a test case / use case example for `grind`, setting up the
very basics of `IndexMap`, modelled on Rust's
[`indexmap`](https://docs.rs/indexmap/latest/indexmap/). It is not
intended as a complete implementation: just enough to exercise `grind`.

(Thanks to @arthurpaulino for suggesting this as a test case.)
2025-06-04 01:33:56 +00:00
Leonardo de Moura
8165ecc1db fix: bug in the equality resolution procedure in grind (#8621)
This PR fixes a bug in the equality-resolution procedure used by
`grind`.
The procedure now performs a topological sort so that every simplified
theorem declaration is emitted **before** any place where it is
referenced.
Previously, applying equality resolution to
```lean
h : ∀ x, p x a → ∀ y, p y b → x ≠ y
```
in the example
```lean
example
  (p : Nat → Nat → Prop)
  (a b c : Nat)
  (h  : ∀ x, p x a → ∀ y, p y b → x ≠ y)
  (h₁ : p c a)
  (h₂ : p c b) :
  False := by
  grind
```
caused `grind` to produce the incorrect term
```lean
p ?y a → ∀ y, p y b → False
```
The patch eliminates this error, and the following correct simplified
theorem is generated
```lean
∀ y, p y a → p y b → False
```
2025-06-04 00:34:47 +00:00
Leonardo de Moura
344b52f999 fix: term internalization issue in grind (#8619)
This PR fixes an internalization (aka preprocessing) issue in `grind`
when applying injectivity theorems.
2025-06-04 00:13:51 +00:00
Kyle Miller
5e952598dc fix: let private names be unresolved in the pretty printer, fix shadowing bug when pp.universes is true (#8617)
This PR fixes (1) an issue where private names are not unresolved when
they are pretty printed, (2) an issue where in `pp.universes` mode names
were allowed to shadow local names, (3) an issue where in `match`
patterns constants shadowing locals wouldn't use `_root_`, and (4) an
issue where tactics might have an incorrect "try this" when
`pp.fullNames` is set. Adds more delaboration tests for name
unresolution.

It also cleans up the `delabConst` delaborator so that it uses
`unresolveNameGlobalAvoidingLocals`, rather than doing any local context
analysis itself. The `inPattern` logic has been removed; it was a
heuristic added back in #575, but it now leads to incorrect results (and
in `match` patterns, local names shadow constants in name resolution).
2025-06-03 23:37:35 +00:00
Cameron Zwarich
b9aefb4a50 feat: LCNF constant folding for Nat.nextPowerOfTwo (#8618)
This PR implements LCNF constant folding for `Nat.nextPowerOfTwo`.
2025-06-03 21:13:58 +00:00
Cameron Zwarich
9afe5ccae3 feat: LCNF constant folding for Nat.pow (#8616)
This PR adds constant folding for `Nat.pow` to the new compiler,
following the same limits as the old compiler.
2025-06-03 19:10:38 +00:00
Marc Huisinga
cb0284f98e feat: signature help (#8511)
This PR implements signature help support. When typing a function
application, editors with support for signature help will now display a
popup that designates the current (remaining) function type. This
removes the need to remember the function signature while typing the
function application, or having to constantly cycle between hovering
over the function identifier and typing the application. In VS Code, the
signature help can be triggered manually using `Ctrl+Shift+Space`.


![Demo](https://github.com/user-attachments/assets/d1f6ed79-bb16-4593-8d28-68b1cce5d5dc)

### Other changes

- In order to support signature help for the partial syntax `f a <|` or
`f a $`, these notations now elaborate as `f a`, not `f a .missing`.
- The logic in `delabConstWithSignature` that delaborates parameters is
factored out into a function `delabForallParamsWithSignature` so that it
can be used for arbitrary `forall`s, not just constants.
- The `InfoTree` formatter is adjusted to produce output where it is
easier to identify the kind of `Info` in the `InfoTree`.
- A bug in `InfoTree.smallestInfo?` is fixed so that it doesn't panic
anymore when its predicate `p` does not ensure that both `pos?` and
`tailPos?` of the `Info` are present.
2025-06-03 17:26:33 +00:00
Cameron Zwarich
35e83066e6 feat: implement LCNF constant folding for toNat (#8614)
This PR implements constant folding for `toNat` in the new compiler,
which improves parity with the old compiler.
2025-06-03 17:12:15 +00:00
Sebastian Ullrich
ba847d41f1 chore: revise environment constant addition details (#8610)
* Move constant registration with elab env from `Lean.addDecl` to
`Lean.Environment.addDeclCore` for compatibility
* Make module system behavior independent of `Elab.async` value
2025-06-03 15:16:45 +00:00
Cameron Zwarich
f5e72d0962 feat: make guard_msgs.diff=true the default (#8596)
This PR makes `guard_msgs.diff=true` the default. The main usage of
`#guard_msgs` is for writing tests, and this makes staring at altered
test outputs considerably less tiring.
2025-06-03 15:13:15 +00:00
Sebastian Ullrich
536c87d73c chore: make test more robust 2025-06-03 16:11:09 +02:00
Sebastian Ullrich
c95e058e3c chore: fix tests after rebootstrap 2025-06-03 16:11:09 +02:00
Sebastian Ullrich
4746e38414 chore: update stage0 2025-06-03 16:11:09 +02:00
Sebastian Ullrich
f718f26200 feat: create private aux decls in private contexts 2025-06-03 15:53:05 +02:00
Marc Huisinga
184dbae130 feat: reusable rpc refs (#8105)
This PR adds support for server-sided `RpcRef` reuse and fixes a bug
where trace nodes in the InfoView would close while the file was still
being processed.

The core of the trace node issue is that the server always serves new
RPC references in every single response to the client, which means that
the client is forced to reset its UI state.

In a previous attempt at fixing this (#8056), the server would memorize
the RPC-encoded JSON of interactive diagnostics (which includes RPC
references) and serve it for as long as it could reuse the snapshot
containing the diagnostics, so that RPC references are reused. The
problem with this was that the client then had multiple finalizers
registered for the same RPC reference (one for every reused RPC
reference that was served), and once the first reference was
garbage-collected, all other reused references would point into the
void.

This PR takes a different approach to resolve the issue: The meaning of
`$/lean/rpc/release` is relaxed from "Free the object pointed to by this
RPC reference" to "Decrement the RPC reference count of the object
pointed to by this RPC reference", and the server now maintains a
reference count to track how often a given `RpcRef` was served. Only
when every single served instance of the `RpcRef` has been released, the
object is freed. Additionally, the reuse mechanism is generalized from
being only supported for interactive diagnostics, to being supported for
any object using `WithRpcRef`. In order to make use of reusable RPC
references, downstream users still need to memorize the `WithRpcRef`
instances accordingly.

Closes #8053.

### Breaking changes

Since `WithRpcRef` is now capable of tracking its identity to decide
which `WithRpcRef` usage constitutes a reuse, the constructor of
`WithRpcRef` has been made `private` to discourage downstream users from
creating `WithRpcRef` instances with manually-set `id`s. Instead,
`WithRpcRef.mk` (which lives in `BaseIO`) is now the preferred way to
create `WithRpcRef` instances.
2025-06-03 12:35:12 +00:00
Kim Morrison
bc47aa180b feat: use grind to shorten some proofs in the LRAT checker (#8609)
This PR uses `grind` to shorten some proofs in the LRAT checker. The
intention is not particularly to improve the quality or maintainability
of these proofs (although hopefully this is a side effect), but just to
give `grind` a work out.

There are a number of remaining notes, either about places where `grind`
fails with an internal error (for which #8608 is hopefully
representative, and we can fix after that), or `omega` works but `grind`
doesn't (to be investigated later).

Only in some of the files have I thoroughly used grind. In many files
I've just replaced leaves or branches of proofs with `grind` where it
worked easily, without setting up the internal annotations in the LRAT
library required to optimize the use of `grind`. It's diminishing
returns to do this in a proof library that is not high priority, so I've
simply drawn a line.
2025-06-03 08:38:57 +00:00
Kim Morrison
f7b6e155d4 chore: add failing grind test (#8608) 2025-06-03 07:45:38 +00:00
Kim Morrison
f4e86e310c chore: add failing grind test (unknown metavariable) (#8607) 2025-06-03 07:00:56 +00:00
Kim Morrison
5f0bdfcada chore: initial @[grind] annotations for Array/Vector.range (#8606) 2025-06-03 06:44:01 +00:00
Kim Morrison
0f4459b42c chore: add @[grind] annotations to Fin.getElem_fin (#8605) 2025-06-03 06:37:35 +00:00
Paul Reichert
55b89aaf38 feat: introduce drop iterator combinator (#8420)
This PR provides the iterator combinator `drop` that transforms any
iterator into one that drops the first `n` elements.

Additionally, the PR removes the specialized `IteratorLoop` instance on
`Take`. It currently does not have a `LawfulIteratorLoop` instance,
which needs to exist for the loop consumer lemmas to work. Having the
specialized instance is low priority.
2025-06-03 06:37:09 +00:00
Kim Morrison
9fc8713946 chore: grind annotations for getElem?_pos and variants (#8590)
This PR adds `@[grind]` to `getElem?_pos` and variants.

I'd initially thought these would result in too much case splitting, but
it seems to be only minor, and in use cases the payoff is good.
2025-06-03 06:17:05 +00:00
Cameron Zwarich
106411420b fix: support compiler.extract_closed option in the new compiler (#8604)
This PR adds support for the `compiler.extract_closed` option to the new
compiler, since this is used by the definition of `unsafeBaseIO`. We'll
revisit this once we switch to the new compiler and rethink its
relationship with IO.
2025-06-03 05:58:32 +00:00
Kim Morrison
921be93535 chore: add @[grind] to List/Array/Vector.mem_map (#8603) 2025-06-03 05:07:11 +00:00
Cameron Zwarich
63d123f4be fix: support Eq.recOn in the new compiler (#8602)
This PR adds support to the new compiler for `Eq.recOn` (which is
supported by the old compiler but missing a test).
2025-06-03 04:45:20 +00:00
Kim Morrison
7adea80123 chore: missing [@grind] annotations for List/Array.modify` (#8601) 2025-06-03 04:13:01 +00:00
Kim Morrison
310a123901 chore: grind annotations for List/Array/Vector.any/all (#8600) 2025-06-03 03:52:54 +00:00
Kim Morrison
6c17ad8954 chore: add failing grind test (#8599)
`@[grind local]` currently doesn't work as expected on theorems in
namespaces.
2025-06-03 01:49:36 +00:00
Jakob von Raumer
3452a8a2e5 feat: improve BitVec.extractLsb' lemma on appended vectors (#8585)
This PR makes the lemma `BitVec.extractLsb'_append_eq_ite` more usable
by using the "simple case" more often, and uses this simplification to
make `BitVec.extractLsb'_append_eq_of_add_lt` stronger, renaming it to
`BitVec.extractLsb'_append_eq_of_add_le`.
2025-06-02 20:11:59 +00:00
Luisa Cicolini
fcc97fe49f feat: add toInt_smod and auxilliary theorems (#8253)
This PR adds `toInt_smod` and auxilliary lemmas necessary for its proof
(`msb_intMin_umod_neg_of_msb_true`,
`msb_neg_umod_neg_of_msb_true_of_msb_true`, `toInt_dvd_toInt_iff`,
`toInt_dvd_toInt_iff_of_msb_true_msb_false`,
`toInt_dvd_toInt_iff_of_msb_false_msb_true`,
`neg_toInt_neg_umod_eq_of_msb_true_msb_true`, `toNat_pos_of_ne_zero`,
`toInt_umod_neg_add`, `toInt_sub_neg_umod` and
`BitVec.[lt_of_msb_false_of_msb_true, msb_umod_of_msb_false_of_ne_zero`,
`neg_toInt_neg]`)

co-authored with @tobiasgrosser

---------

Co-authored-by: Tobias Grosser <tobias@grosser.es>
Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: kuhnsa <151550049+salinhkuhn@users.noreply.github.com>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
2025-06-02 20:09:00 +00:00
Cameron Zwarich
af365238a1 fix: wrap the new compiler in withoutExporting (#8595)
This PR wraps the invocation of the new compiler in `withoutExporting`.
This is not necessary for the old compiler because it uses more direct
access to the kernel environment.
2025-06-02 16:57:10 +00:00
Cameron Zwarich
3ccc9ca7ac fix: remove incorrect strictOr/strictAnd optimizations (#8594)
This PR removes incorrect optimizations for strictOr/strictAnd from the
old compiler, along with deleting an incorrect test. In order to do
these optimizations correctly, nontermination analysis is required.
Arguably, the correct way to express these optimizations is by exposing
the implementation of strictOr/strictAnd to a nontermination-aware phase
of the compiler, and then having them follow from more general
transformations.
2025-06-02 16:14:56 +00:00
Cameron Zwarich
b73a67a635 chore: use HashMap in ToMonoM.State.noncomputableVars (#8592) 2025-06-02 15:08:51 +00:00
Kim Morrison
9a3228ef88 chore: adjustments to grind lemmas for List.Pairwise (#8588) 2025-06-02 13:19:21 +00:00
Kim Morrison
b0963938d4 chore: initial grind annotations for List.erase (#8589) 2025-06-02 12:56:09 +00:00
Kim Morrison
47b353f155 chore: adjust HashMap grind lemmas (#8587)
This PR adjusts the grind annotation on
`Std.HashMap.map_fst_toList_eq_keys` and variants, so `grind` can reason
bidirectionally between `m.keys` and `m.toList`.
2025-06-02 12:50:21 +00:00
Sebastian Ullrich
add3e1ae12 fix: IO.FS.removeDirAll should not follow symlinks (#8573)
This PR avoids the likely unexpected behavior of `removeDirAll` to
delete through symlinks and adds the new function
`IO.FS.symlinkMetadata`.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-06-02 08:44:17 +00:00
Sebastian Ullrich
569e46033b feat: do not export private declarations (#8337)
This PR adjusts the experimental module system to not export any private
declarations from modules.

Fixes #5002
2025-06-02 08:01:08 +00:00
Sebastian Ullrich
5023b40576 chore: CI: fix cache (#8579)
* include .olean variants
* include SHA in key on push as well
2025-06-02 08:00:42 +00:00
Sebastien Gouezel
3516143aed doc: use notMem instead of not_mem in recommended_spelling (#8496)
This PR changes the recommended spelling from `not_mem` to `notMem`, to
reflect the decision that has been made in mathlib.

It does *not* change the name of any core lemma.

See Zulip discussion at [#mathlib4 > Naming: nmem vs not_mem @
💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Naming.3A.20nmem.20vs.20not_mem/near/520315224)
2025-06-02 06:46:36 +00:00
Cameron Zwarich
0339cd2836 fix: don't drop state during update in Param.toMono (#8582)
This PR fixes an accidental dropping of state in Param.toMono. When this
code was originally written, there was no other state besides
`typeParams`.
2025-06-02 05:28:27 +00:00
Cameron Zwarich
bae336da87 chore: make ToMonoM.State.typeParams an FVarIdHashSet rather than an FVarIdSet (#8581) 2025-06-02 05:07:57 +00:00
dependabot[bot]
e7b24479ed chore: CI: bump dawidd6/action-download-artifact from 9 to 10 (#8578)
Bumps
[dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact)
from 9 to 10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dawidd6/action-download-artifact/releases">dawidd6/action-download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v10</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix the download-commit test to actually look for a commit by <a
href="https://github.com/mstorsjo"><code>@​mstorsjo</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/330">dawidd6/action-download-artifact#330</a></li>
<li>Add the option &quot;ref&quot;, specifying either a commit or a
branch by <a
href="https://github.com/mstorsjo"><code>@​mstorsjo</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/329">dawidd6/action-download-artifact#329</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/mstorsjo"><code>@​mstorsjo</code></a>
made their first contribution in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/330">dawidd6/action-download-artifact#330</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dawidd6/action-download-artifact/compare/v9...v10">https://github.com/dawidd6/action-download-artifact/compare/v9...v10</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4c1e823582"><code>4c1e823</code></a>
Add the option &quot;ref&quot;, specifying either a commit or a branch
(<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/329">#329</a>)</li>
<li><a
href="a708c3c648"><code>a708c3c</code></a>
Fix the download-commit test to actually look for a commit (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/330">#330</a>)</li>
<li><a
href="19f6be5f04"><code>19f6be5</code></a>
Update README.md</li>
<li>See full diff in <a
href="https://github.com/dawidd6/action-download-artifact/compare/v9...v10">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dawidd6/action-download-artifact&package-manager=github_actions&previous-version=9&new-version=10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 21:37:06 +00:00
Leonardo de Moura
193f59aefe feat: grind +ring by default (#8576)
This PR sets `ring := true` by default in `grind`. It also fixes a bug
in the reification procedure, and improves the term internalization in
the ring and cutsat modules.
2025-06-01 17:46:49 +00:00
Cameron Zwarich
c681cccf1d fix: make simpAppApp? actually bail out on trivial aliases as intended (#8575)
This PR makes LCNF's simpAppApp? bail out on trivial aliases as
intended. It seems that there was a typo in the original logic, and this
PR also extends it to include aliases of global constants rather than
just local vars.
2025-06-01 16:03:17 +00:00
user202729
c6cad5fcff doc: fix a typo in ULift's documentation (#8571)
Just a typo. From my understanding (and the specification otherwise) the
resulting level is the maximum of `r` and `s` instead of the minimum.

No issue opened yet (thus the draft).
2025-06-01 06:25:52 +00:00
Leonardo de Moura
bb6d1e000b feat: generalized Option theorems for grind (#8572)
This PR adds some generalized `Option` theorems for `grind` . The avoid
`casts` operations during E-matching.
2025-06-01 06:25:37 +00:00
Lean stage0 autoupdater
abcfa708f2 chore: update stage0 2025-06-01 05:51:10 +00:00
Mac Malone
ed705306ae fix: invalid field notation error for mvar (#8259)
This PR clarifies the invalid field notation error when projected value
type is a metavariable.

Co-authored-by @sgraf812.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-06-01 03:02:04 +00:00
Mac Malone
e618a0a4f5 fix: invalid dotted identifier notation error for sort (#8260)
This PR clarifies the invalid dotted identifier notation error when the
type is a sort.

Co-authored-by @sgraf812.

---------

Co-authored-by: Joseph Rotella <7482866+jrr6@users.noreply.github.com>
2025-06-01 03:00:46 +00:00
Leonardo de Moura
db353ab964 fix: ematch generalized patterns (#8570)
This PR fixes some issues in the E-matching generalized pattern support
after the update stage0.
2025-06-01 02:38:29 +00:00
Leonardo de Moura
157ca5a4f3 feat: ematch generalized patterns (#8569)
This PR adds support for generalized E-match patterns to arbitrary
theorems.
2025-05-31 19:08:33 -07:00
jrr6
43aec5b254 fix: improve error-message hint rendering and API (#8486)
This PR improves the rendering of hints in error messages by
consistently indenting diffs and splitting large diffs less granularly;
it also improves the ergonomics of `Lean.MessageData.hint`. Note that
the changes to the signature of `Lean.MessageData.hint` are breaking.

This PR depends on #8457.
2025-06-01 01:22:09 +00:00
Leonardo de Moura
f6c83f3dce chore: adjust test (#8567)
It is working now
2025-06-01 00:21:23 +00:00
Kyle Miller
502380e1f0 fix: record fvar alias info for generalized variables in induction/cases (#8002)
This PR fixes an issue where "go to definition" for variables
generalized by the `induction` and `cases` tactic did not work. Closes
#2873.
2025-05-31 22:27:44 +00:00
Cameron Zwarich
936eb3d62e fix: don't convert Nat multiplication by 2^n to a left shift (#8566)
This PR changes the LCNF constant folding pass to not convert Nat
multiplication to a left shift by a power of 2. The fast path test for
this is sufficiently complex that it's simpler to just use the fast path
for multiplication.
2025-05-31 21:36:55 +00:00
Cameron Zwarich
0c43efc2c9 fix: only treat type/instance params as ground vars in specialization (#8565)
This PR makes the LCNF specialization pass only treat type/instance
params as ground vars. The current policy was too liberal and would
result on computations being floated into specialized loops.
2025-05-31 21:18:24 +00:00
Leonardo de Moura
2c8ee4f29c fix: simplify interface between grind core and cutsat (#8564)
This PR simplifies the interface between the `grind` core and the cutsat
procedure. Before this PR, core would try to minimize the number of
numeric literals that have to be internalized in cutsat. This
optimization was buggy (see `grind_cutsat_zero.lean` test), and produced
counterintuitive counterexamples.
2025-05-31 16:28:31 +00:00
Leonardo de Moura
0988db9ab2 refactor: simplify inferface between core and offset module (#8562)
`processNewEqLit` optimization is not worth the extra complexity.
2025-05-31 15:16:29 +00:00
Cameron Zwarich
adc7b1ed87 fix: increase maxHeartbeats in isDefEqProjIssue test for the new compiler (#8561)
This PR increases maxHeartbeats in the isDefEqProjIssue test, because
when running under the new compiler the `run_meta` call includes the
allocations of the compiler itself. With the old compiler, many of the
corresponding allocations were internal to C++ code and would not
increase the heartbeat count.
2025-05-31 04:56:29 +00:00
Leonardo de Moura
837193b5ec fix: block potential adversarial exploit of non-aborting assert! (#8560)
This PR is similar to #8559 but for `Expr.mkData`. This vulnerability
has not been exploited yet, but adversarial users may find a way.
2025-05-31 03:14:01 +00:00
Leonardo de Moura
6940d2c4ff fix: block adversarial exploit of non-aborting assert! (#8559)
This PR fixes an adversarial soundness attack described in #8554. The
attack exploits the fact that `assert!` no longer aborts execution, and
that users can redirect error messages.
Another PR will implement the same fix for `Expr.Data`.
2025-05-31 00:08:30 +00:00
Paul Reichert
ed4252f8c9 feat: array iterators, repeat/unfold, ForM for iterators (#8552)
This PR provides array iterators (`Array.iter(M)`,
`Array.iterFromIdx(M)`), infinite iterators produced by a step function
(`Iter.repeat`), and a `ForM` instance for finite iterators that is
implemented in terms of `ForIn`.
2025-05-30 18:17:53 +00:00
Leonardo de Moura
8883ca0965 chore: move test (#8550)
It is working now.
2025-05-30 17:13:38 +00:00
Leonardo de Moura
999fcd2d95 fix: hash function for congruence closure in grind (#8549)
This PR fixes the hash function used to implement congruence closure in
`grind`. The hash of an `Expr` must not depend on whether the expression
has been internalized or not.
2025-05-30 17:07:26 +00:00
Paul Reichert
a8ab3f230c feat: introduce iterator combinators takeWhile and dropWhile (#8493)
This PR provides the iterator combinators `takeWhile` (forwarding all
emitted values of another iterator until a predicate becomes false)
`dropWhile` (dropping values until some predicate on these values
becomes false, then forwarding all the others).
2025-05-30 16:35:40 +00:00
Paul Reichert
4f77e05225 feat: introduce zip iterator combinator (#8484)
This PR provides the iterator combinator `zip` in a pure and monadic
version.
2025-05-30 15:20:28 +00:00
Paul Reichert
90462e2551 feat: introduce iterator combinators filterMap, filter and map (#8451)
This PR provides the iterator combinator `filterMap` in a pure and
monadic version and specializations `map` and `filter`. This new
combinator allows to apply a function to the emitted values of a stream
while filtering out certain elements.

`map` should have an optimized `IteratorCollect` implementation but it
turns out that this is not possible without a major refactor of
`IteratorCollect`: `toArrayMapped` requires a proof that the iterator is
finite. If `it.mapM f` is `Finite` but `it` is not, then such a proof
does not exist. `IteratorCollect` needs to take a proof that the loop
will terminate for the given monadic function `f` instead. This will not
be done in this PR.
2025-05-30 13:43:41 +00:00
Paul Reichert
a12f89aefa feat: introduce take iterator combinator (#8418)
This PR provides the `take` iterator combinator that transforms any
iterator into an iterator that stops after a given number of steps. The
change contains the implementation and lemmas.

`take` has a special implementation of `IteratorLoop` that relies on a
potentially more efficient `forIn` implementation of the inner iterator.

The mysterious `@[specialize]` on a test has been removed because it is
not necessary anymore according to a manual inspection of the IR. Either
I erroneously concluded from experiments that it was necessary of
something has changed in the meantime that makes it unnecessary.
2025-05-30 10:34:12 +00:00
Paul Reichert
2d5e8ca311 feat: upstream LawfulMonadLift(T) from Batteries (#8435)
This PR upstreams the `LawfulMonadLift(T)` classes, lemmas and instances
from Batteries into Core because the iterator library needs them in
order to prove lemmas about the `mapM` operator, which relies on
`MonadLiftT`.
2025-05-30 09:14:01 +00:00
Paul Reichert
d60cb88e62 feat: ForIn, fold(M), drain lemmas for iterators (#8405)
This PR provides lemmas about the loop constructs `ForIn`, `fold`,
`foldM` and `drain` and their relation to each other in the context of
iterators.
2025-05-30 09:10:31 +00:00
Leonardo de Moura
d2e01bbd09 feat: overapplied ite and dite applications in grind (#8544)
This PR implements support for over-applied `ite` and `dite`
applications in the `grind` tactic. It adds support for propagation and
case-split.
2025-05-30 06:34:04 +00:00
Leonardo de Moura
069fb4351c fix: inappropriate whnfD uses in grind (#8542)
This PR fixes two inappropriate uses of `whnfD` in `grind`. They were
potential performance foot guns, and were producing unexpected errors
since `whnfD` is not consistently used (and it should not be) in all
modules.
2025-05-30 04:35:29 +00:00
Leonardo de Moura
f54a65f72f feat: nested proof propagation in grind (#8541)
This PR ensures that for any nested proof `h : p` in a goal, we
propagate that `p` is true in the `grind` tactic.
2025-05-30 03:25:14 +00:00
Mac Malone
3817dd57bd fix: lake: precompile imports of non-workspace files by library (#8529)
This PR changes `lake lean` and `lake setup-file` to precompile the
imports of non-workspace files using the the import's whole library.
This ensures that additional link objects are linked and available
during elaboration.

Closes #8448.
2025-05-30 02:28:28 +00:00
Mac Malone
e68c6a38fb feat: lake: relative paths for Lean build messages (#8539)
This PR changes Lake to use relative path for the Lean messages produced
by a module build. This makes the message portable across different
machines, which is useful for Mathlib's cache.
2025-05-30 02:02:35 +00:00
Cameron Zwarich
b7ec369863 fix: allow ground variables to depend on fun decls in LCNF specialize pass (#8540)
This PR changes the LCNF specialize pass to allow ground variables to
depend on local fun decls (with no non-ground free variables). This
enables specialization of Monad instances that depend on local lambdas.
2025-05-30 00:45:00 +00:00
Mac Malone
3fdaf24b49 fix: lake: ensure valid use of (sync := true) (#8531)
This PR fixes some places in Lake where `(sync := true)` was incorrectly
used on code that could block, and more generally improves `(sync :;=
true)` usage.
2025-05-30 00:19:25 +00:00
Kim Morrison
77e16407e4 chore: add test case where grind causes a PANIC (#8538)
Minimized from #8518, thanks @wkrozowski!
2025-05-30 00:12:37 +00:00
Kim Morrison
efd8d149ea chore: add missing lemma for List.range 1 (#8537) 2025-05-30 00:09:51 +00:00
Leonardo de Moura
4316629119 fix: BEq support in grind (#8536)
This PR fixes the support for `LawfulBEq` and `BEq` in `grind`.
2025-05-29 23:47:40 +00:00
jrr6
020da5bffb fix: behavior of hard line breaks in Format strings (#8457)
This PR fixes an issue when including a hard line break in a `Format`
that caused subsequent (ordinary) line breaks to be erroneously
flattened to spaces.

This issue is especially important for displaying notes and hints in
error messages, as these components could appear garbled due to improper
line-break rendering.
2025-05-29 22:10:27 +00:00
Sebastian Ullrich
bc8189b61d perf: avoid Environment.find? block in addDecl (#8533) 2025-05-29 21:32:37 +00:00
Cameron Zwarich
e30303e33c fix: extract more Nats in extractClosed (#8535)
This PR extracts more Nats (and their downstream users) in extractClosed
by fixing a silly oversight in the logic.
2025-05-29 21:11:21 +00:00
Sebastian Ullrich
1879a2bafc fix: SnapshotTree.waitAll (#8532) 2025-05-29 20:12:23 +00:00
Mac Malone
3b72c7d193 fix: lake: better library plugin heuristic (#8528)
This PR fixes the heuristic Lake uses to determine whether a `lean_lib`
can be loaded via `lean --plugin` rather than `lean --load-dynlib`.
Previously, a mismatch between the single root's name and the library's
name would not be caught and cause loading to fail.
2025-05-29 17:48:05 +00:00
Kim Morrison
22d4c1d803 chore: failing grind tests (subset of #8518) (#8526)
This is a subset of tests from #8518 that are fully minimized. I'll
merge this first.

---------

Co-authored-by: Wojciech Rozowski <wojciech@lean-fro.org>
2025-05-29 11:48:19 +00:00
Kim Morrison
0fe23b7fd6 feat: initial @[grind] annotations for List.count (#8527)
This PR adds `grind` annotations for theorems about `List.countP` and
`List.count`.
2025-05-29 11:46:44 +00:00
Kim Morrison
72141b05fd chore: add failing grind test (#8524) 2025-05-29 05:59:58 +00:00
Leonardo de Moura
1fd7206f00 feat: match-expressions with congruence equation theorems (#8506)
This PR implements `match`-expressions in `grind` using `match`
congruence equations. The goal is to minimize the number of `cast`
operations that need to be inserted, and avoid `cast` over functions.
The new approach support `match`-expressions of the form `match h : ...
with ...`.
2025-05-29 02:23:26 +00:00
Cameron Zwarich
a6e76b424c fix: move the new compiler's noncomputable check into toMono (#8523)
This PR moves the new compiler's noncomputable check into toMono,
matching the recent change in the old compiler. This is mildly more
complicated because we can't throw an error at the mere use of a
constant, we need to check for a later relevant use. This is still a bit
more conservative than it could theoretically be around join points and
local functions, but it's hard to imagine that mattering in practice
(and we can easily enable it if it does).
2025-05-29 00:40:25 +00:00
Kyle Miller
4dd8648a25 feat: different syntax for new clear_value tactic (#8516)
This PR is a followup to #8449 to refine the syntax of `clear_value`.
The syntax for adding equality hypotheses before clearing values is now
`clear_value (h : x = _)`. Any expression definitionally equal to `x`
can be used in place of the underscore.

This syntax was developed in a [Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/.60clear_value.60.20syntax.20request.20for.20comments/near/520704290).
2025-05-28 22:33:35 +00:00
Cameron Zwarich
5814c1e757 fix: recursively process jmp args in LCNF.toMono (#8521)
This PR makes LCNF.toMono recursively process jmp args.
2025-05-28 20:56:03 +00:00
Kyle Miller
c3a010a938 feat: use dot notation for class parent projections (#8504)
This PR modifies the pretty printer so that dot notation is used for
class parent projections. Previously, dot notation was never used for
classes.

We still need to modify dot notation to take the method resolution order
into account when collapsing parent projections.
2025-05-28 20:34:40 +00:00
Kim Morrison
bd14e7079b fix: make Array.size not reducible (#8513)
This PR removes the `@[reducible]` annotation on `Array.size`. This is
probably best gone anyway in order to keep separation between the `List`
and `Array` APIs, but it also helps avoid uselessly instantiating
`Array` theorems when `grind` is working on `List` problems.
2025-05-28 12:37:24 +00:00
Sebastian Ullrich
f214708636 chore: update stage0 2025-05-28 14:27:31 +02:00
Sebastian Ullrich
5d7e09ddad feat: [no_expose] attribute 2025-05-28 14:26:22 +02:00
Kim Morrison
c6194e05b8 chore: remove prime from Fin.ofNat' (#8515)
This PR removes the prime from `Fin.ofNat'`: the old `Fin.ofNat` has
completed its 6 month deprecation cycle and is being removed.
2025-05-28 11:51:00 +00:00
Kim Morrison
1087ec9225 chore: remove >6 month old deprecations (#8514) 2025-05-28 11:28:03 +00:00
Kyle Miller
c5bea23a54 feat: value_of% elaborator (#8512)
This PR adds a `value_of% ident` term that elaborates to the value of
the local or global constant `ident`. This is useful for creating
definition hypotheses:
```lean
let x := ... complicated expression ...
have hx : x = value_of% x := rfl
```
2025-05-28 11:12:11 +00:00
Kim Morrison
ede085ae74 chore: add failing grind test (#8509) 2025-05-28 08:56:23 +00:00
Sebastian Ullrich
067fa83b1a chore: update stage0 2025-05-28 10:18:04 +02:00
Sebastian Ullrich
af1d8dd070 feat: := private instance syntax 2025-05-28 10:18:04 +02:00
Joachim Breitner
803dc3e687 refactor: Init: expose lots of functions (#8501)
This PR adds the `@[expose]` attribute to many functions (and changes
some theorems to be by `:= (rfl)`) in preparation for the `@[defeq]`
attribute change in #8419.
2025-05-28 07:37:54 +00:00
Kyle Miller
921ce7682e feat: use omission dots for hidden let values in Infoview (#8041)
This PR changes the behavior of `pp.showLetValues` to use a hoverable
`⋯` to hide let values. This is now false by default, and there is a new
option `pp.showLetValues.threshold` for allowing small expressions to be
shown anyway. For tactic metavariables, there is an additional option
`pp.showLetValues.tactic.threshold`, which by default is set to the
maximal value, since in tactic states local values are usually
significant.
2025-05-27 23:09:11 +00:00
Leonardo de Moura
5187cb37a9 chore: notation for HEq (#8503) 2025-05-27 19:22:57 +00:00
Cameron Zwarich
632d078a70 fix: use kernel environment to find definitions in the new compiler (#8502)
This PR changes the new compiler to use the kernel environment to find
definitions, which causes compilation to be skipped when the decl had a
kernel error (e.g. due to an unresolved metavariable). This matches the
behavior of the old compiler.

This will need to be revisited in the future when we want to make
compilation more asynchronous.
2025-05-27 16:56:00 +00:00
Luisa Cicolini
5fda4c1023 feat: BitVec.[toNat|toInt] non-overflow simp lemmas (#8492)
This PR adds `simp` lemmas for `toInt_*` and `toNat_*` with arithmetic
operation given the hypothesis of no-overflow
(`toNat_add_of_not_uaddOverflow`, `toInt_add_of_not_saddOverflow`,
`toNat_sub_of_not_usubOverflow`, `toInt_sub_of_not_ssubOverflow`,
`toInt_neg_of_not_negOverflow`, `toNat_mul_of_not_umulOverflow`,
`toInt_mul_of_not_smulOverflow`). In particular, these are `simp` since
(1) the `rhs` is strictly simpler than the `lhs` and (2) this version is
also simpler than the standard operation when the hypothesis is
available.
 
co-authored by @tobiasgrosser

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2025-05-27 15:13:43 +00:00
Kim Morrison
a4fb2eef47 feat: make Array.ofFn.go use fuel (#8499)
This PR changes the definition of `Array.ofFn.go` to use recursion on
`Nat` (rather than well-founded recursion). This resolves a problem
reported on [zulip]([#lean4 > Memory issues with &#96;Vector.ofFn&#96;.
@
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Memory.20issues.20with.20.60Vector.2EofFn.60.2E/near/520622564)).
2025-05-27 13:44:28 +00:00
Kim Morrison
87152a3fae feat: grind annotations for List.Sublist/IsInfix/IsPrefix/IsSuffix (#8497)
This PR adds preliminary grind annotations for
`List.Sublist`/`IsInfix`/`IsPrefix`/`IsSuffix`, along with test cases.
2025-05-27 12:56:43 +00:00
Tobias Grosser
ed6c78048e chore: skip OS X aarch64 CI only in merge groups (#8334)
This PR enables the build of all artifacts for custom releases, e.g.,
releases outside the main lean4 repository.

This resolves https://github.com/leanprover/lean4/issues/8333.
2025-05-27 11:51:59 +00:00
Kim Morrison
3ab60c59fe chore: missing @[grind] annotations for Array (#8495) 2025-05-27 09:56:10 +00:00
Kim Morrison
eaa1bc14ed chore: more simp lemmas for LawfulGetElem (#8470)
This PR adds `@[simp]` to `getElem_pos/neg` (similarly for `getElem!`).
These are often already simp lemmas for concrete types.
2025-05-27 09:41:22 +00:00
Rob23oba
a912652b7d fix: simp_all? and simp_all?! (#8491)
This PR fixes the behavior of `simp_all?` and `simp_all?!`, aligning
them with `simp_all` and `simp_all!` respectively.

Closes #8490
2025-05-27 07:07:12 +00:00
Kyle Miller
3af9ab64ed feat: subst tactic can substitute let values (#8450)
This PR adds a feature to the `subst` tactic so that when `x : X := v`
is a local definition, `subst x` substitutes `v` for `x` in the goal and
removes `x`. Previously the tactic would throw an error.
2025-05-27 06:06:35 +00:00
Kyle Miller
a6dd6a4656 feat: clear_value tactic (#8449)
This PR upstreams and extends the Mathlib `clear_value` tactic. Given a
local definition `x : T := v`, the tactic `clear_value x` replaces it
with a hypothesis `x : T`, or throws an error if the goal does not
depend on the value `v`. The syntax `clear_value x with h` creates a
hypothesis `h : x = v` before clearing the value of `x`. Furthermore,
`clear_value *` clears all values that can be cleared, or throws an
error if none can be cleared.
2025-05-27 01:52:08 +00:00
Kim Morrison
1e752b0a01 chore: cleanup simp lemmas, following the simpNF linter (#8481) 2025-05-26 04:13:17 +00:00
Leonardo de Moura
11f7d6da39 feat: reuse simp cache in grind (#8483)
This PR ensures `grind` reuses the `simp` cache between different calls.
Recall that `grind` uses `simp` to normalize terms during
internalization.
2025-05-26 04:10:58 +00:00
Kim Morrison
e2fc9ba92e feat: grind annotations for List.Pairwise/Nodup (#8482)
This PR adds preliminary `@[grind]` annotations for `List.Pairwise` and
`List.Nodup`.
2025-05-26 03:13:18 +00:00
Kim Morrison
c1866a7b7e chore: fix awaiting-mathlib.yml (#8480)
This PR hopefully fixes a problem from #8471, which even the most
cursory testing (by me!) should have detected.
2025-05-26 02:13:00 +00:00
Leonardo de Moura
03e905d994 feat: hash consing with alpha equivalence in grind (#8479)
This PR implements hash-consing for `grind` that takes alpha equivalence
into account.
2025-05-26 00:51:18 +00:00
Kim Morrison
383f68f806 chore: add grind_trig test case (#8476) 2025-05-26 00:03:53 +00:00
Kim Morrison
41c2ae12f3 chore: update syntax in grind_ite example (#8475) 2025-05-25 23:21:11 +00:00
Sebastian Ullrich
9982bab93e perf: Environment.find? should not block on privacy mismatch (#8472)
This PR avoids name resolution blocking on the elaboration of a
theorem's proof when looking up the theorem name.
2025-05-25 16:18:57 +00:00
Cameron Zwarich
be513656b0 fix: use a custom environment extension for LCNF decls (#8468)
This PR switches the LCNF baseExt/monoExt environment extensions to use
a custom environment extension that uses a PersistentHashMap. The
optimizer relies upon the ability to update a decl multiple times, which
does not work with SimplePersistentEnvExtension.
2025-05-25 15:11:54 +00:00
Kim Morrison
bdbb659765 chore: while awaiting-mathlib, show yellow status not red (#8471)
This PR changes the CI check when the `awaiting-mathlib` label is
present. If `breaks-mathlib` is present, it shows a red cross, but if
neither `breaks-mathlib` nor `builds-mathlib` is present it shows a
yellow circle.
2025-05-25 12:38:56 +00:00
Leonardo de Moura
2a1354b3cc chore: add seal to workaround performance issue (#8469)
This PR adds `seal` commands at `grind_ite.lean` to workaround expensive
definitionally equality tests in the canonicalizer. The new module
system will automatically hide definitions such as `HashMap.insert` and
`TreeMap.insert` which are being unfolded by the canonicalizer in this
test.
This PR also adds a `profileItM` for tracking the time spent in the
`grind` canonicalizer.
2025-05-25 00:54:30 +00:00
Leonardo de Moura
a54872f5f6 fix: preprocessLight at ensureInternalized (#8466)
This PR fixes another instance of the `grind` issue "unexpected kernel
projection term during internalization".
2025-05-24 17:13:20 +00:00
Kim Morrison
2b0b1e013f feat: further generic GetElem lemmas (#8465)
This PR adds further lemmas about `LawfulGetElem`, including marking
some with `@[grind]`.
2025-05-24 12:58:29 +00:00
Mario Carneiro
1f000feb80 chore: remove unnecessary partial in Lean.Expr (#8464)
The termination prover has gotten stronger since these definitions were
written, and now they can be proved terminating automatically. (One
definition had to be changed slightly because it wasn't actually
terminating before.)
2025-05-24 07:00:37 +00:00
Cameron Zwarich
d5060e9e66 feat: add extractClosed pass to LCNF pass list (#8462)
This PR enables the LCNF extractClosed pass by default.
2025-05-24 05:20:10 +00:00
Kim Morrison
38ca310fb7 feat: @[grind] annotations for TreeMap (#8446)
This PR adds basic `@[grind]` annotations for `TreeMap` and its
variants. Likely more annotations will be added after we've explored
some examples.
2025-05-24 04:49:54 +00:00
Kim Morrison
3dd12f85f0 feat: further @[grind] annotations for Option (#8460)
This PR adds further `@[grind]` annotations for `Option`, as follow-up
to the recent additions to the `Option` API in #8379 and #8298.

**However**, I am concurrently investigating adding `attribute [grind
cases] Option`, which will result in many (most?) of the annotations for
`Option` being removed again. In any case, I'm going to merge this
first, as if that is viable I would like to test that most/all the
lemmas now marked with `@[grind]` are still provable by `grind`.
2025-05-24 04:25:00 +00:00
Kim Morrison
0f8618f842 chore: remove @[grind] from Array.size_eq_zero_iff` (#8461) 2025-05-24 04:20:52 +00:00
Kim Morrison
acdef6e04b feat: verification of qsort via grind (#7995)
This PR adds a verification of `Array.qsort` properties, trying to use
`grind` and `fun_induction` where possible.
Currently this is in the `tests/` folder, but once `grind` is ready for
production use we will move it out into the library.

Note that the current `qsort` algorithm has quadratic behaviour on
constant lists, and needs to be adjusted. We'll only move the
verification out into the library once this has been fixed (and the
proofs adapted). These verification theorems may be commented out in the
meantime if it's urgent to fix `qsort`.

---------

Co-authored-by: Kyle Miller <kmill31415@gmail.com>
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-24 04:01:55 +00:00
Cameron Zwarich
7b80cd24a9 feat: closed term extraction in the new compiler (#8458)
This PR adds closed term extraction to the new compiler, closely
following the approach in the old compiler. In the future, we will
explore some ideas to improve upon this approach.
2025-05-24 02:40:37 +00:00
Leonardo de Moura
21846ebdf8 feat: non-chronological backtracking for grind (WIP) (#8440)
This PR implements non-chronological backtracking for the `grind`
tactic. This feature ensures that `grind` does not need to process
irrelevant branches after performing a case-split that is not relevant.
It is not just about performance, but also the size of the final proof
term. The new test demonstrates this feature in practice.
```lean
-- In the following test, the first 8 case-splits are irrelevant,
-- and non-choronological backtracking is used to avoid searching
-- (2^8 - 1) irrelevant branches
/--
trace: 
[grind.split] p8 ∨ q8, generation: 0
[grind.split] p7 ∨ q7, generation: 0
[grind.split] p6 ∨ q6, generation: 0
[grind.split] p5 ∨ q5, generation: 0
[grind.split] p4 ∨ q4, generation: 0
[grind.split] p3 ∨ q3, generation: 0
[grind.split] p2 ∨ q2, generation: 0
[grind.split] p1 ∨ q1, generation: 0
[grind.split] ¬p ∨ ¬q, generation: 0
-/
#guard_msgs (trace) in
set_option trace.grind.split true in
theorem ex
    : p ∨ q →
      ¬ p ∨ q →
      p ∨ ¬ q →
      ¬ p ∨ ¬ q →
      p1 ∨ q1 →
      p2 ∨ q2 →
      p3 ∨ q3 →
      p4 ∨ q4 →
      p5 ∨ q5 →
      p6 ∨ q6 →
      p7 ∨ q7 →
      p8 ∨ q8 →
      False := by
  grind (splits := 10)
```
2025-05-23 19:33:54 +00:00
Cameron Zwarich
9ea4946560 feat: add support for USize literals in LCNF (#8456)
This PR adds support for primitive USize literals in LCNF.
2025-05-23 17:22:31 +00:00
Cameron Zwarich
3b205505ef chore: clean up structProjCases pass (#8455) 2025-05-23 15:46:21 +00:00
Lean stage0 autoupdater
6afa8208ec chore: update stage0 2025-05-23 15:21:08 +00:00
Rob23oba
65a5d0cb9d feat: improve Ord proof api (#8378)
This PR improves and extends the api around `Ord` and `Ordering`. These
changes are split off from #8210.
2025-05-23 14:00:20 +00:00
Joachim Breitner
fc3c82b1c7 chore: denixify stage0-updater workflow (#8452)
This PR lets the stage0 autoupdater build lean using the `cmake`
infrastructure, not the deprecated nix infrastructure.
2025-05-23 13:12:50 +00:00
Sebastian Graf
8fc94c5c90 fix: Make split work with metavariables in the target (#8437)
This PR fixes `split` in the presence of metavariables in the target.

The fix consists of replacing an internal use of `apply` for
instantiating match splitters by a new, simpler variant `applyN`. This
new `applyN` is not prone to #8436, which is the ultimate cause for
`split` failing on targets containing metavariables.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-23 12:46:27 +00:00
Paul Reichert
96b81f3cc1 feat: lemmas about list iterators (#8384)
This PR provides lemmas about the behavior of `step`, `toArray`,
`toList` and `toListRev` on list iterators created with `List.iter` and
`List.iterM`.
2025-05-23 09:29:59 +00:00
Kim Morrison
44ff70020d feat: add simp lemma writing Vector.tail in terms of Vector.extract (#8445)
This PR adds a `@[simp]` lemma, and comments explaining that there is
intentionally no verification API for `Vector.take`, `Vector.drop`, or
`Vector.tail`, which should all be rewritten in terms of
`Vector.extract`.
2025-05-22 23:22:54 +00:00
Eric Wieser
ae1ab94992 fix: replace bad simp lemmas for Id (#7352)
This PR reworks the `simp` set around the `Id` monad, to not elide or
unfold `pure` and `Id.run`

In particular, it stops encoding the "defeq abuse" of `Id X = X` in the
statements of theorems, instead using `Id.run` and `pure` to pass back
and forth between these two spellings. Often when writing these with
`pure`, they generalize to other lawful monads; though such changes were
split off to other PRs.

This fixes the problem with the current simp set where `Id.run (pure x)`
is simplified to `Id.run x`, instead of the desirable `x`.
This is particularly bad because the` x` is sometimes inferred with type
`Id X` instead of `X`, which prevents other `simp` lemmas about `X` from
firing.

Making `Id` reducible instead is not an option, as then the `Monad`
instances would have nothing to key on.

---------

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-05-22 22:45:35 +00:00
Joachim Breitner
5e40f4af52 feat: linear-size noConfusionType construction (#8037)
This PR introduces a `noConfusionType` construction that’s sub-quadratic
in size, and reduces faster.

The previous `noConfusion` construction with two nested `match`
statements is quadratic in size and reduction behavior. Using some
helper definitions, a linear size construction is possible.

With this, processing the RISC-V-AST definition from
https://github.com/opencompl/sail-riscv-lean takes 6s instead of 60s.

The previous construction is still used when processing the early
prelude, and can be enabled elsewhere using `set_option
backwards.linearNoConfusionType false`.
2025-05-22 14:54:05 +00:00
Rob23oba
2594a8edad fix: namespace completion to only use the short name (#8350)
This PR changes namespace completion to use the same algorithm as
declaration identifier completion, which makes it use the short name
(last name component) for completions instead of the full name, avoiding
namespace duplications.

Closes #5654
2025-05-22 11:58:47 +00:00
Kim Morrison
b24e232a7a feat: lemmas about ordered rings and fields for grind (#8443)
This PR adds the lemmas about ordered rings and ordered fields which
will be needed by the new algebraic normalization components of `grind`.
2025-05-22 11:41:51 +00:00
Jakob von Raumer
9ad3974314 feat: add List.drop_cons (#8434)
This PR adds the equivalent of `List.take_cons` about `List.drop`.
2025-05-22 11:29:42 +00:00
Lean stage0 autoupdater
b31bf4e645 chore: update stage0 2025-05-22 11:24:54 +00:00
Marc Huisinga
c8d245a08f fix: unknown identifier ranges (#8362)
This PR fixes a bug where the unknown identifier code actions wouldn't
work correctly for some unknown identifier error spans and adjusts
several unknown identifier spans to actually end on the identifier in
question.

The following additional adjustments are made:
- The fallback mechanism of the unknown identifier code actions is
removed, since it could produce severely incorrect suggestions for
unknown identifier errors on fields.
- A performance bug when using the code action to import all unknown
identifiers is fixed.
- A bug that occurs when the elaborator produces multiple overlapping
completion infos is fixed.
- A bug in the snapshot selection that could cause it to wait for
snapshots in snapshots with non-canonical syntax is fixed.
- Some invariants of the snapshot tree are documented.
- The snapshot tree formatting is adjusted to display the final info
tree again.
2025-05-22 10:05:31 +00:00
Leonardo de Moura
4eccb5b479 fix: grind diagnostics at maxHeartbeats (#8438)
This PR ensures that `grind` diagnostics are obtained even when
`maxHeartbeats` is reached.
This PR also removes some dead code.
2025-05-21 22:14:59 +00:00
Paul Reichert
0a43c138ac feat: lemmas about iterator collectors (#8380)
This PR provides simple lemmas about `toArray`, `toList` and `toListRev`
for the iterator library.

It also changes the definition of `Iter` and `IterM` so that they aren't
equal anymore and in particular not definitionally equal. While it was
very convenient to have them be definitionally equal when working with
dependent code, it was also confusing and annoying that one would
sometimes end up with something like `it.toList = IterM.toList it`,
where `it : Iter β`.
2025-05-21 21:11:26 +00:00
Arthur Adjedj
1138062a70 fix: normalize imax 1 u to u (#7631)
This PR fixes `Lean.Level.mkIMaxAux` (`mk_imax` in the kernel) such that
`imax 1 u` reduces to `u`.

Closes #7096
2025-05-21 20:27:53 +00:00
grunweg
ebf455a137 doc: clarify that .now returns a date(time) in the local time zone (#8331)
This PR improves the docstring for `PlainDateTime.now` and its variants.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-05-21 08:04:36 +00:00
Kim Morrison
87cc330489 feat: ordered ring typeclass for grind (#8429)
This PR adds `Lean.Grind.Ring.IsOrdered`, and cleans up the ring/module
grind API. These typeclasses are at present unused, but will support
future algorithmic improvements in `grind`.
2025-05-21 07:05:01 +00:00
Kim Morrison
47a1355fc4 chore: cleanup grind palindrome test (#8428) 2025-05-21 03:31:56 +00:00
Kim Morrison
79254d039c chore: restore @[simp] to List.ofFn_succ (#8427) 2025-05-21 03:12:26 +00:00
Leonardo de Moura
c28b052576 feat: [grind?] attribute (#8426)
This PR adds the attribute `[grind?]`. It is like `[grind]` but displays
inferred E-matching patterns. It is a more convinient than writing.
Thanks @kim-em for suggesting this feature.
```lean
set_option trace.grind.ematch.pattern true
```
This PR also improves some tests, and adds helper function
`ENode.isRoot`.
2025-05-21 00:32:49 +00:00
Kim Morrison
a541b8e75e chore: fix name of new Fin.foldlM_eq_finRange_foldlM lemmas (#8425) 2025-05-21 00:30:33 +00:00
Li Xuanji
a9a069a0ef doc: Fix doc bug in Resolve.lean (#8411)
This PR fixes a doc bug in the Resolve.lean; in reverse order, B comes
before A
2025-05-20 17:16:18 +00:00
Leonardo de Moura
8753239226 chore: remove Grind.Config.failures options (#8423)
Option is not very useful.
2025-05-20 15:40:51 +00:00
Paul Reichert
f4ee72b18c feat: minimal iterator library (#8358)
This PR introduces a very minimal version of the new iterator library.
It comes with list iterators and various consumers, namely `toArray`,
`toList`, `toListRev`, `ForIn`, `fold`, `foldM` and `drain`. All
consumers also come in a partial variant that can be used without any
proofs. This limited version of the iterator library generates decent
code, even with the old code generator.
2025-05-20 14:53:57 +00:00
Leonardo de Moura
8535a2268b fix: simplify isCasesAttrCandidate? in grind (#8415)
The behavior was counterintuitive.
2025-05-20 14:29:07 +00:00
Kim Morrison
d8e7ca2355 feat: draft typeclasses/tests for grind handling fields (#8417)
This PR introduces `Lean.Grind.Field`, proves that a `IsCharP 0` field
satisfies `NoNatZeroDivisors`, and sets up some basic (currently
failing) tests for `grind`.
2025-05-20 13:44:11 +00:00
Henrik Böving
8e0870beec feat: LT for Timestamp and Duration (#8422)
This PR adds `LT` and `Decidable` `LT` instances for
`Std.Time.Timestamp` and `Std.Time.Duration`.
2025-05-20 11:33:49 +00:00
Kim Morrison
3790f8c78e chore: deduplicate Grind.RatModule and Grind.NoNatZeroDivisors (#8416)
Also adds instances from e.g. `Semiring` to `NatModule` and `Ring` to
`IntModule`.
2025-05-20 07:49:42 +00:00
Kim Morrison
3bf95e9b58 feat: add List/Array/Vector.ofFnM (#8389)
This PR adds the `List/Array/Vector.ofFnM`, the monadic analogues of
`ofFn`, along with basic theory.

At the same time we pave some potholes in nearby API.

---------

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
2025-05-20 05:28:29 +00:00
Kim Morrison
bc21b57396 chore: use HMul in Lean.Grind.Module (#8414) 2025-05-20 04:22:06 +00:00
Kim Morrison
6395d69140 feat: add HashMap.get*_filter* lemmas specialized for LawfulBEq (#8399)
This PR adds variants of `HashMap.getElem?_filter` that assume
`LawfulBEq` and have a simpler right-hand-side. `simp` can already
achieve these, via rewriting with `getKey_eq` under the lambda. However
`grind` can not, and these lemmas help `grind` work with `HashMap`
goals. There are variants for all variants of `HashMap`,
`getElem?/getElem/getElem!/getD`, and for `filter` and `filterMap`.
2025-05-20 03:04:32 +00:00
Leonardo de Moura
4ba72aeef7 feat: missing normalization rules in grind (#8413)
This PR implements normalization rules that pull universal quantifiers
across disjunctions. This is a common normalization step performed by
first-order theorem provers.
2025-05-20 02:38:29 +00:00
Leonardo de Moura
e984473886 fix: markNestedProofs preprocessor in grind (#8412)
This PR fixes the `markNestedProofs` preprocessor used in `grind`. There
was a missing case (e.g., `Expr.mdata`)
2025-05-20 01:46:23 +00:00
Leonardo de Moura
88f6439955 fix: case-splitting in grind (#8410)
This PR fixes a case-splitting heuristic in `grind` and simplifies the
proof for test `grind_palindrome2.lean`.
2025-05-20 00:51:47 +00:00
Cameron Zwarich
fc8f290347 feat: support native literals of size unsigned integer types (#8409)
This PR adds support to LCNF for native UInt8/UInt16/UInt32/UInt64
literals.
2025-05-20 00:38:38 +00:00
Cameron Zwarich
423b31755d chore: remove dependency of pretty-printing LCNF.LitValue on toExpr (#8408) 2025-05-19 22:55:21 +00:00
jrr6
d1ec806834 feat: improve error messages in invalid match alternatives (#8368)
This PR improves the error messages produced by invalid pattern-match
alternatives and improves parity in error placement between
pattern-matching tactics and elaborators.

Closes #7170
2025-05-19 17:40:41 +00:00
jrr6
b93231f97e feat: improve inductive type parameter error messages (#8338)
This PR improves the error messages displayed in `inductive`
declarations when type parameters are invalid or absent.

Closes #2195 by improving the relevant error message.
2025-05-19 17:03:49 +00:00
Kim Morrison
f40d72ea47 feat: typeclasses for grind to work with ordered modules (#8347)
This PR adds draft typeclasses for `grind` to process facts about
ordered modules. These interfaces will evolve as the implementation
develops.
2025-05-19 13:55:38 +00:00
Kim Morrison
10fdfc54cb chore: upstream HSMul notation typeclass (#8401)
Upstreaming the `HSMul` notation typeclass, to enable `grind` to process
goals using it.
2025-05-19 12:37:08 +00:00
David Thrane Christiansen
943a9c6a43 chore: revert mistaken deletion (#8404)
This PR reverts the deletion of files that should not have been removed
with the old documentation site.
2025-05-19 12:14:09 +00:00
Wojciech Rozowski
a8a6f71abb fix: add monotonicity lemmas for universal quantifiers (#8403)
This PR adds missing monotonicity lemmas for universal quantifiers, that
are used in defining (co)inductive predicates.
2025-05-19 11:27:46 +00:00
Markus Himmel
9ad4414642 feat: Option lemmas (#8379)
This PR adds missing `Option` lemmas.

Also:

- generalize `bindM` from `Monad` to `Pure`
- change the `simp` normal form of both `<|>` and `Option.orElse` to
`Option.or`
2025-05-19 08:59:31 +00:00
Kim Morrison
efe2ab4c04 chore: remove duplicate instances (#8397)
This PR cleans up many duplicate instances (or, in some cases,
needlessly duplicated `def X := ...; instance Y := X`).
2025-05-19 04:36:06 +00:00
Cameron Zwarich
831026bcf4 chore: remove redundant ToFormat/ToString debug printing instances (#8400) 2025-05-19 03:31:22 +00:00
3426 changed files with 97711 additions and 18237 deletions

38
.github/workflows/awaiting-manual.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Check awaiting-manual label
on:
merge_group:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
check-awaiting-manual:
runs-on: ubuntu-latest
steps:
- name: Check awaiting-manual label
id: check-awaiting-manual-label
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const { labels, number: prNumber } = context.payload.pull_request;
const hasAwaiting = labels.some(label => label.name == "awaiting-manual");
const hasBreaks = labels.some(label => label.name == "breaks-manual");
const hasBuilds = labels.some(label => label.name == "builds-manual");
if (hasAwaiting && hasBreaks) {
core.setFailed('PR has both "awaiting-manual" and "breaks-manual" labels.');
} else if (hasAwaiting && !hasBreaks && !hasBuilds) {
core.info('PR is marked "awaiting-manual" but neither "breaks-manual" nor "builds-manual" labels are present.');
core.setOutput('awaiting', 'true');
}
- name: Wait for manual compatibility
if: github.event_name == 'pull_request' && steps.check-awaiting-manual-label.outputs.awaiting == 'true'
run: |
echo "::notice title=Awaiting manual::PR is marked 'awaiting-manual' but neither 'breaks-manual' nor 'builds-manual' labels are present."
echo "This check will remain in progress until the PR is updated with appropriate manual compatibility labels."
# Keep the job running indefinitely to show "in progress" status
while true; do
sleep 3600 # Sleep for 1 hour at a time
done

View File

@@ -10,11 +10,29 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check awaiting-mathlib label
id: check-awaiting-mathlib-label
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const { labels } = context.payload.pull_request;
if (labels.some(label => label.name == "awaiting-mathlib") && !labels.some(label => label.name == "builds-mathlib")) {
core.setFailed('PR is marked "awaiting-mathlib" but "builds-mathlib" label has not been applied yet by the bot');
const { labels, number: prNumber } = context.payload.pull_request;
const hasAwaiting = labels.some(label => label.name == "awaiting-mathlib");
const hasBreaks = labels.some(label => label.name == "breaks-mathlib");
const hasBuilds = labels.some(label => label.name == "builds-mathlib");
if (hasAwaiting && hasBreaks) {
core.setFailed('PR has both "awaiting-mathlib" and "breaks-mathlib" labels.');
} else if (hasAwaiting && !hasBreaks && !hasBuilds) {
core.info('PR is marked "awaiting-mathlib" but neither "breaks-mathlib" nor "builds-mathlib" labels are present.');
core.setOutput('awaiting', 'true');
}
- name: Wait for mathlib compatibility
if: github.event_name == 'pull_request' && steps.check-awaiting-mathlib-label.outputs.awaiting == 'true'
run: |
echo "::notice title=Awaiting mathlib::PR is marked 'awaiting-mathlib' but neither 'breaks-mathlib' nor 'builds-mathlib' labels are present."
echo "This check will remain in progress until the PR is updated with appropriate mathlib compatibility labels."
# Keep the job running indefinitely to show "in progress" status
while true; do
sleep 3600 # Sleep for 1 hour at a time
done

View File

@@ -82,7 +82,7 @@ jobs:
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-*
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-* tests/lean/run/importStructure.lean
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
@@ -104,12 +104,13 @@ jobs:
# NOTE: must be in sync with `save` below
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
build/stage1/**/*.olean
${{ matrix.name == 'Linux Lake' && false && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*' || '' }}
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }}
key: ${{ matrix.name }}-build-v3-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
@@ -127,9 +128,12 @@ jobs:
[ -d build ] || mkdir build
cd build
# arguments passed to `cmake`
# this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
OPTIONS=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
if [[ -n '${{ matrix.release }}' ]]; then
# this also enables githash embedding into stage 1 library, which prohibits reusing
# `.olean`s across commits, so we don't do it in the fast non-release CI
OPTIONS+=(-DCHECK_OLEAN_VERSION=ON)
fi
if [[ -n '${{ matrix.cross_target }}' ]]; then
# used by `prepare-llvm`
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
@@ -193,7 +197,7 @@ jobs:
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.name == 'Linux release')
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
- name: Test Summary
uses: test-summary/action@v2
with:
@@ -210,7 +214,7 @@ jobs:
- name: Check Stage 3
run: |
make -C build -j$NPROC check-stage3
if: matrix.test-speedcenter
if: matrix.check-stage3
- name: Test Speedcenter Benchmarks
run: |
# Necessary for some timing metrics but does not work on Namespace runners
@@ -224,7 +228,7 @@ jobs:
run: |
# clean rebuild in case of Makefile changes
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC
if: matrix.name == 'Linux' && inputs.check-level >= 1
if: matrix.check-rebootstrap
- name: CCache stats
if: always()
run: ccache -s
@@ -242,9 +246,10 @@ jobs:
# NOTE: must be in sync with `restore` above
path: |
.ccache
${{ matrix.name == 'Linux Lake' && 'build/stage1/**/*.trace
build/stage1/**/*.olean
${{ matrix.name == 'Linux Lake' && false && 'build/stage1/**/*.trace
build/stage1/**/*.olean*
build/stage1/**/*.ilean
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*' || '' }}
key: ${{ steps.restore-cache.outputs.cache-primary-key }}

View File

@@ -103,6 +103,13 @@ jobs:
echo "Tag ${TAG_NAME} did not match SemVer regex."
fi
- name: Check for custom releases (e.g., not in the main lean repository)
if: startsWith(github.ref, 'refs/tags/') && github.repository != 'leanprover/lean4'
id: set-release-custom
run: |
TAG_NAME="${GITHUB_REF##*/}"
echo "RELEASE_TAG=$TAG_NAME" >> "$GITHUB_OUTPUT"
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
@@ -111,7 +118,7 @@ jobs:
run: |
check_level=0
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" ]]; then
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=2
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
@@ -138,6 +145,7 @@ jobs:
// use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
const isPushToMaster = "${{ github.event_name }}" == "push" && "${{ github.ref_name }}" == "master";
let matrix = [
/* TODO: to be updated to new LLVM
{
@@ -157,9 +165,17 @@ jobs:
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"os": large && level < 2 ? "nscloud-ubuntu-22.04-amd64-4x16" : "ubuntu-latest",
"release": true,
"check-level": 0,
// Special handling for release jobs. We want:
// 1. To run it in PRs so developers get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the
// Linux lake build and adds little value in the merge queue
// 3. To run it in release (obviously)
// 4. To run it for pushes to master so that pushes to master have a Linux toolchain
// available as an artifact for Grove to use.
"check-level": (isPr || isPushToMaster) ? 0 : 2,
"secondary": isPr,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
@@ -169,21 +185,14 @@ jobs:
},
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16" : "ubuntu-latest",
"check-level": 0,
// just a secondary build job for now until false positives can be excluded
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// TODO: importStructure is not compatible with .olean caching
// TODO: why does scopedMacros fail?
"CTEST_OPTIONS": "-E 'scopedMacros|importStructure'"
},
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"test": true,
"check-rebootstrap": level >= 1,
"check-stage3": level >= 2,
"test-speedcenter": level >= 2,
"check-level": 1,
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
"test-speedcenter": large && level >= 2,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
},
{
"name": "Linux Reldebug",
@@ -216,7 +225,8 @@ jobs:
},
{
"name": "macOS aarch64",
"os": "macos-14",
// standard GH runner only comes with 7GB so use large runner if possible
"os": large ? "nscloud-macos-sonoma-arm64-6x14" : "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"shell": "bash -euxo pipefail {0}",
@@ -224,11 +234,7 @@ jobs:
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// Special handling for MacOS aarch64, we want:
// 1. To run it in PRs so Mac devs get PR toolchains (so secondary is sufficient)
// 2. To skip it in merge queues as it takes longer than the Linux build and adds
// little value in the merge queue
// 3. To run it in release (obviously)
// See above for release job levels
"check-level": isPr ? 0 : 2,
"secondary": isPr,
},
@@ -247,7 +253,7 @@ jobs:
},
{
"name": "Linux aarch64",
"os": "nscloud-ubuntu-22.04-arm64-4x8",
"os": "nscloud-ubuntu-22.04-arm64-4x16",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"check-level": 2,
@@ -357,7 +363,7 @@ jobs:
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -401,7 +407,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
- name: Release Nightly
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
body_path: diff.md
prerelease: true
@@ -418,6 +424,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
- name: Update toolchain on mathlib4's nightly-testing branch
run: |
gh workflow -R leanprover-community/mathlib4 run nightly_bump_toolchain.yml
gh workflow -R leanprover-community/mathlib4-nightly-testing run nightly_bump_toolchain.yml
env:
GITHUB_TOKEN: ${{ secrets.MATHLIB4_BOT }}

161
.github/workflows/grove.yml vendored Normal file
View File

@@ -0,0 +1,161 @@
name: Grove
on:
workflow_run: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
workflows: [CI]
types: [completed]
permissions:
pull-requests: write
jobs:
grove-build:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success' && github.repository == 'leanprover/lean4'
steps:
- name: Retrieve information about the original workflow
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
# This action is deprecated and archived, but it seems hard to find a
# better solution for getting the PR number
# see https://github.com/orgs/community/discussions/25220 for some discussion
id: workflow-info
with:
token: ${{ secrets.GITHUB_TOKEN }}
sourceRunId: ${{ github.event.workflow_run.id }}
- name: Check if should run
id: should-run
run: |
# Check if it's a push to master (no PR number and target branch is master)
if [ -z "${{ steps.workflow-info.outputs.pullRequestNumber }}" ]; then
if [ "${{ github.event.workflow_run.head_branch }}" = "master" ]; then
echo "Push to master detected. Skipping for now, to be enabled later."
echo "should-run=false" >> "$GITHUB_OUTPUT"
else
echo "Push to non-master branch, skipping"
echo "should-run=false" >> "$GITHUB_OUTPUT"
fi
else
# Check if it's a PR with grove label
PR_LABELS='${{ steps.workflow-info.outputs.pullRequestLabels }}'
if echo "$PR_LABELS" | grep -q '"grove"'; then
echo "PR with grove label detected"
echo "should-run=true" >> "$GITHUB_OUTPUT"
else
echo "PR without grove label, skipping"
echo "should-run=false" >> "$GITHUB_OUTPUT"
fi
fi
- name: Fetch upstream invalidated facts
if: ${{ steps.should-run.outputs.should-run == 'true' && steps.workflow-info.outputs.pullRequestNumber != '' }}
id: fetch-upstream
uses: TwoFx/grove-action/fetch-upstream@v0.3
with:
artifact-name: grove-invalidated-facts
base-ref: master
- name: Download toolchain for this commit
if: ${{ steps.should-run.outputs.should-run == 'true' }}
id: download-toolchain
uses: dawidd6/action-download-artifact@v11
with:
commit: ${{ steps.workflow-info.outputs.sourceHeadSha }}
workflow: ci.yml
path: artifacts
name: build-Linux.*
name_is_regexp: true
- name: Unpack toolchain
if: ${{ steps.should-run.outputs.should-run == 'true' }}
id: unpack-toolchain
run: |
cd artifacts
# Find the tar.zst file
TAR_FILE=$(find . -name "lean-*.tar.zst" -type f | head -1)
if [ -z "$TAR_FILE" ]; then
echo "Error: No lean-*.tar.zst file found"
exit 1
fi
echo "Found archive: $TAR_FILE"
# Extract the archive
tar --zstd -xf "$TAR_FILE"
# Find the extracted directory name
LEAN_DIR=$(find . -maxdepth 1 -name "lean-*" -type d | head -1)
if [ -z "$LEAN_DIR" ]; then
echo "Error: No lean-* directory found after extraction"
exit 1
fi
echo "Extracted directory: $LEAN_DIR"
echo "lean-dir=$LEAN_DIR" >> "$GITHUB_OUTPUT"
- name: Build
if: ${{ steps.should-run.outputs.should-run == 'true' }}
id: build
uses: TwoFx/grove-action/build@v0.3
with:
project-path: doc/std/grove
script-name: grove-stdlib
invalidated-facts-artifact-name: grove-invalidated-facts
comment-artifact-name: grove-comment
toolchain-id: lean4
toolchain-path: artifacts/${{ steps.unpack-toolchain.outputs.lean-dir }}
project-ref: ${{ steps.workflow-info.outputs.sourceHeadSha }}
# deploy-alias computes a URL component for the PR preview. This
# is so we can have a stable name to use for feedback on draft
# material.
- id: deploy-alias
if: ${{ steps.should-run.outputs.should-run == 'true' }}
uses: actions/github-script@v7
name: Compute Alias
with:
result-encoding: string
script: |
if (process.env.PR) {
return `pr-${process.env.PR}`
} else {
return 'deploy-preview-main';
}
env:
PR: ${{ steps.workflow-info.outputs.pullRequestNumber }}
- name: Deploy to Netlify
if: ${{ steps.should-run.outputs.should-run == 'true' }}
id: deploy-draft
uses: nwtgck/actions-netlify@v3.0
with:
publish-dir: ${{ steps.build.outputs.out-path }}
production-deploy: false
github-token: ${{ secrets.GITHUB_TOKEN }}
alias: ${{ steps.deploy-alias.outputs.result }}
enable-commit-comment: false
enable-pull-request-comment: false
fails-without-credentials: true
enable-github-deployment: false
enable-commit-status: false
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: "1cacfa39-a11c-467c-99e7-2e01d7b4089e"
# actions-netlify cannot add deploy links to a PR because it assumes a
# pull_request context, not a workflow_run context, see
# https://github.com/nwtgck/actions-netlify/issues/545
# We work around by using a comment to post the latest link
- name: "Comment on PR with preview links"
uses: marocchino/sticky-pull-request-comment@v2
if: ${{ steps.should-run.outputs.should-run == 'true' && steps.workflow-info.outputs.pullRequestNumber != '' }}
with:
number: ${{ env.PR_NUMBER }}
header: preview-comment
recreate: true
message: |
[Grove](${{ steps.deploy-draft.outputs.deploy-url }}) for revision ${{ steps.workflow-info.outputs.sourceHeadSha }}.
${{ steps.build.outputs.comment-text }}
env:
PR_NUMBER: ${{ steps.workflow-info.outputs.pullRequestNumber }}
PR_HEADSHA: ${{ steps.workflow-info.outputs.sourceHeadSha }}

View File

@@ -34,7 +34,7 @@ jobs:
- name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: download-artifact
uses: dawidd6/action-download-artifact@v9 # https://github.com/marketplace/actions/download-workflow-artifact
uses: dawidd6/action-download-artifact@v10 # https://github.com/marketplace/actions/download-workflow-artifact
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts
@@ -48,19 +48,30 @@ jobs:
git -C lean4.git remote add origin https://github.com/${{ github.repository_owner }}/lean4.git
git -C lean4.git fetch -n origin master
git -C lean4.git fetch -n origin "${{ steps.workflow-info.outputs.sourceHeadSha }}"
# Create both the original tag and the SHA-suffixed tag
SHORT_SHA="${{ steps.workflow-info.outputs.sourceHeadSha }}"
SHORT_SHA="${SHORT_SHA:0:7}"
# Export the short SHA for use in subsequent steps
echo "SHORT_SHA=${SHORT_SHA}" >> "$GITHUB_ENV"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}" "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-"${SHORT_SHA}"
- name: Delete existing release if present
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
# Try to delete any existing release for the current PR.
# Try to delete any existing release for the current PR (just the version without the SHA suffix).
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -73,7 +84,22 @@ jobs:
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})
# There are coredumps files here as well, but all in deeper subdirectories.
files: artifacts/*/*
fail_on_unmatched_files: true
draft: false
tag_name: pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}
repository: ${{ github.repository_owner }}/lean4-pr-releases
env:
# The token used here must have `workflow` privileges.
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Report release status (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
with:
@@ -87,6 +113,20 @@ jobs:
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}",
});
- name: Report release status (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
state: "success",
context: "PR toolchain (SHA-suffixed)",
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}",
});
- name: Add label
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
@@ -114,7 +154,7 @@ jobs:
uses: dcarbone/install-jq-action@v3.1.1
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
- name: Check merge-base and nightly-testing-YYYY-MM-DD
- name: Check merge-base and nightly-testing-YYYY-MM-DD for Mathlib/Batteries
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: ready
run: |
@@ -127,7 +167,7 @@ jobs:
echo "The merge base of this PR coincides with the nightly release"
BATTERIES_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/batteries.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4-nightly-testing.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
if [[ -n "$BATTERIES_REMOTE_TAGS" ]]; then
echo "... and Batteries has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
@@ -143,7 +183,6 @@ jobs:
echo "... but Batteries does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Batteries CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Batteries CI should run now."
fi
else
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
@@ -225,6 +264,108 @@ jobs:
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
fi
- name: Check merge-base and nightly-testing-YYYY-MM-DD for reference manual
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: reference-manual-ready
run: |
echo "Most recent nightly release in your branch: $MOST_RECENT_NIGHTLY"
NIGHTLY_SHA=$(git -C lean4.git rev-parse "nightly-$MOST_RECENT_NIGHTLY^{commit}")
echo "SHA of most recent nightly release: $NIGHTLY_SHA"
MERGE_BASE_SHA=$(git -C lean4.git merge-base origin/master "${{ steps.workflow-info.outputs.sourceHeadSha }}")
echo "SHA of merge-base: $MERGE_BASE_SHA"
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
echo "The merge base of this PR coincides with the nightly release"
MANUAL_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover/reference-manual.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
if [[ -n "$MANUAL_REMOTE_TAGS" ]]; then
echo "... and the reference manual has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE=""
else
echo "... but the reference manual does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Reference manual CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-manual\`, reference manual CI should run now."
fi
else
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
git -C lean4.git log -10 origin/master
git -C lean4.git fetch origin nightly-with-manual
NIGHTLY_WITH_MANUAL_SHA="$(git -C lean4.git rev-parse "origin/nightly-with-manual")"
MESSAGE="- ❗ Reference manual CI will not be attempted unless your PR branches off the \`nightly-with-manual\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MANUAL_SHA\`."
fi
if [[ -n "$MESSAGE" ]]; then
# Check if force-manual-ci label is present
LABELS="$(curl --retry 3 --location --silent \
-H "Authorization: token ${{ secrets.MANUAL_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/labels" \
| jq -r '.[].name')"
if echo "$LABELS" | grep -q "^force-manual-ci$"; then
echo "force-manual-ci label detected, forcing CI despite issues"
MESSAGE="Forcing reference manual CI because the \`force-manual-ci\` label is present, despite problem: $MESSAGE"
FORCE_CI=true
else
MESSAGE="$MESSAGE You can force reference manual CI using the \`force-manual-ci\` label."
fi
echo "Checking existing messages"
# The code for updating comments is duplicated in the reference manual's
# scripts/lean-pr-testing-comments.sh
# so keep in sync
# Use GitHub API to check if a comment already exists
existing_comment="$(curl --retry 3 --location --silent \
-H "Authorization: token ${{ secrets.MANUAL_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
| jq 'first(.[] | select(.body | test("^- . Manual") or startswith("Reference manual CI status")) | select(.user.login == "leanprover-bot"))')"
existing_comment_id="$(echo "$existing_comment" | jq -r .id)"
existing_comment_body="$(echo "$existing_comment" | jq -r .body)"
if [[ "$existing_comment_body" != *"$MESSAGE"* ]]; then
MESSAGE="$MESSAGE ($(date "+%Y-%m-%d %H:%M:%S"))"
echo "Posting message to the comments: $MESSAGE"
# Append new result to the existing comment or post a new comment
# It's essential we use the MANUAL_COMMENT_BOT token here, so that reference manual CI can subsequently edit the comment.
if [ -z "$existing_comment_id" ]; then
INTRO="Reference manual CI status:"
# Post new comment with a bullet point
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
curl -L -s \
-X POST \
-H "Authorization: token ${{ secrets.MANUAL_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
-d "$(jq --null-input --arg intro "$INTRO" --arg val "$MESSAGE" '{"body":($intro + "\n" + $val)}')" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
else
# Append new result to the existing comment
echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
curl -L -s \
-X PATCH \
-H "Authorization: token ${{ secrets.MANUAL_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
-d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \
"https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id"
fi
else
echo "The message already exists in the comment body."
fi
if [[ "$FORCE_CI" == "true" ]]; then
echo "manual_ready=true" >> "$GITHUB_OUTPUT"
else
echo "manual_ready=false" >> "$GITHUB_OUTPUT"
fi
else
echo "manual_ready=true" >> "$GITHUB_OUTPUT"
fi
- name: Report mathlib base
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }}
uses: actions/github-script@v7
@@ -282,16 +423,18 @@ jobs:
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, pushing an empty commit."
echo "Branch already exists, updating lean-toolchain."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Batteries `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -313,7 +456,7 @@ jobs:
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4
with:
repository: leanprover-community/mathlib4
repository: leanprover-community/mathlib4-nightly-testing
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
@@ -346,24 +489,86 @@ jobs:
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}",' lakefile.lean
lake update batteries
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, merging $BASE and bumping Batteries."
echo "Branch already exists, updating lean-toolchain and bumping Batteries."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
lake update batteries
git add lake-manifest.json
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
run: |
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# We next automatically create a reference manual branch using this toolchain.
# Reference manual CI will be responsible for reporting back success or failure
# to the PR comments asynchronously (and thus transitively SubVerso/Verso).
- name: Cleanup workspace
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
run: |
sudo rm -rf ./*
# Checkout the reference manual repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
uses: actions/checkout@v4
with:
repository: leanprover/reference-manual
token: ${{ secrets.MANUAL_PR_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
- name: Check if tag in reference manual exists
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
id: check_manual_tag
run: |
git config user.name "leanprover-bot"
git config user.email "leanprover-bot@lean-fro.org"
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
else
echo "Couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch in the reference manual. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi
echo "Using base tag: $BASE"
EXISTS="$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)"
echo "Branch exists: $EXISTS"
if [ "$EXISTS" = "0" ]; then
echo "Branch does not exist, creating it."
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else
echo "Branch already exists, updating lean-toolchain."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The reference manual's `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}" > lean-toolchain
git add lean-toolchain
git add lake-manifest.json
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
run: |
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}

View File

@@ -40,34 +40,24 @@ jobs:
run: |
git config --global user.name "Lean stage0 autoupdater"
git config --global user.email "<>"
# Would be nice, but does not work yet:
# https://github.com/DeterminateSystems/magic-nix-cache/issues/39
# This action does not run that often and building runs in a few minutes, so ok for now
#- if: env.should_update_stage0 == 'yes'
# uses: DeterminateSystems/magic-nix-cache-action@v2
- if: env.should_update_stage0 == 'yes'
name: Restore Build Cache
uses: actions/cache/restore@v4
with:
path: nix-store-cache
key: Nix Linux-nix-store-cache-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
Nix Linux-nix-store-cache
- if: env.should_update_stage0 == 'yes'
name: Further Set Up Nix Cache
shell: bash -euxo pipefail {0}
run: |
# Nix seems to mutate the cache, so make a copy
cp -r nix-store-cache nix-store-cache-copy || true
- if: env.should_update_stage0 == 'yes'
name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
extra-conf: |
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
- name: Open Nix shell once
if: env.should_update_stage0 == 'yes'
run: true
shell: 'nix develop -c bash -euxo pipefail {0}'
- name: Set up NPROC
if: env.should_update_stage0 == 'yes'
run: |
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: nix run .#update-stage0-commit
run: cmake --preset release
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: make -j$NPROC -C build/release update-stage0-commit
shell: 'nix develop -c bash -euxo pipefail {0}'
- if: env.should_update_stage0 == 'yes'
run: git show --stat
- if: env.should_update_stage0 == 'yes' && github.event_name == 'push'

1
.gitignore vendored
View File

@@ -6,7 +6,6 @@
lake-manifest.json
/build
/src/lakefile.toml
/tests/lakefile.toml
/lakefile.toml
GPATH
GRTAGS

View File

@@ -131,14 +131,21 @@ Thus `[init]` functions are run iff their module is imported, regardless of whet
The initializer for module `A.B` is called `initialize_A_B` and will automatically initialize any imported modules.
Module initializers are idempotent (when run with the same `builtin` flag), but not thread-safe.
**Important for process-related functionality**: If your application needs to use process-related functions from libuv, such as `Std.Internal.IO.Process.getProcessTitle` and `Std.Internal.IO.Process.setProcessTitle`, you must call `lean_setup_args(argc, argv)` (which returns a potentially modified `argv` that must be used in place of the original) **before** calling `lean_initialize()` or `lean_initialize_runtime_module()`. This sets up process handling capabilities correctly, which is essential for certain system-level operations that Lean's runtime may depend on.
Together with initialization of the Lean runtime, you should execute code like the following exactly once before accessing any Lean declarations:
```c
void lean_initialize_runtime_module();
void lean_initialize();
char ** lean_setup_args(int argc, char ** argv);
lean_object * initialize_A_B(uint8_t builtin, lean_object *);
lean_object * initialize_C(uint8_t builtin, lean_object *);
...
argv = lean_setup_args(argc, argv); // if using process-related functionality
lean_initialize_runtime_module();
//lean_initialize(); // necessary (and replaces `lean_initialize_runtime_module`) if you (indirectly) access the `Lean` package

View File

@@ -85,5 +85,13 @@ such that changing files in `Init` doesn't force a full rebuild of `Lean`.
You can test a Lean PR against Mathlib and Batteries by rebasing your PR
on to `nightly-with-mathlib` branch. (It is fine to force push after rebasing.)
CI will generate a branch of Mathlib and Batteries called `lean-pr-testing-NNNN`
that uses the toolchain for your PR, and will report back to the Lean PR with results from Mathlib CI.
on the `leanprover-community/mathlib4-nightly-testing` fork of Mathlib.
This branch uses the toolchain for your PR, and will report back to the Lean PR with results from Mathlib CI.
See https://leanprover-community.github.io/contribute/tags_and_branches.html for more details.
### Testing against the Lean Language Reference
You can test a Lean PR against the reference manual by rebasing your PR
on to `nightly-with-manual` branch. (It is fine to force push after rebasing.)
CI will generate a branch of the reference manual called `lean-pr-testing-NNNN`
in `leanprover/reference-manual`. This branch uses the toolchain for your PR,
and will report back to the Lean PR with results from Mathlib CI.

View File

@@ -50,7 +50,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Re-running `script/release_checklist.py` will then create the tag `v4.6.0` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
- `script/release_checklist.py` will then merge the tag `v4.6.0` into the `stable` branch and push it (unless `stable-branch: false` in the `release_repos.yml` file).
- Special notes on repositories with exceptional requirements:
- `doc-gen4` has addition dependencies which we do not update at each toolchain release, although occasionally these break and need to be updated manually.
- `doc-gen4` has additional dependencies which we do not update at each toolchain release, although occasionally these break and need to be updated manually.
- `verso`:
- The `subverso` dependency is unusual in that it needs to be compatible with _every_ Lean release simultaneously.
Usually you don't need to do anything.
@@ -94,6 +94,8 @@ We'll use `v4.6.0` as the intended release version as a running example.
This checklist walks you through creating the first release candidate for a version of Lean.
For subsequent release candidates, the process is essentially the same, but we start out with the `releases/v4.7.0` branch already created.
We'll use `v4.7.0-rc1` as the intended release version in this example.
- Decide which nightly release you want to turn into a release candidate.
@@ -112,7 +114,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git fetch nightly tag nightly-2024-02-29
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
git push --set-upstream origin releases/v4.18.0
git push --set-upstream origin releases/v4.7.0
```
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.

9
doc/std/README.md Normal file
View File

@@ -0,0 +1,9 @@
# The Lean standard library
This directory contains development information about the Lean standard library. The user-facing documentation of the standard library
is part of the [Lean Language Reference](https://lean-lang.org/doc/reference/latest/).
Here you will find
* the [standard library vision document](./vision.md), including the call for contributions,
* the [standard library style guide](./style.md), and
* the [standard library naming conventions](./naming.md).

4
doc/std/grove/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/.lake
!lake-manifest.json
metadata.json
invalidated.json

View File

@@ -0,0 +1,14 @@
import Grove.Framework
import GroveStdlib.Generated.«associative-query-operations»
/-
This file is autogenerated by grove. You can manually edit it, for example to resolve merge
conflicts, but be careful.
-/
open Grove.Framework Widget
namespace GroveStdlib.Generated
def restoreState : RestoreStateM Unit := do
«associative-query-operations».restoreState

View File

@@ -0,0 +1,486 @@
import Grove.Framework
/-
This file is autogenerated by grove. You can manually edit it, for example to resolve merge
conflicts, but be careful.
-/
open Grove.Framework Widget
namespace GroveStdlib.Generated.«associative-query-operations»
def «01f88623-fa5f-4380-9772-b30f2fec5c94» : AssociationTable.Fact .subexpression where
widgetId := "associative-query-operations"
factId := "01f88623-fa5f-4380-9772-b30f2fec5c94"
rowId := "01f88623-fa5f-4380-9772-b30f2fec5c94"
rowState := #["Std.DHashMap", "Std.DHashMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DHashMap.isEmpty,
renderedStatement := "Std.DHashMap.isEmpty.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.DHashMap α β) : Bool",
isDeprecated := false }),"Std.DHashMap.Raw", "Std.DHashMap.Raw.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DHashMap.Raw.isEmpty,
renderedStatement := "Std.DHashMap.Raw.isEmpty.{u, v} {α : Type u} {β : α → Type v} (m : Std.DHashMap.Raw α β) : Bool",
isDeprecated := false }),"Std.ExtDHashMap", "Std.ExtDHashMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtDHashMap.isEmpty,
renderedStatement := "Std.ExtDHashMap.isEmpty.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α]\n [LawfulHashable α] (m : Std.ExtDHashMap α β) : Bool",
isDeprecated := false }),"Std.DTreeMap", "Std.DTreeMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DTreeMap.isEmpty,
renderedStatement := "Std.DTreeMap.isEmpty.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap α β cmp) : Bool",
isDeprecated := false }),"Std.DTreeMap.Raw", "Std.DTreeMap.Raw.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DTreeMap.Raw.isEmpty,
renderedStatement := "Std.DTreeMap.Raw.isEmpty.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap.Raw α β cmp) :\n Bool",
isDeprecated := false }),"Std.ExtDTreeMap", "Std.ExtDTreeMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtDTreeMap.isEmpty,
renderedStatement := "Std.ExtDTreeMap.isEmpty.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.ExtDTreeMap α β cmp) :\n Bool",
isDeprecated := false }),"Std.HashMap", "Std.HashMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashMap.isEmpty,
renderedStatement := "Std.HashMap.isEmpty.{u, v} {α : Type u} {β : Type v} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashMap α β) : Bool",
isDeprecated := false }),"Std.HashMap.Raw", "Std.HashMap.Raw.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashMap.Raw.isEmpty,
renderedStatement := "Std.HashMap.Raw.isEmpty.{u, v} {α : Type u} {β : Type v} (m : Std.HashMap.Raw α β) : Bool",
isDeprecated := false }),"Std.ExtHashMap", "Std.ExtHashMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtHashMap.isEmpty,
renderedStatement := "Std.ExtHashMap.isEmpty.{u, v} {α : Type u} {β : Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashMap α β) : Bool",
isDeprecated := false }),"Std.TreeMap", "Std.TreeMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeMap.isEmpty,
renderedStatement := "Std.TreeMap.isEmpty.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap α β cmp) : Bool",
isDeprecated := false }),"Std.TreeMap.Raw", "Std.TreeMap.Raw.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeMap.Raw.isEmpty,
renderedStatement := "Std.TreeMap.Raw.isEmpty.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap.Raw α β cmp) : Bool",
isDeprecated := false }),"Std.ExtTreeMap", "Std.ExtTreeMap.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtTreeMap.isEmpty,
renderedStatement := "Std.ExtTreeMap.isEmpty.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.ExtTreeMap α β cmp) : Bool",
isDeprecated := false }),"Std.HashSet", "Std.HashSet.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashSet.isEmpty,
renderedStatement := "Std.HashSet.isEmpty.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashSet α) : Bool",
isDeprecated := false }),"Std.HashSet.Raw", "Std.HashSet.Raw.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashSet.Raw.isEmpty,
renderedStatement := "Std.HashSet.Raw.isEmpty.{u} {α : Type u} (m : Std.HashSet.Raw α) : Bool",
isDeprecated := false }),"Std.ExtHashSet", "Std.ExtHashSet.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtHashSet.isEmpty,
renderedStatement := "Std.ExtHashSet.isEmpty.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashSet α) : Bool",
isDeprecated := false }),"Std.TreeSet", "Std.TreeSet.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeSet.isEmpty,
renderedStatement := "Std.TreeSet.isEmpty.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet α cmp) : Bool",
isDeprecated := false }),"Std.TreeSet.Raw", "Std.TreeSet.Raw.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeSet.Raw.isEmpty,
renderedStatement := "Std.TreeSet.Raw.isEmpty.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet.Raw α cmp) : Bool",
isDeprecated := false }),"Std.ExtTreeSet", "Std.ExtTreeSet.isEmpty", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtTreeSet.isEmpty,
renderedStatement := "Std.ExtTreeSet.isEmpty.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.ExtTreeSet α cmp) : Bool",
isDeprecated := false }),]
metadata := {
status := .done
comment := ""
}
def «f084f852-af71-45b6-8ab3-d251a8144f72» : AssociationTable.Fact .subexpression where
widgetId := "associative-query-operations"
factId := "f084f852-af71-45b6-8ab3-d251a8144f72"
rowId := "f084f852-af71-45b6-8ab3-d251a8144f72"
rowState := #["Std.DHashMap", "Std.DHashMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DHashMap.size,
renderedStatement := "Std.DHashMap.size.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.DHashMap α β) : Nat",
isDeprecated := false }),"Std.DHashMap.Raw", "Std.DHashMap.Raw.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DHashMap.Raw.size,
renderedStatement := "Std.DHashMap.Raw.size.{u, v} {α : Type u} {β : α → Type v} (self : Std.DHashMap.Raw α β) : Nat",
isDeprecated := false }),"Std.ExtDHashMap", "Std.ExtDHashMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtDHashMap.size,
renderedStatement := "Std.ExtDHashMap.size.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α]\n [LawfulHashable α] (m : Std.ExtDHashMap α β) : Nat",
isDeprecated := false }),"Std.DTreeMap", "Std.DTreeMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DTreeMap.size,
renderedStatement := "Std.DTreeMap.size.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap α β cmp) : Nat",
isDeprecated := false }),"Std.DTreeMap.Raw", "Std.DTreeMap.Raw.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DTreeMap.Raw.size,
renderedStatement := "Std.DTreeMap.Raw.size.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap.Raw α β cmp) : Nat",
isDeprecated := false }),"Std.ExtDTreeMap", "Std.ExtDTreeMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtDTreeMap.size,
renderedStatement := "Std.ExtDTreeMap.size.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.ExtDTreeMap α β cmp) : Nat",
isDeprecated := false }),"Std.HashMap", "Std.HashMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashMap.size,
renderedStatement := "Std.HashMap.size.{u, v} {α : Type u} {β : Type v} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashMap α β) : Nat",
isDeprecated := false }),"Std.HashMap.Raw", "Std.HashMap.Raw.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashMap.Raw.size,
renderedStatement := "Std.HashMap.Raw.size.{u, v} {α : Type u} {β : Type v} (m : Std.HashMap.Raw α β) : Nat",
isDeprecated := false }),"Std.ExtHashMap", "Std.ExtHashMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtHashMap.size,
renderedStatement := "Std.ExtHashMap.size.{u, v} {α : Type u} {β : Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashMap α β) : Nat",
isDeprecated := false }),"Std.TreeMap", "Std.TreeMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeMap.size,
renderedStatement := "Std.TreeMap.size.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap α β cmp) : Nat",
isDeprecated := false }),"Std.TreeMap.Raw", "Std.TreeMap.Raw.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeMap.Raw.size,
renderedStatement := "Std.TreeMap.Raw.size.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap.Raw α β cmp) : Nat",
isDeprecated := false }),"Std.ExtTreeMap", "Std.ExtTreeMap.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtTreeMap.size,
renderedStatement := "Std.ExtTreeMap.size.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.ExtTreeMap α β cmp) : Nat",
isDeprecated := false }),"Std.HashSet", "Std.HashSet.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashSet.size,
renderedStatement := "Std.HashSet.size.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashSet α) : Nat",
isDeprecated := false }),"Std.HashSet.Raw", "Std.HashSet.Raw.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashSet.Raw.size,
renderedStatement := "Std.HashSet.Raw.size.{u} {α : Type u} (m : Std.HashSet.Raw α) : Nat",
isDeprecated := false }),"Std.ExtHashSet", "Std.ExtHashSet.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtHashSet.size,
renderedStatement := "Std.ExtHashSet.size.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashSet α) : Nat",
isDeprecated := false }),"Std.TreeSet", "Std.TreeSet.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeSet.size,
renderedStatement := "Std.TreeSet.size.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet α cmp) : Nat",
isDeprecated := false }),"Std.TreeSet.Raw", "Std.TreeSet.Raw.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeSet.Raw.size,
renderedStatement := "Std.TreeSet.Raw.size.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet.Raw α cmp) : Nat",
isDeprecated := false }),"Std.ExtTreeSet", "Std.ExtTreeSet.size", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtTreeSet.size,
renderedStatement := "Std.ExtTreeSet.size.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.ExtTreeSet α cmp) : Nat",
isDeprecated := false }),]
metadata := {
status := .done
comment := ""
}
def «f4e6fa70-5aed-439d-aaad-5f4ced65bf7b» : AssociationTable.Fact .subexpression where
widgetId := "associative-query-operations"
factId := "f4e6fa70-5aed-439d-aaad-5f4ced65bf7b"
rowId := "f4e6fa70-5aed-439d-aaad-5f4ced65bf7b"
rowState := #["Std.DTreeMap", "Std.DTreeMap.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DTreeMap.any,
renderedStatement := "Std.DTreeMap.any.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap α β cmp)\n (p : (a : α) → β a → Bool) : Bool",
isDeprecated := false }),"Std.DTreeMap.Raw", "Std.DTreeMap.Raw.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.DTreeMap.Raw.any,
renderedStatement := "Std.DTreeMap.Raw.any.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap.Raw α β cmp)\n (p : (a : α) → β a → Bool) : Bool",
isDeprecated := false }),"Std.ExtDTreeMap", "Std.ExtDTreeMap.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtDTreeMap.any,
renderedStatement := "Std.ExtDTreeMap.any.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n (t : Std.ExtDTreeMap α β cmp) (p : (a : α) → β a → Bool) : Bool",
isDeprecated := false }),"Std.TreeMap", "Std.TreeMap.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeMap.any,
renderedStatement := "Std.TreeMap.any.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap α β cmp) (p : α → β → Bool) :\n Bool",
isDeprecated := false }),"Std.TreeMap.Raw", "Std.TreeMap.Raw.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeMap.Raw.any,
renderedStatement := "Std.TreeMap.Raw.any.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap.Raw α β cmp)\n (p : α → β → Bool) : Bool",
isDeprecated := false }),"Std.ExtTreeMap", "Std.ExtTreeMap.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtTreeMap.any,
renderedStatement := "Std.ExtTreeMap.any.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n (t : Std.ExtTreeMap α β cmp) (p : α → β → Bool) : Bool",
isDeprecated := false }),"Std.HashSet", "Std.HashSet.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashSet.any,
renderedStatement := "Std.HashSet.any.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashSet α) (p : α → Bool) : Bool",
isDeprecated := false }),"Std.HashSet.Raw", "Std.HashSet.Raw.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.HashSet.Raw.any,
renderedStatement := "Std.HashSet.Raw.any.{u} {α : Type u} (m : Std.HashSet.Raw α) (p : α → Bool) : Bool",
isDeprecated := false }),"Std.TreeSet", "Std.TreeSet.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeSet.any,
renderedStatement := "Std.TreeSet.any.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet α cmp) (p : α → Bool) : Bool",
isDeprecated := false }),"Std.TreeSet.Raw", "Std.TreeSet.Raw.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.TreeSet.Raw.any,
renderedStatement := "Std.TreeSet.Raw.any.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet.Raw α cmp) (p : α → Bool) : Bool",
isDeprecated := false }),"Std.ExtTreeSet", "Std.ExtTreeSet.any", Grove.Framework.Subexpression.State.declaration
(Grove.Framework.Declaration.def
{ name := `Std.ExtTreeSet.any,
renderedStatement := "Std.ExtTreeSet.any.{u} {α : Type u} {cmp : αα → Ordering} [Std.TransCmp cmp] (t : Std.ExtTreeSet α cmp)\n (p : α → Bool) : Bool",
isDeprecated := false }),]
metadata := {
status := .bad
comment := "Missing for some containers"
}
def «c1d181f6-3204-4956-946f-e81619f9feb4» : AssociationTable.Fact .subexpression where
widgetId := "associative-query-operations"
factId := "c1d181f6-3204-4956-946f-e81619f9feb4"
rowId := "c1d181f6-3204-4956-946f-e81619f9feb4"
rowState := #["Std.DTreeMap", "Std.DTreeMap.all", .declaration (Declaration.def {
name := `Std.DTreeMap.all
renderedStatement := "Std.DTreeMap.all.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap α β cmp)\n (p : (a : α) → β a → Bool) : Bool"
isDeprecated := false
}
),"Std.DTreeMap.Raw", "Std.DTreeMap.Raw.all", .declaration (Declaration.def {
name := `Std.DTreeMap.Raw.all
renderedStatement := "Std.DTreeMap.Raw.all.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} (t : Std.DTreeMap.Raw α β cmp)\n (p : (a : α) → β a → Bool) : Bool"
isDeprecated := false
}
),"Std.ExtDTreeMap", "Std.ExtDTreeMap.all", .declaration (Declaration.def {
name := `Std.ExtDTreeMap.all
renderedStatement := "Std.ExtDTreeMap.all.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n (t : Std.ExtDTreeMap α β cmp) (p : (a : α) → β a → Bool) : Bool"
isDeprecated := false
}
),"Std.TreeMap", "Std.TreeMap.all", .declaration (Declaration.def {
name := `Std.TreeMap.all
renderedStatement := "Std.TreeMap.all.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap α β cmp) (p : α → β → Bool) :\n Bool"
isDeprecated := false
}
),"Std.TreeMap.Raw", "Std.TreeMap.Raw.all", .declaration (Declaration.def {
name := `Std.TreeMap.Raw.all
renderedStatement := "Std.TreeMap.Raw.all.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap.Raw α β cmp)\n (p : α → β → Bool) : Bool"
isDeprecated := false
}
),"Std.ExtTreeMap", "Std.ExtTreeMap.all", .declaration (Declaration.def {
name := `Std.ExtTreeMap.all
renderedStatement := "Std.ExtTreeMap.all.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n (t : Std.ExtTreeMap α β cmp) (p : α → β → Bool) : Bool"
isDeprecated := false
}
),"Std.HashSet", "Std.HashSet.all", .declaration (Declaration.def {
name := `Std.HashSet.all
renderedStatement := "Std.HashSet.all.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashSet α) (p : α → Bool) : Bool"
isDeprecated := false
}
),"Std.HashSet.Raw", "Std.HashSet.Raw.all", .declaration (Declaration.def {
name := `Std.HashSet.Raw.all
renderedStatement := "Std.HashSet.Raw.all.{u} {α : Type u} (m : Std.HashSet.Raw α) (p : α → Bool) : Bool"
isDeprecated := false
}
),"Std.TreeSet", "Std.TreeSet.all", .declaration (Declaration.def {
name := `Std.TreeSet.all
renderedStatement := "Std.TreeSet.all.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet α cmp) (p : α → Bool) : Bool"
isDeprecated := false
}
),"Std.TreeSet.Raw", "Std.TreeSet.Raw.all", .declaration (Declaration.def {
name := `Std.TreeSet.Raw.all
renderedStatement := "Std.TreeSet.Raw.all.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet.Raw α cmp) (p : α → Bool) : Bool"
isDeprecated := false
}
),"Std.ExtTreeSet", "Std.ExtTreeSet.all", .declaration (Declaration.def {
name := `Std.ExtTreeSet.all
renderedStatement := "Std.ExtTreeSet.all.{u} {α : Type u} {cmp : αα → Ordering} [Std.TransCmp cmp] (t : Std.ExtTreeSet α cmp)\n (p : α → Bool) : Bool"
isDeprecated := false
}
),]
metadata := {
status := .bad
comment := "Missing for some containers"
}
def «efe57f41-7db7-4303-b3a6-5216a70c43ce» : AssociationTable.Fact .subexpression where
widgetId := "associative-query-operations"
factId := "efe57f41-7db7-4303-b3a6-5216a70c43ce"
rowId := "efe57f41-7db7-4303-b3a6-5216a70c43ce"
rowState := #["Std.DHashMap", "Std.DHashMap.getD", .declaration (Declaration.def {
name := `Std.DHashMap.getD
renderedStatement := "Std.DHashMap.getD.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [LawfulBEq α]\n (m : Std.DHashMap α β) (a : α) (fallback : β a) : β a"
isDeprecated := false
}
),"Std.DHashMap.Raw", "Std.DHashMap.Raw.getD", .declaration (Declaration.def {
name := `Std.DHashMap.Raw.getD
renderedStatement := "Std.DHashMap.Raw.getD.{u, v} {α : Type u} {β : α → Type v} [BEq α] [Hashable α] [LawfulBEq α] (m : Std.DHashMap.Raw α β)\n (a : α) (fallback : β a) : β a"
isDeprecated := false
}
),"Std.ExtDHashMap", "Std.ExtDHashMap.getD", .declaration (Declaration.def {
name := `Std.ExtDHashMap.getD
renderedStatement := "Std.ExtDHashMap.getD.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [LawfulBEq α]\n (m : Std.ExtDHashMap α β) (a : α) (fallback : β a) : β a"
isDeprecated := false
}
),"Std.DTreeMap", "Std.DTreeMap.getD", .declaration (Declaration.def {
name := `Std.DTreeMap.getD
renderedStatement := "Std.DTreeMap.getD.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.LawfulEqCmp cmp]\n (t : Std.DTreeMap α β cmp) (a : α) (fallback : β a) : β a"
isDeprecated := false
}
),"Std.DTreeMap.Raw", "Std.DTreeMap.Raw.getD", .declaration (Declaration.def {
name := `Std.DTreeMap.Raw.getD
renderedStatement := "Std.DTreeMap.Raw.getD.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.LawfulEqCmp cmp]\n (t : Std.DTreeMap.Raw α β cmp) (a : α) (fallback : β a) : β a"
isDeprecated := false
}
),"Std.ExtDTreeMap", "Std.ExtDTreeMap.getD", .declaration (Declaration.def {
name := `Std.ExtDTreeMap.getD
renderedStatement := "Std.ExtDTreeMap.getD.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n [Std.LawfulEqCmp cmp] (t : Std.ExtDTreeMap α β cmp) (a : α) (fallback : β a) : β a"
isDeprecated := false
}
),"Std.HashMap", "Std.HashMap.getD", .declaration (Declaration.def {
name := `Std.HashMap.getD
renderedStatement := "Std.HashMap.getD.{u, v} {α : Type u} {β : Type v} {x✝ : BEq α} {x✝¹ : Hashable α} (m : Std.HashMap α β) (a : α)\n (fallback : β) : β"
isDeprecated := false
}
),"Std.HashMap.Raw", "Std.HashMap.Raw.getD", .declaration (Declaration.def {
name := `Std.HashMap.Raw.getD
renderedStatement := "Std.HashMap.Raw.getD.{u, v} {α : Type u} {β : Type v} [BEq α] [Hashable α] (m : Std.HashMap.Raw α β) (a : α)\n (fallback : β) : β"
isDeprecated := false
}
),"Std.ExtHashMap", "Std.ExtHashMap.getD", .declaration (Declaration.def {
name := `Std.ExtHashMap.getD
renderedStatement := "Std.ExtHashMap.getD.{u, v} {α : Type u} {β : Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashMap α β) (a : α) (fallback : β) : β"
isDeprecated := false
}
),"Std.TreeMap", "Std.TreeMap.getD", .declaration (Declaration.def {
name := `Std.TreeMap.getD
renderedStatement := "Std.TreeMap.getD.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap α β cmp) (a : α)\n (fallback : β) : β"
isDeprecated := false
}
),"Std.TreeMap.Raw", "Std.TreeMap.Raw.getD", .declaration (Declaration.def {
name := `Std.TreeMap.Raw.getD
renderedStatement := "Std.TreeMap.Raw.getD.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} (t : Std.TreeMap.Raw α β cmp) (a : α)\n (fallback : β) : β"
isDeprecated := false
}
),"Std.ExtTreeMap", "Std.ExtTreeMap.getD", .declaration (Declaration.def {
name := `Std.ExtTreeMap.getD
renderedStatement := "Std.ExtTreeMap.getD.{u, v} {α : Type u} {β : Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n (t : Std.ExtTreeMap α β cmp) (a : α) (fallback : β) : β"
isDeprecated := false
}
),"Std.HashSet", "Std.HashSet.getD", .declaration (Declaration.def {
name := `Std.HashSet.getD
renderedStatement := "Std.HashSet.getD.{u} {α : Type u} [BEq α] [Hashable α] (m : Std.HashSet α) (a fallback : α) : α"
isDeprecated := false
}
),"Std.HashSet.Raw", "Std.HashSet.Raw.getD", .declaration (Declaration.def {
name := `Std.HashSet.Raw.getD
renderedStatement := "Std.HashSet.Raw.getD.{u} {α : Type u} [BEq α] [Hashable α] (m : Std.HashSet.Raw α) (a fallback : α) : α"
isDeprecated := false
}
),"Std.ExtHashSet", "Std.ExtHashSet.getD", .declaration (Declaration.def {
name := `Std.ExtHashSet.getD
renderedStatement := "Std.ExtHashSet.getD.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashSet α) (a fallback : α) : α"
isDeprecated := false
}
),"Std.TreeSet", "Std.TreeSet.getD", .declaration (Declaration.def {
name := `Std.TreeSet.getD
renderedStatement := "Std.TreeSet.getD.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet α cmp) (a fallback : α) : α"
isDeprecated := false
}
),"Std.TreeSet.Raw", "Std.TreeSet.Raw.getD", .declaration (Declaration.def {
name := `Std.TreeSet.Raw.getD
renderedStatement := "Std.TreeSet.Raw.getD.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet.Raw α cmp) (a fallback : α) : α"
isDeprecated := false
}
),"Std.ExtTreeSet", "Std.ExtTreeSet.getD", .declaration (Declaration.def {
name := `Std.ExtTreeSet.getD
renderedStatement := "Std.ExtTreeSet.getD.{u} {α : Type u} {cmp : αα → Ordering} [Std.TransCmp cmp] (t : Std.ExtTreeSet α cmp)\n (a fallback : α) : α"
isDeprecated := false
}
),]
metadata := {
status := .done
comment := ""
}
def «e23b1119-3b57-433e-a68d-68fd70b9943d» : AssociationTable.Fact .subexpression where
widgetId := "associative-query-operations"
factId := "e23b1119-3b57-433e-a68d-68fd70b9943d"
rowId := "e23b1119-3b57-433e-a68d-68fd70b9943d"
rowState := #["Std.DHashMap", "Std.DHashMap.get", .declaration (Declaration.def {
name := `Std.DHashMap.get
renderedStatement := "Std.DHashMap.get.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [LawfulBEq α]\n (m : Std.DHashMap α β) (a : α) (h : a ∈ m) : β a"
isDeprecated := false
}
),"Std.DHashMap.Raw", "Std.DHashMap.Raw.Const.get", .declaration (Declaration.def {
name := `Std.DHashMap.Raw.Const.get
renderedStatement := "Std.DHashMap.Raw.Const.get.{u, v} {α : Type u} {β : Type v} [BEq α] [Hashable α] (m : Std.DHashMap.Raw α fun x => β)\n (a : α) (h : a ∈ m) : β"
isDeprecated := false
}
),"Std.ExtDHashMap", "Std.ExtDHashMap.get", .declaration (Declaration.def {
name := `Std.ExtDHashMap.get
renderedStatement := "Std.ExtDHashMap.get.{u, v} {α : Type u} {β : α → Type v} {x✝ : BEq α} {x✝¹ : Hashable α} [LawfulBEq α]\n (m : Std.ExtDHashMap α β) (a : α) (h : a ∈ m) : β a"
isDeprecated := false
}
),"Std.DTreeMap", "Std.DTreeMap.get", .declaration (Declaration.def {
name := `Std.DTreeMap.get
renderedStatement := "Std.DTreeMap.get.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.LawfulEqCmp cmp]\n (t : Std.DTreeMap α β cmp) (a : α) (h : a ∈ t) : β a"
isDeprecated := false
}
),"Std.DTreeMap.Raw", "Std.DTreeMap.Raw.get", .declaration (Declaration.def {
name := `Std.DTreeMap.Raw.get
renderedStatement := "Std.DTreeMap.Raw.get.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.LawfulEqCmp cmp]\n (t : Std.DTreeMap.Raw α β cmp) (a : α) (h : a ∈ t) : β a"
isDeprecated := false
}
),"Std.ExtDTreeMap", "Std.ExtDTreeMap.get", .declaration (Declaration.def {
name := `Std.ExtDTreeMap.get
renderedStatement := "Std.ExtDTreeMap.get.{u, v} {α : Type u} {β : α → Type v} {cmp : αα → Ordering} [Std.TransCmp cmp]\n [Std.LawfulEqCmp cmp] (t : Std.ExtDTreeMap α β cmp) (a : α) (h : a ∈ t) : β a"
isDeprecated := false
}
),"Std.HashMap", "app (GetElem.getElem) (Std.HashMap*)", Grove.Framework.Subexpression.State.predicate
{ key := "app (GetElem.getElem) (Std.HashMap*)", displayShort := "Std.HashMap[·]" },"Std.HashMap.Raw", "app (GetElem.getElem) (Std.HashMap.Raw*)", Grove.Framework.Subexpression.State.predicate
{ key := "app (GetElem.getElem) (Std.HashMap.Raw*)", displayShort := "Std.HashMap.Raw[·]" },"Std.ExtHashMap", "app (GetElem.getElem) (Std.ExtHashMap*)", Grove.Framework.Subexpression.State.predicate
{ key := "app (GetElem.getElem) (Std.ExtHashMap*)", displayShort := "Std.ExtHashMap[·]" },"Std.TreeMap", "app (GetElem.getElem) (Std.TreeMap*)", Grove.Framework.Subexpression.State.predicate
{ key := "app (GetElem.getElem) (Std.TreeMap*)", displayShort := "Std.TreeMap[·]" },"Std.TreeMap.Raw", "app (GetElem.getElem) (Std.TreeMap.Raw*)", Grove.Framework.Subexpression.State.predicate
{ key := "app (GetElem.getElem) (Std.TreeMap.Raw*)", displayShort := "Std.TreeMap.Raw[·]" },"Std.ExtTreeMap", "app (GetElem.getElem) (Std.ExtTreeMap*)", Grove.Framework.Subexpression.State.predicate
{ key := "app (GetElem.getElem) (Std.ExtTreeMap*)", displayShort := "Std.ExtTreeMap[·]" },"Std.HashSet", "Std.HashSet.get", .declaration (Declaration.def {
name := `Std.HashSet.get
renderedStatement := "Std.HashSet.get.{u} {α : Type u} [BEq α] [Hashable α] (m : Std.HashSet α) (a : α) (h : a ∈ m) : α"
isDeprecated := false
}
),"Std.HashSet.Raw", "Std.HashSet.Raw.get", .declaration (Declaration.def {
name := `Std.HashSet.Raw.get
renderedStatement := "Std.HashSet.Raw.get.{u} {α : Type u} [BEq α] [Hashable α] (m : Std.HashSet.Raw α) (a : α) (h : a ∈ m) : α"
isDeprecated := false
}
),"Std.ExtHashSet", "Std.ExtHashSet.get", .declaration (Declaration.def {
name := `Std.ExtHashSet.get
renderedStatement := "Std.ExtHashSet.get.{u} {α : Type u} {x✝ : BEq α} {x✝¹ : Hashable α} [EquivBEq α] [LawfulHashable α]\n (m : Std.ExtHashSet α) (a : α) (h : a ∈ m) : α"
isDeprecated := false
}
),"Std.TreeSet", "Std.TreeSet.get", .declaration (Declaration.def {
name := `Std.TreeSet.get
renderedStatement := "Std.TreeSet.get.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet α cmp) (a : α) (h : a ∈ t) : α"
isDeprecated := false
}
),"Std.TreeSet.Raw", "Std.TreeSet.Raw.get", .declaration (Declaration.def {
name := `Std.TreeSet.Raw.get
renderedStatement := "Std.TreeSet.Raw.get.{u} {α : Type u} {cmp : αα → Ordering} (t : Std.TreeSet.Raw α cmp) (a : α) (h : a ∈ t) : α"
isDeprecated := false
}
),"Std.ExtTreeSet", "Std.ExtTreeSet.get", .declaration (Declaration.def {
name := `Std.ExtTreeSet.get
renderedStatement := "Std.ExtTreeSet.get.{u} {α : Type u} {cmp : αα → Ordering} [Std.TransCmp cmp] (t : Std.ExtTreeSet α cmp) (a : α)\n (h : a ∈ t) : α"
isDeprecated := false
}
),]
metadata := {
status := .bad
comment := "Should *Set have GetElem?"
}
def table : AssociationTable.Data .subexpression where
widgetId := "associative-query-operations"
rows := #[
"01f88623-fa5f-4380-9772-b30f2fec5c94", "isEmpty", #["Std.DHashMap", "Std.DHashMap.isEmpty","Std.DHashMap.Raw", "Std.DHashMap.Raw.isEmpty","Std.ExtDHashMap", "Std.ExtDHashMap.isEmpty","Std.DTreeMap", "Std.DTreeMap.isEmpty","Std.DTreeMap.Raw", "Std.DTreeMap.Raw.isEmpty","Std.ExtDTreeMap", "Std.ExtDTreeMap.isEmpty","Std.HashMap", "Std.HashMap.isEmpty","Std.HashMap.Raw", "Std.HashMap.Raw.isEmpty","Std.ExtHashMap", "Std.ExtHashMap.isEmpty","Std.TreeMap", "Std.TreeMap.isEmpty","Std.TreeMap.Raw", "Std.TreeMap.Raw.isEmpty","Std.ExtTreeMap", "Std.ExtTreeMap.isEmpty","Std.HashSet", "Std.HashSet.isEmpty","Std.HashSet.Raw", "Std.HashSet.Raw.isEmpty","Std.ExtHashSet", "Std.ExtHashSet.isEmpty","Std.TreeSet", "Std.TreeSet.isEmpty","Std.TreeSet.Raw", "Std.TreeSet.Raw.isEmpty","Std.ExtTreeSet", "Std.ExtTreeSet.isEmpty",],
"f084f852-af71-45b6-8ab3-d251a8144f72", "size", #["Std.DHashMap", "Std.DHashMap.size","Std.DHashMap.Raw", "Std.DHashMap.Raw.size","Std.ExtDHashMap", "Std.ExtDHashMap.size","Std.DTreeMap", "Std.DTreeMap.size","Std.DTreeMap.Raw", "Std.DTreeMap.Raw.size","Std.ExtDTreeMap", "Std.ExtDTreeMap.size","Std.HashMap", "Std.HashMap.size","Std.HashMap.Raw", "Std.HashMap.Raw.size","Std.ExtHashMap", "Std.ExtHashMap.size","Std.TreeMap", "Std.TreeMap.size","Std.TreeMap.Raw", "Std.TreeMap.Raw.size","Std.ExtTreeMap", "Std.ExtTreeMap.size","Std.HashSet", "Std.HashSet.size","Std.HashSet.Raw", "Std.HashSet.Raw.size","Std.ExtHashSet", "Std.ExtHashSet.size","Std.TreeSet", "Std.TreeSet.size","Std.TreeSet.Raw", "Std.TreeSet.Raw.size","Std.ExtTreeSet", "Std.ExtTreeSet.size",],
"f4e6fa70-5aed-439d-aaad-5f4ced65bf7b", "any", #["Std.DTreeMap", "Std.DTreeMap.any","Std.DTreeMap.Raw", "Std.DTreeMap.Raw.any","Std.ExtDTreeMap", "Std.ExtDTreeMap.any","Std.TreeMap", "Std.TreeMap.any","Std.TreeMap.Raw", "Std.TreeMap.Raw.any","Std.ExtTreeMap", "Std.ExtTreeMap.any","Std.HashSet", "Std.HashSet.any","Std.HashSet.Raw", "Std.HashSet.Raw.any","Std.TreeSet", "Std.TreeSet.any","Std.TreeSet.Raw", "Std.TreeSet.Raw.any","Std.ExtTreeSet", "Std.ExtTreeSet.any",],
"c1d181f6-3204-4956-946f-e81619f9feb4", "all", #["Std.DTreeMap", "Std.DTreeMap.all","Std.DTreeMap.Raw", "Std.DTreeMap.Raw.all","Std.ExtDTreeMap", "Std.ExtDTreeMap.all","Std.TreeMap", "Std.TreeMap.all","Std.TreeMap.Raw", "Std.TreeMap.Raw.all","Std.ExtTreeMap", "Std.ExtTreeMap.all","Std.HashSet", "Std.HashSet.all","Std.HashSet.Raw", "Std.HashSet.Raw.all","Std.TreeSet", "Std.TreeSet.all","Std.TreeSet.Raw", "Std.TreeSet.Raw.all","Std.ExtTreeSet", "Std.ExtTreeSet.all",],
"efe57f41-7db7-4303-b3a6-5216a70c43ce", "getD", #["Std.DHashMap", "Std.DHashMap.getD","Std.DHashMap.Raw", "Std.DHashMap.Raw.getD","Std.ExtDHashMap", "Std.ExtDHashMap.getD","Std.DTreeMap", "Std.DTreeMap.getD","Std.DTreeMap.Raw", "Std.DTreeMap.Raw.getD","Std.ExtDTreeMap", "Std.ExtDTreeMap.getD","Std.HashMap", "Std.HashMap.getD","Std.HashMap.Raw", "Std.HashMap.Raw.getD","Std.ExtHashMap", "Std.ExtHashMap.getD","Std.TreeMap", "Std.TreeMap.getD","Std.TreeMap.Raw", "Std.TreeMap.Raw.getD","Std.ExtTreeMap", "Std.ExtTreeMap.getD","Std.HashSet", "Std.HashSet.getD","Std.HashSet.Raw", "Std.HashSet.Raw.getD","Std.ExtHashSet", "Std.ExtHashSet.getD","Std.TreeSet", "Std.TreeSet.getD","Std.TreeSet.Raw", "Std.TreeSet.Raw.getD","Std.ExtTreeSet", "Std.ExtTreeSet.getD",],
"e23b1119-3b57-433e-a68d-68fd70b9943d", "getElem", #["Std.DHashMap", "Std.DHashMap.get","Std.DHashMap.Raw", "Std.DHashMap.Raw.Const.get","Std.ExtDHashMap", "Std.ExtDHashMap.get","Std.DTreeMap", "Std.DTreeMap.get","Std.DTreeMap.Raw", "Std.DTreeMap.Raw.get","Std.ExtDTreeMap", "Std.ExtDTreeMap.get","Std.HashMap", "app (GetElem.getElem) (Std.HashMap*)","Std.HashMap.Raw", "app (GetElem.getElem) (Std.HashMap.Raw*)","Std.ExtHashMap", "app (GetElem.getElem) (Std.ExtHashMap*)","Std.TreeMap", "app (GetElem.getElem) (Std.TreeMap*)","Std.TreeMap.Raw", "app (GetElem.getElem) (Std.TreeMap.Raw*)","Std.ExtTreeMap", "app (GetElem.getElem) (Std.ExtTreeMap*)","Std.HashSet", "Std.HashSet.get","Std.HashSet.Raw", "Std.HashSet.Raw.get","Std.ExtHashSet", "Std.ExtHashSet.get","Std.TreeSet", "Std.TreeSet.get","Std.TreeSet.Raw", "Std.TreeSet.Raw.get","Std.ExtTreeSet", "Std.ExtTreeSet.get",],
]
facts := #[
«01f88623-fa5f-4380-9772-b30f2fec5c94»,
«f084f852-af71-45b6-8ab3-d251a8144f72»,
«f4e6fa70-5aed-439d-aaad-5f4ced65bf7b»,
«c1d181f6-3204-4956-946f-e81619f9feb4»,
«efe57f41-7db7-4303-b3a6-5216a70c43ce»,
«e23b1119-3b57-433e-a68d-68fd70b9943d»,
]
def restoreState : RestoreStateM Unit := do
addAssociationTable table

View File

@@ -0,0 +1,31 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import GroveStdlib.Std.CoreTypesAndOperations
import GroveStdlib.Std.LanguageConstructs
import GroveStdlib.Std.Libraries
import GroveStdlib.Std.OperatingSystemAbstractions
open Grove.Framework Widget
namespace GroveStdlib
namespace Std
def introduction : Node :=
.text "Welcome to the interactive Lean standard library outline!"
end Std
def std : Node :=
.section "stdlib" "The Lean standard library" #[
Std.introduction,
Std.coreTypesAndOperations,
Std.languageConstructs,
Std.libraries,
Std.operatingSystemAbstractions
]
end GroveStdlib

View File

@@ -0,0 +1,28 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
import GroveStdlib.Std.CoreTypesAndOperations.BasicTypes
import GroveStdlib.Std.CoreTypesAndOperations.Containers
import GroveStdlib.Std.CoreTypesAndOperations.Numbers
import GroveStdlib.Std.CoreTypesAndOperations.StringsAndFormatting
open Grove.Framework Widget
namespace GroveStdlib.Std
namespace CoreTypesAndOperations
end CoreTypesAndOperations
def coreTypesAndOperations : Node :=
.section "core-types-and-operations" "Core types and operations" #[
CoreTypesAndOperations.basicTypes,
CoreTypesAndOperations.containers,
CoreTypesAndOperations.numbers,
CoreTypesAndOperations.stringsAndFormatting
]
end GroveStdlib.Std

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.CoreTypesAndOperations
namespace BasicTypes
end BasicTypes
def basicTypes : Node :=
.section "basic-types" "Basic types" #[]
end GroveStdlib.Std.CoreTypesAndOperations

View File

@@ -0,0 +1,58 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.CoreTypesAndOperations
namespace Containers
namespace SequentialContainers
end SequentialContainers
def sequentialContainers : Node :=
.section "sequential-containers" "Sequential containers" #[]
namespace AssociativeContainers
def associativeQueryOperations : AssociationTable .subexpression
[`Std.DHashMap, `Std.DHashMap.Raw, `Std.ExtDHashMap, `Std.DTreeMap, `Std.DTreeMap.Raw, `Std.ExtDTreeMap, `Std.HashMap,
`Std.HashMap.Raw, `Std.ExtHashMap, `Std.TreeMap, `Std.TreeMap.Raw, `Std.ExtTreeMap, `Std.HashSet, `Std.HashSet.Raw, `Std.ExtHashSet,
`Std.TreeSet, `Std.TreeSet.Raw, `Std.ExtTreeSet] where
id := "associative-query-operations"
title := "Associative query operations"
description := "Operations that take as input an associative container and return a 'single' piece of information (e.g., `GetElem` or `isEmpty`, but not `toList`)."
dataSources n :=
(DataSource.declarationsInNamespace n .definitionsOnly)
|>.map Subexpression.declaration
|>.or (DataSource.getElem n)
end AssociativeContainers
def associativeContainers : Node :=
.section "associative-containers" "Associative containers" #[
.associationTable AssociativeContainers.associativeQueryOperations
]
namespace PersistentDataStructures
end PersistentDataStructures
def persistentDataStructures : Node :=
.section "persistent-data-structures" "Persistent data structures" #[]
end Containers
def containers : Node :=
.section "containers" "Containers" #[
Containers.sequentialContainers,
Containers.associativeContainers,
Containers.persistentDataStructures
]
end GroveStdlib.Std.CoreTypesAndOperations

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.CoreTypesAndOperations
namespace Numbers
end Numbers
def numbers : Node :=
.section "numbers" "Numbers" #[]
end GroveStdlib.Std.CoreTypesAndOperations

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.CoreTypesAndOperations
namespace StringsAndFormatting
end StringsAndFormatting
def stringsAndFormatting : Node :=
.section "strings-and-formatting" "Strings and formatting" #[]
end GroveStdlib.Std.CoreTypesAndOperations

View File

@@ -0,0 +1,26 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
import GroveStdlib.Std.LanguageConstructs.ComparisonOrderingHashing
import GroveStdlib.Std.LanguageConstructs.Monads
import GroveStdlib.Std.LanguageConstructs.RangesAndIterators
open Grove.Framework Widget
namespace GroveStdlib.Std
namespace LanguageConstructs
end LanguageConstructs
def languageConstructs : Node :=
.section "language-constructs" "Language constructs" #[
LanguageConstructs.comparisonOrderingHashing,
LanguageConstructs.monads,
LanguageConstructs.rangesAndIterators
]
end GroveStdlib.Std

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.LanguageConstructs
namespace ComparisonOrderingHashing
end ComparisonOrderingHashing
def comparisonOrderingHashing : Node :=
.section "comparison-ordering-hashing" "Comparison, ordering, hashing" #[]
end GroveStdlib.Std.LanguageConstructs

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.LanguageConstructs
namespace Monads
end Monads
def monads : Node :=
.section "monads" "Monads" #[]
end GroveStdlib.Std.LanguageConstructs

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.LanguageConstructs
namespace RangesAndIterators
end RangesAndIterators
def rangesAndIterators : Node :=
.section "ranges-and-iterators" "Ranges and iterators" #[]
end GroveStdlib.Std.LanguageConstructs

View File

@@ -0,0 +1,24 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
import GroveStdlib.Std.Libraries.DateAndTime
import GroveStdlib.Std.Libraries.RandomNumbers
open Grove.Framework Widget
namespace GroveStdlib.Std
namespace Libraries
end Libraries
def libraries : Node :=
.section "libraries" "Libraries" #[
Libraries.dateAndTime,
Libraries.randomNumbers
]
end GroveStdlib.Std

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.Libraries
namespace DateAndTime
end DateAndTime
def dateAndTime : Node :=
.section "date-and-time" "Date and time" #[]
end GroveStdlib.Std.Libraries

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.Libraries
namespace RandomNumbers
end RandomNumbers
def randomNumbers : Node :=
.section "random-numbers" "Random numbers" #[]
end GroveStdlib.Std.Libraries

View File

@@ -0,0 +1,30 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
import GroveStdlib.Std.OperatingSystemAbstractions.AsynchronousIO
import GroveStdlib.Std.OperatingSystemAbstractions.BasicIO
import GroveStdlib.Std.OperatingSystemAbstractions.ConcurrencyAndParallelism
import GroveStdlib.Std.OperatingSystemAbstractions.EnvironmentFileSystemProcesses
import GroveStdlib.Std.OperatingSystemAbstractions.Locales
open Grove.Framework Widget
namespace GroveStdlib.Std
namespace OperatingSystemAbstractions
end OperatingSystemAbstractions
def operatingSystemAbstractions : Node :=
.section "operating-system-abstractions" "Operating system abstractions" #[
OperatingSystemAbstractions.asynchronousIO,
OperatingSystemAbstractions.basicIO,
OperatingSystemAbstractions.concurrencyAndParallelism,
OperatingSystemAbstractions.environmentFileSystemProcesses,
OperatingSystemAbstractions.locales
]
end GroveStdlib.Std

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.OperatingSystemAbstractions
namespace AsynchronousIO
end AsynchronousIO
def asynchronousIO : Node :=
.section "asynchronous-io" "Asynchronous I/O" #[]
end GroveStdlib.Std.OperatingSystemAbstractions

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.OperatingSystemAbstractions
namespace BasicIO
end BasicIO
def basicIO : Node :=
.section "basic-io" "Basic I/O" #[]
end GroveStdlib.Std.OperatingSystemAbstractions

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.OperatingSystemAbstractions
namespace ConcurrencyAndParallelism
end ConcurrencyAndParallelism
def concurrencyAndParallelism : Node :=
.section "concurrency-and-parallelism" "Concurrency and parallelism" #[]
end GroveStdlib.Std.OperatingSystemAbstractions

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.OperatingSystemAbstractions
namespace EnvironmentFileSystemProcesses
end EnvironmentFileSystemProcesses
def environmentFileSystemProcesses : Node :=
.section "environment-filesystem-processes" "Environment, file system, processes" #[]
end GroveStdlib.Std.OperatingSystemAbstractions

View File

@@ -0,0 +1,19 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import Grove.Framework
open Grove.Framework Widget
namespace GroveStdlib.Std.OperatingSystemAbstractions
namespace Locales
end Locales
def locales : Node :=
.section "locales" "Locales" #[]
end GroveStdlib.Std.OperatingSystemAbstractions

18
doc/std/grove/Main.lean Normal file
View File

@@ -0,0 +1,18 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import GroveStdlib.Std
import GroveStdlib.Generated
def config : Grove.Framework.Project.Configuration where
projectNamespace := `GroveStdlib
def project : Grove.Framework.Project where
config := config
rootNode := GroveStdlib.std
restoreState := GroveStdlib.Generated.restoreState
def main (args : List String) : IO UInt32 :=
Grove.Framework.main project #[`Init, `Std, `Lean] args

3
doc/std/grove/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Standard library QA
This directory contains the [Grove](github.com/TwoFX/grove) data files for the standard library.

10
doc/std/grove/grove-local.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
lake exe grove-stdlib --full metadata.json
cd .lake/packages/grove/frontend
npm install
if [ -f "../../../../invalidated.json" ]; then
GROVE_DATA_LOCATION=../../../../metadata.json GROVE_UPSTREAM_INVALIDATED_FACTS_LOCATION=../../../../invalidated.json npm run dev
else
GROVE_DATA_LOCATION=../../../../metadata.json npm run dev
fi

View File

@@ -0,0 +1,25 @@
{"version": "1.1.0",
"packagesDir": ".lake/packages",
"packages":
[{"url": "https://github.com/TwoFx/grove.git",
"type": "git",
"subDir": "backend",
"scope": "",
"rev": "e8127fc6554b99fb988ecdceb770a5e112afbe24",
"name": "grove",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
"inherited": false,
"configFile": "lakefile.toml"},
{"url": "https://github.com/leanprover/lean4-cli",
"type": "git",
"subDir": null,
"scope": "leanprover",
"rev": "1604206fcd0462da9a241beeac0e2df471647435",
"name": "Cli",
"manifestFile": "lake-manifest.json",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.toml"}],
"name": "grovestdlib",
"lakeDir": ".lake"}

View File

@@ -0,0 +1,18 @@
name = "grovestdlib"
version = "0.1.0"
defaultTargets = ["grove-stdlib"]
[[require]]
name = "grove"
git = "https://github.com/TwoFx/grove.git"
rev = "master"
subDir = "backend"
[[lean_lib]]
name = "GroveStdlib"
root = "GroveStdlib"
[[lean_exe]]
name = "grove-stdlib"
supportInterpreter = true
root = "Main"

View File

@@ -0,0 +1 @@
lean4

View File

@@ -0,0 +1,3 @@
#!/bin/sh
lake exe grove-stdlib --invalidated invalidated.json

3
doc/std/naming-tree.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 68 KiB

260
doc/std/naming.md Normal file
View File

@@ -0,0 +1,260 @@
# Standard library naming conventions
The easiest way to access a result in the standard library is to correctly guess the name of the declaration (possibly with the help of identifier autocompletion). This is faster and has lower friction than more sophisticated search tools, so easily guessable names (which are still reasonably short) make Lean users more productive.
The guide that follows contains very few hard rules, many heuristics and a selection of examples. It cannot and does not present a deterministic algorithm for choosing good names in all situations. It is intended as a living document that gets clarified and expanded as situations arise during code reviews for the standard library. If applying one of the suggestions in this guide leads to nonsensical results in a certain situation, it is
probably safe to ignore the suggestion (or even better, suggest a way to improve the suggestion).
## Prelude
Identifiers use a mix of `UpperCamelCase`, `lowerCamelCase` and `snake_case`, used for types, data, and theorems, respectively.
Structure fields should be named such that the projections have the correct names.
## Naming convention for types
When defining a type, i.e., a (possibly 0-ary) function whose codomain is Sort u for some u, it should be named in UpperCamelCase. Examples include `List`, and `List.IsPrefix`.
When defining a predicate, prefix the name by `Is`, like in `List.IsPrefix`. The `Is` prefix may be omitted if
* the resulting name would be ungrammatical, or
* the predicate depends on additional data in a way where the `Is` prefix would be confusing (like `List.Pairwise`), or
* the name is an adjective (like `Std.Time.Month.Ordinal.Valid`)
## Namespaces and generalized projection notation
Almost always, definitions and theorems relating to a type should be placed in a namespace with the same name as the type. For example, operations and theorems about lists should be placed in the `List` namespace, and operations and theorems about `Std.Time.PlainDate` should be placed in the `Std.Time.PlainDate` namespace.
Declarations in the root namespace will be relatively rare. The most common type of declaration in the root namespace are declarations about data and properties exported by notation type classes, as long as they are not about a specific type implementing that type class. For example, we have
```lean
theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b := sorry
```
in the root namespace, but
```lean
theorem List.cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
(a :: l₁ == b :: l₂) = (a == b && l₁ == l₂) := rfl
```
belongs in the `List` namespace.
Subtleties arise when multiple namespaces are in play. Generally, place your theorem in the most specific namespace that appears in one of the hypotheses of the theorem. The following names are both correct according to this convention:
```lean
theorem List.Sublist.reverse : l₁ <+ l₂ l₁.reverse <+ l₂.reverse := sorry
theorem List.reverse_sublist : l₁.reverse <+ l₂.reverse l₁ <+ l₂ := sorry
```
Notice that the second theorem does not have a hypothesis of type `List.Sublist l` for some `l`, so the name `List.Sublist.reverse_iff` would be incorrect.
The advantage of placing results in a namespace like `List.Sublist` is that it enables generalized projection notation, i.e., given `h : l₁ <+ l₂`,
one can write `h.reverse` to obtain a proof of `l₁.reverse <+ l₂.reverse`. Thinking about which dot notations are convenient can act as a guideline
for deciding where to place a theorem, and is, on occasion, a good reason to duplicate a theorem into multiple namespaces.
### The `Std` namespace
New types that are added will usually be placed in the `Std` namespace and in the `Std/` source directory, unless there are good reasons to place
them elsewhere.
Inside the `Std` namespace, all internal declarations should be `private` or else have a name component that clearly marks them as internal, preferably
`Internal`.
## Naming convention for data
When defining data, i.e., a (possibly 0-ary) function whose codomain is not Sort u, but has type Type u for some u, it should be named in lowerCamelCase. Examples include `List.append` and `List.isPrefixOf`.
If your data is morally fully specified by its type, then use the naming procedure for theorems described below and convert the result to lower camel case.
If your function returns an `Option`, consider adding `?` as a suffix. If your function may panic, consider adding `!` as a suffix. In many cases, there will be multiple variants of a function; one returning an option, one that may panic and possibly one that takes a proof argument.
## Naming algorithm for theorems and some definitions
There is, in principle, a general algorithm for naming a theorem. The problem with this algorithm is that it produces very long and unwieldy names which need to be shortened. So choosing a name for a declaration can be thought of as consisting of a mechanical part and a creative part.
Usually the first part is to decide which namespace the result should live in, according to the guidelines described above.
Next, consider the type of your declaration as a tree. Inner nodes of this tree are function types or function applications. Leaves of the tree are 0-ary functions or bound variables.
As an example, consider the following result from the standard library:
```lean
example {α : Type u} {β : Type v} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
[Inhabited β] {m : Std.HashMap α β} {a : α} {h' : a m} : m[a]? = some (m[a]'h') :=
sorry
```
The correct namespace is clearly `Std.HashMap`. The corresponding tree looks like this:
![](naming-tree.svg)
The preferred spelling of a notation can be looked up by hovering over the notation.
Now traverse the tree and build a name according to the following rules:
* When encountering a function type, first turn the result type into a name, then all of the argument types from left to right, and join the names using `_of_`.
* When encountering a function that is neither an infix notation nor a structure projection, first put the function name and then the arguments, joined by an underscore.
* When encountering an infix notation, join the arguments using the name of the notation, separated by underscores.
* When encountering a structure projection, proceed as for normal functions, but put the name of the projection last.
* When encountering a name, put it in lower camel case.
* Skip bound variables and proofs.
* Type class arguments are also generally skipped.
When encountering namespaces names, concatenate them in lower camel case.
Applying this algorithm to our example yields the name `Std.HashMap.getElem?_eq_optionSome_getElem_of_mem`.
From there, the name should be shortened, using the following heuristics:
* The namespace of functions can be omitted if it is clear from context or if the namespace is the current one. This is almost always the case.
* For infix operators, it is possible to leave out the RHS or the name of the notation and the RHS if they are clear from context.
* Hypotheses can be left out if it is clear that they are required or if they appear in the conclusion.
Based on this, here are some possible names for our example:
1. `Std.HashMap.getElem?_eq`
2. `Std.HashMap.getElem?_eq_of_mem`
3. `Std.HashMap.getElem?_eq_some`
4. `Std.HashMap.getElem?_eq_some_of_mem`
5. `Std.HashMap.getElem?_eq_some_getElem`
6. `Std.Hashmap.getElem?_eq_some_getElem_of_mem`
Choosing a good name among these then requires considering the context of the lemma. In this case it turns out that the first four options are underspecified as there is also a lemma relating `m[a]?` and `m[a]!` which could have the same name. This leaves the last two options, the first of which is shorter, and this is how the lemma is called in the Lean standard library.
Here are some additional examples:
```lean
example {x y : List α} (h : x <+: y) (hx : x []) :
x.head hx = y.head (h.ne_nil hx) := sorry
```
Since we have an `IsPrefix` parameter, this should live in the `List.IsPrefix` namespace, and the algorithm suggests `List.IsPrefix.head_eq_head_of_ne_nil`, which is shortened to `List.IsPrefix.head`. Note here the difference between the namespace name (`IsPrefix`) and the recommended spelling of the corresponding notation (`prefix`).
```lean
example : l₁ <+: l₂ reverse l₁ <:+ reverse l₂ := sorry
```
Again, this result should be in the `List.IsPrefix` namespace; the algorithm suggests `List.IsPrefix.reverse_prefix_reverse`, which becomes `List.IsPrefix.reverse`.
The following examples show how the traversal order often matters.
```lean
theorem Nat.mul_zero (n : Nat) : n * 0 = 0 := sorry
theorem Nat.zero_mul (n : Nat) : 0 * n = 0 := sorry
```
Here we see that one name may be a prefix of another name:
```lean
theorem Int.mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 := sorry
theorem Int.mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := sorry
```
It is usually a good idea to include the `iff` in a theorem name even if the name would still be unique without the name. For example,
```lean
theorem List.head?_eq_none_iff : l.head? = none l = [] := sorry
```
is a good name: if the lemma was simply called `List.head?_eq_none`, users might try to `apply` it when the goal is `l.head? = none`, leading
to confusion.
The more common you expect (or want) a theorem to be, the shorter you should try to make the name. For example, we have both
```lean
theorem Std.HashMap.getElem?_eq_none_of_contains_eq_false {a : α} : m.contains a = false m[a]? = none := sorry
theorem Std.HashMap.getElem?_eq_none {a : α} : ¬a m m[a]? = none := sorry
```
As users of the hash map are encouraged to use ∈ rather than contains, the second lemma gets the shorter name.
## Special cases
There are certain special “keywords” that may appear in identifiers.
| Keyword | Meaning | Example |
| :---- | :---- | :---- |
| `def` | Unfold a definition. Avoid this for public APIs. | `Nat.max_def` |
| `refl` | Theorems of the form `a R a`, where R is a reflexive relation and `a` is an explicit parameter | `Nat.le_refl` |
| `rfl` | Like `refl`, but with `a` implicit | `Nat.le_rfl` |
| `irrefl` | Theorems of the form `¬a R a`, where R is an irreflexive relation | `Nat.lt_irrefl` |
| `symm` | Theorems of the form `a R b → b R a`, where R is a symmetric relation (compare `comm` below) | `Eq.symm` |
| `trans` | Theorems of the form `a R b → b R c → a R c`, where R is a transitive relation (R may carry data) | `Eq.trans` |
| `antisymmm` | Theorems of the form `a R b → b R a → a = b`, where R is an antisymmetric relation | `Nat.le_antisymm` |
| `congr` | Theorems of the form `a R b → f a S f b`, where R and S are usually equivalence relations | `Std.HashMap.mem_congr` |
| `comm` | Theorems of the form `f a b = f b a` (compare `symm` above) | `Eq.comm`, `Nat.add_comm` |
| `assoc` | Theorems of the form `g (f a b) c = f a (g b c)` (note the order! In most cases, we have f = g) | `Nat.add_sub_assoc` |
| `distrib` | Theorems of the form `f (g a b) = g (f a) (f b)` | `Nat.add_left_distrib` |
| `self` | May be used if a variable appears multiple times in the conclusion | `List.mem_cons_self` |
| `inj` | Theorems of the form `f a = f b ↔ a = b`. | `Int.neg_inj`, `Nat.add_left_inj` |
| `cancel` | Theorems which have one of the forms `f a = f b → a = b` or `g (f a) = a`, where `f` and `g` usually involve a binary operator | `Nat.add_sub_cancel` |
| `cancel_iff` | Same as `inj`, but with different conventions for left and right (see below) | `Nat.add_right_cancel_iff` |
| `ext` | Theorems of the form `f a = f b → a = b`, where `f` usually involves some kind of projection | `List.ext_getElem`
| `mono` | Theorems of the form `a R b → f a R f b`, where `R` is a transitive relation | `List.countP_mono_left`
### Left and right
The keywords left and right are useful to disambiguate symmetric variants of theorems.
```lean
theorem imp_congr_left (h : a b) : (a c) (b c) := sorry
theorem imp_congr_right (h : a (b c)) : (a b) (a c) := sorry
```
It is not always obvious which version of a theorem should be “left” and which should be “right”.
Heuristically, the theorem should name the side which is “more variable”, but there are exceptions. For some of the special keywords discussed in this section, there are conventions which should be followed, as laid out in the following examples:
```lean
theorem Nat.left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := sorry
theorem Nat.right_distrib (n m k : Nat) : (n + m) * k = n * k + m * k := sorry
theorem Nat.add_left_cancel {n m k : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel {n m k : Nat} : n + m = k + m n = k := sorry
theorem Nat.add_left_cancel_iff {m k n : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel_iff {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_left_inj {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_right_inj {m k n : Nat} : n + m = n + k m = k := sorry
```
Note in particular that the convention is opposite for `cancel_iff` and `inj`.
```lean
theorem Nat.add_sub_self_left (a b : Nat) : (a + b) - a = b := sorry
theorem Nat.add_sub_self_right (a b : Nat) : (a + b) - b = a := sorry
theorem Nat.add_sub_cancel (n m : Nat) : (n + m) - m = n := sorry
```
## Primed names
Avoid disambiguating variants of a concept by appending the `'` character (e.g., introducing both `BitVec.sshiftRight` and `BitVec.sshiftRight'`), as it is impossible to tell the difference without looking at the type signature, the documentation or even the code, and even if you know what the two variants are there is no way to tell which is which. Prefer descriptive pairs `BitVec.sshiftRightNat`/`BitVec.sshiftRight`.
## Acronyms
For acronyms which are three letters or shorter, all letters should use the same case as dictated by the convention. For example, `IO` is a correct name for a type and the name `IO.Ref` may become `IORef` when used as part of a definition name and `ioRef` when used as part of a theorem name.
For acronyms which are at least four letters long, switch to lower case starting from the second letter. For example, `Json` is a correct name for a type, as is `JsonRPC`.
If an acronym is typically spelled using mixed case, this mixed spelling may be used in identifiers (for example `Std.Net.IPv4Addr`).
## Simp sets
Simp sets centered around a conversion function should be called `source_to_target`. For example, a simp set for the `BitVec.toNat` function, which goes from `BitVec` to
`Nat`, should be called `bitvec_to_nat`.
## Variable names
We make the following recommendations for variable names, but without insisting on them:
* Simple hypotheses should be named `h`, `h'`, or using a numerical sequence `h₁`, `h₂`, etc.
* Another common name for a simple hypothesis is `w` (for "witness").
* `List`s should be named `l`, `l'`, `l₁`, etc, or `as`, `bs`, etc.
(Use of `as`, `bs` is encouraged when the lists are of different types, e.g. `as : List α` and `bs : List β`.)
`xs`, `ys`, `zs` are allowed, but it is better if these are reserved for `Array` and `Vector`.
A list of lists may be named `L`.
* `Array`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the arrays are of different types, e.g. `as : Array α` and `bs : Array β`.
An array of arrays may be named `xss`.
* `Vector`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the vectors are of different types, e.g. `as : Vector α n` and `bs : Vector β n`.
A vector of vectors may be named `xss`.
* A common exception for `List` / `Array` / `Vector` is to use `acc` for an accumulator in a recursive function.
* `i`, `j`, `k` are preferred for numerical indices.
Descriptive names such as `start`, `stop`, `lo`, and `hi` are encouraged when they increase readability.
* `n`, `m` are preferred for sizes, e.g. in `Vector α n` or `xs.size = n`.
* `w` is preferred for the width of a `BitVec`.

522
doc/std/style.md Normal file
View File

@@ -0,0 +1,522 @@
# Standard library style
Please take some time to familiarize yourself with the stylistic conventions of
the project and the specific part of the library you are planning to contribute
to. While the Lean compiler may not enforce strict formatting rules,
consistently formatted code is much easier for others to read and maintain.
Attention to formatting is more than a cosmetic concern—it reflects the same
level of precision and care required to meet the deeper standards of the Lean 4
standard library.
Below we will give specific formatting prescriptions for various language constructs. Note that this style guide only applies to the Lean standard library, even though some examples in the guide are taken from other parts of the Lean code base.
## Basic whitespace rules
Syntactic elements (like `:`, `:=`, `|`, `::`) are surrounded by single spaces, with the exception of `,` and `;`, which are followed by a space but not preceded by one. Delimiters (like `()`, `{}`) do not have spaces on the inside, with the exceptions of subtype notation and structure instance notation.
Examples of correctly formatted function parameters:
* `{α : Type u}`
* `[BEq α]`
* `(cmp : αα → Ordering)`
* `(hab : a = b)`
* `{d : { l : List ((n : Nat) × Vector Nat n) // l.length % 2 = 0 }}`
Examples of correctly formatted terms:
* `1 :: [2, 3]`
* `letI : Ord α := ⟨cmp⟩; True`
* `(⟨2, 3⟩ : Nat × Nat)`
* `((2, 3) : Nat × Nat)`
* `{ x with fst := f (4 + f 0), snd := 4, .. }`
* `match 1 with | 0 => 0 | _ => 0`
* `fun ⟨a, b⟩ _ _ => by cases hab <;> apply id; rw [hbc]`
Configure your editor to remove trailing whitespace. If you have set up Visual Studio Code for Lean development in the recommended way then the correct setting is applied automatically.
## Splitting terms across multiple lines
When splitting a term across multiple lines, increase indentation by two spaces starting from the second line. When splitting a function application, try to split at argument boundaries. If an argument itself needs to be split, increase indentation further as appropriate.
When splitting at an infix operator, the operator goes at the end of the first line, not at the beginning of the second line. When splitting at an infix operator, you may or may not increase indentation depth, depending on what is more readable.
When splitting an `if`-`then`-`else` expression, the `then` keyword wants to stay with the condition and the `else` keyword wants to stay with the alternative term. Otherwise, indent as if the `if` and `else` keywords were arguments to the same function.
When splitting a comma-separated bracketed sequence (i.e., anonymous constructor application, list/array/vector literal, tuple) it is allowed to indent subsequent lines for alignment, but indenting by two spaces is also allowed.
Do not orphan parentheses.
Correct:
```lean
def MacroScopesView.isPrefixOf (v₁ v₂ : MacroScopesView) : Bool :=
v₁.name.isPrefixOf v₂.name &&
v₁.scopes == v₂.scopes &&
v₁.mainModule == v₂.mainModule &&
v₁.imported == v₂.imported
```
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
example : Nat :=
functionWithAVeryLongNameSoThatSomeArgumentsWillNotFit firstArgument secondArgument
(firstArgumentWithAnEquallyLongNameAndThatFunctionDoesHaveMoreArguments firstArgument
secondArgument)
secondArgument
```
Correct:
```lean
theorem size_alter [LawfulBEq α] {k : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).size =
if m.contains k && (f (m.get? k)).isNone then
m.size - 1
else if !m.contains k && (f (m.get? k)).isSome then
m.size + 1
else
m.size := by
simp_to_raw using Raw₀.size_alter
```
Correct:
```lean
theorem get?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).get? k' =
if h : k == k' then
cast (congrArg (Option β) (eq_of_beq h)) (f (m.get? k))
else m.get? k' := by
simp_to_raw using Raw₀.get?_alter
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Vector Nat :=
#v[imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm]
```
## Basic file structure
Every file should start with a copyright header, imports (in the standard library, this always includes a `prelude` declaration) and a module documentation string. There should not be a blank line between the copyright header and the imports. There should be a blank line between the imports and the module documentation string.
If you explicitly declare universe variables, do so at the top of the file, after the module documentation.
Correct:
```lean
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Yury Kudryashov
-/
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Find
/-!
**# Lemmas about `List.eraseP` and `List.erase`.**
-/
universe u u'
```
Syntax that is not supposed to be user-facing must be scoped. New public syntax must always be discussed explicitly in an RFC.
## Top-level commands and declarations
All top-level commands are unindented. Sectioning commands like `section` and `namespace` do not increase the indentation level.
Attributes may be placed on the same line as the rest of the command or on a separate line.
Multi-line declaration headers are indented by four spaces starting from the second line. The colon that indicates the type of a declaration may not be placed at the start of a line or on its own line.
Declaration bodies are indented by two spaces. Short declaration bodies may be placed on the same line as the declaration type.
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
```
Correct:
```lean
@[simp]
theorem eraseP_nil : [].eraseP p = [] := rfl
```
### Documentation comments
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
Declarations should be documented as required by the `docBlame` linter, which may be activated in a file using
`set_option linter.missingDocs true` (we allow these to stay in the file).
Single-line documentation comments should go on the same line as `/--`/`-/`, while multi-line documentation strings
should have these delimiters on their own line, with the documentation comment itself unindented.
Documentation comments must be written in the indicative mood. Use American orthography.
Correct:
```lean
/-- Carries out a monadic action on each mapping in the hash map in some order. -/
@[inline] def forM (f : (a : α) β a m PUnit) (b : Raw α β) : m PUnit :=
b.buckets.forM (AssocList.forM f)
```
Correct:
```lean
/--
Monadically computes a value by folding the given function over the mappings in the hash
map in some order.
-/
@[inline] def foldM (f : δ (a : α) β a m δ) (init : δ) (b : Raw α β) : m δ :=
b.buckets.foldlM (fun acc l => l.foldlM f acc) init
```
### Where clauses
The `where` keyword should be unindented, and all declarations bound by it should be indented with two spaces.
Blank lines before and after `where` and between declarations bound by `where` are optional and should be chosen
to maximize readability.
Correct:
```lean
@[simp] theorem partition_eq_filter_filter (p : α Bool) (l : List α) :
partition p l = (filter p l, filter (not p) l) := by
simp [partition, aux]
where
aux (l) {as bs} : partition.loop p l (as, bs) =
(as.reverse ++ filter p l, bs.reverse ++ filter (not p) l) :=
match l with
| [] => by simp [partition.loop, filter]
| a :: l => by cases pa : p a <;> simp [partition.loop, pa, aux, filter, append_assoc]
```
### Termination arguments
The `termination_by`, `decreasing_by`, `partial_fixpoint` keywords should be unindented. The associated terms should be indented like declaration bodies.
Correct:
```lean
@[inline] def multiShortOption (handle : Char m PUnit) (opt : String) : m PUnit := do
let rec loop (p : String.Pos) := do
if h : opt.atEnd p then
return
else
handle (opt.get' p h)
loop (opt.next' p h)
termination_by opt.utf8ByteSize - p.byteIdx
decreasing_by
simp [String.atEnd] at h
apply Nat.sub_lt_sub_left h
simp [String.lt_next opt p]
loop 1
```
Correct:
```lean
def substrEq (s1 : String) (off1 : String.Pos) (s2 : String) (off2 : String.Pos) (sz : Nat) : Bool :=
off1.byteIdx + sz s1.endPos.byteIdx && off2.byteIdx + sz s2.endPos.byteIdx && loop off1 off2 { byteIdx := off1.byteIdx + sz }
where
loop (off1 off2 stop1 : Pos) :=
if _h : off1.byteIdx < stop1.byteIdx then
let c₁ := s1.get off1
let c₂ := s2.get off2
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
else true
termination_by stop1.1 - off1.1
decreasing_by
have := Nat.sub_lt_sub_left _h (Nat.add_lt_add_left c₁.utf8Size_pos off1.1)
decreasing_tactic
```
Correct:
```lean
theorem div_add_mod (m n : Nat) : n * (m / n) + m % n = m := by
rw [div_eq, mod_eq]
have h : Decidable (0 < n n m) := inferInstance
cases h with
| isFalse h => simp [h]
| isTrue h =>
simp [h]
have ih := div_add_mod (m - n) n
rw [Nat.left_distrib, Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, ih, Nat.add_comm, Nat.sub_add_cancel h.2]
decreasing_by apply div_rec_lemma; assumption
```
### Deriving
The `deriving` clause should be unindented.
Correct:
```lean
structure Iterator where
array : ByteArray
idx : Nat
deriving Inhabited
```
## Notation and Unicode
We generally prefer to use notation as available. We usually prefer the Unicode versions of notations over non-Unicode alternatives.
There are some rules and exceptions regarding specific notations which are listed below:
* Sigma types: use `(a : α) × β a` instead of `Σ a, β a` or `Sigma β`.
* Function arrows: use `fun a => f x` instead of `fun x ↦ f x` or `λ x => f x` or any other variant.
## Language constructs
### Pattern matching, induction etc.
Match arms are indented at the indentation level that the match statement would have if it was on its own line. If the match is implicit, then the arms should be indented as if the match was explicitly given. The content of match arms is indented two spaces, so that it appears on the same level as the match pattern.
Correct:
```lean
def alter [BEq α] {β : Type v} (a : α) (f : Option β Option β) :
AssocList α (fun _ => β) AssocList α (fun _ => β)
| nil => match f none with
| none => nil
| some b => AssocList.cons a b nil
| cons k v l =>
if k == a then
match f v with
| none => l
| some b => cons a b l
else
cons k v (alter a f l)
```
Correct:
```lean
theorem eq_append_cons_of_mem {a : α} {xs : List α} (h : a xs) :
as bs, xs = as ++ a :: bs a as := by
induction xs with
| nil => cases h
| cons x xs ih =>
simp at h
cases h with
| inl h => exact [], xs, by simp_all
| inr h =>
by_cases h' : a = x
· subst h'
exact [], xs, by simp
· obtain as, bs, rfl, h := ih h
exact x :: as, bs, rfl, by simp_all
```
Aligning match arms is allowed, but not required.
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
### Structures
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
When using structure instance syntax over multiple lines, the opening brace should go on the preceding line, while the closing brace should go on its own line. The rest of the syntax should be indented by one level. During structure updates, the `with` clause goes on the same line as the opening brace. Aligning at the assignment symbol is allowed but not required.
Correct:
```lean
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
IO AddConstAsyncResult := do
let sigPromise IO.Promise.new
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
let asyncConst := {
constInfo := {
name := constName
kind
sig := sigPromise.result
constInfo := infoPromise.result
}
exts? := guard reportExts *> some extensionsPromise.result
}
return {
constName, kind
mainEnv := { env with
asyncConsts := env.asyncConsts.add asyncConst
checked := checkedEnvPromise.result }
asyncEnv := { env with
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
}
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
}
```
Correct:
```lean
instance [Inhabited α] : Inhabited (Descr α β σ) where
default := {
name := default
mkInitial := default
ofOLeanEntry := default
toOLeanEntry := default
addEntry := fun s _ => s
}
```
### Declaring structures
When defining structure types, do not parenthesize structure fields.
When declaring a structure type with a custom constructor name, put the custom name on its own line, indented like the
structure fields, and add a documentation comment.
Correct:
```lean
/--
A bitvector of the specified width.
This is represented as the underlying `Nat` number in both the runtime
and the kernel, inheriting all the special support for `Nat`.
-/
structure BitVec (w : Nat) where
/--
Constructs a `BitVec w` from a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector.
-/
ofFin ::
/--
Interprets a bitvector as a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector.
-/
toFin : Fin (2 ^ w)
```
## Tactic proofs
Tactic proofs are the most common thing to break during any kind of upgrade, so it is important to write them in a way that minimizes the likelihood of proofs breaking and that makes it easy to debug breakages if they do occur.
If there are multiple goals, either use a tactic combinator (like `all_goals`) to operate on all of them or a clearly specified subset, or use focus dots to work on goals one at a time. Using structured proofs (e.g., `induction … with`) is encouraged but not mandatory.
Squeeze non-terminal `simp`s (i.e., calls to `simp` which do not close the goal). Squeezing terminal `simp`s is generally discouraged, although there are exceptions (for example if squeezing yields a noticeable performance improvement).
Do not over-golf proofs in ways that are likely to lead to hard-to-debug breakage. Examples of things to avoid include complex multi-goal manipulation using lots of tactic combinators, complex uses of the substitution operator (`▸`) and clever point-free expressions (possibly involving anonymous function notation for multiple arguments).
Do not under-golf proofs: for routine tasks, use the most powerful tactics available.
Do not use `erw`. Avoid using `rfl` after `simp` or `rw`, as this usually indicates a missing lemma that should be used instead of `rfl`.
Use `(d)simp` or `rw` instead of `delta` or `unfold`. Use `refine` instead of `refine`. Use `haveI` and `letI` only if they are actually required.
Prefer highly automated tactics (like `grind` and `omega`) over low-level proofs, unless the automated tactic requires unacceptable additional imports or has bad performance. If you decide against using a highly automated tactic, leave a comment explaining the decision.
## `do` notation
The `do` keyword goes on the same line as the corresponding `:=` (or `=>`, or similar). `Id.run do` should be treated as if it was a bare `do`.
Use early `return` statements to reduce nesting depth and make the non-exceptional control flow of a function easier to see.
Alternatives for `let` matches may be placed in the same line or in the next line, indented by two spaces. If the term that is
being matched on is itself more than one line and there is an alternative present, consider breaking immediately after `←` and indent
as far as necessary to ensure readability.
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl? fvarId | throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl
findFunDecl? fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl?
fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def tagUntaggedGoals (parentTag : Name) (newSuffix : Name) (newGoals : List MVarId) : TacticM Unit := do
let mctx getMCtx
let mut numAnonymous := 0
for g in newGoals do
if mctx.isAnonymousMVar g then
numAnonymous := numAnonymous + 1
modifyMCtx fun mctx => Id.run do
let mut mctx := mctx
let mut idx := 1
for g in newGoals do
if mctx.isAnonymousMVar g then
if numAnonymous == 1 then
mctx := mctx.setMVarUserName g parentTag
else
mctx := mctx.setMVarUserName g (parentTag ++ newSuffix.appendIndexAfter idx)
idx := idx + 1
pure mctx
```

98
doc/std/vision.md Normal file
View File

@@ -0,0 +1,98 @@
# The Lean 4 standard library
Maintainer team (in alphabetical order): Henrik Böving, Markus Himmel
(community contact & external contribution coordinator), Kim Morrison, Paul
Reichert, Sofia Rodrigues.
The Lean 4 standard library is a core part of the Lean distribution, providing
essential building blocks for functional programming, verified software
development, and software verification. Unlike the standard libraries of most
other languages, many of its components are formally verified and can be used
as part of verified applications.
The standard library is a public API that contains the components listed in the
standard library outline below. Not all public APIs in the Lean distribution
are part of the standard library, and the standard library does not correspond
to a certain directory within the Lean source repository (like `Std`). For
example, the metaprogramming framework is not part of the standard library, but
basic types like `True` and `Nat` are.
The standard library is under active development. Our guiding principles are:
* Provide comprehensive, verified building blocks for real-world software.
* Build a public API of the highest quality with excellent internal consistency.
* Carefully optimize components that may be used in performance-critical software.
* Ensure smooth adoption and maintenance for users.
* Offer excellent documentation, example projects, and guides.
* Provide a reliable and extensible basis that libraries for software
development, software verification and mathematics can build on.
The standard library is principally developed by the Lean FRO. Community
contributions are welcome. If you would like to contribute, please refer to the
call for contributions below.
### Standard library outline
1. Core types and operations
1. Basic types
2. Numeric types, including floating point numbers
3. Containers
4. Strings and formatting
2. Language constructs
1. Ranges and iterators
2. Comparison, ordering, hashing and related type classes
3. Basic monad infrastructure
3. Libraries
1. Random numbers
2. Dates and times
4. Operating system abstractions
1. Concurrency and parallelism primitives
2. Asynchronous I/O
3. FFI helpers
4. Environment, file system, processes
5. Locales
The material covered in the first three sections (core types and operations,
language constructs and libraries) will be verified, with the exception of
floating point numbers and the parts of the libraries that interface with the
operating system (e.g., sources of operating system randomness or time zone
database access).
### Call for contributions
Thank you for taking interest in contributing to the Lean standard library\!
There are two main ways for community members to contribute to the Lean
standard library: by contributing experience reports or by contributing code
and lemmas.
**If you are using Lean for software verification or verified software
development:** hearing about your experiences using Lean and its standard
library for software verification is extremely valuable to us. We are committed
to building a standard library suitable for real-world applications and your
input will directly influence the continued evolution of the Lean standard
library. Please reach out to the standard library maintainer team via Zulip
(either in a public thread in the \#lean4 channel or via direct message). Even
just a link to your code helps. Thanks\!
**If you have code that you believe could enhance the Lean 4 standard
library:** we encourage you to initiate a discussion in the \#lean4 channel on
Zulip. This is the most effective way to receive preliminary feedback on your
contribution. The Lean standard library has a very precise scope and it has
very high quality standards, so at the moment we are mostly interested in
contributions that expand upon existing material rather than introducing novel
concepts.
**If you would like to contribute code to the standard library but dont know
what to work on:** we are always excited to meet motivated community members
who would like to contribute, and there is always impactful work that is
suitable for new contributors. Please reach out to Markus Himmel on Zulip to
discuss possible contributions.
As laid out in the [project-wide External Contribution
Guidelines](../../CONTRIBUTING.md),
PRs are much more likely to be merged if they are preceded by an RFC or if you
discussed your planned contribution with a member of the standard library
maintainer team. When in doubt, introducing yourself is always a good idea.
All code in the standard library is expected to strictly adhere to the
[standard library coding conventions](./style.md).

9
script/bench.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
# We benchmark against stage 2 to test new optimizations.
timeout -s KILL 1h time bash -c 'mkdir -p build/release; cd build/release; cmake ../.. && make -j$(nproc) stage2' 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2

View File

@@ -5,8 +5,11 @@ set -euo pipefail
[ $# -eq 1 ] || (echo "usage: $0 <lean4 PR #>"; exit 1)
echo "Warning: the speedcenter is probably not listening on mathlib4-nightly-testing yet."
echo "If you're using this script, please contact @kim-em or @Kha to get this set up, and then remove this notice."
LEAN_PR=$1
PR_RESPONSE=$(gh api repos/leanprover-community/mathlib4/pulls -X POST -f head=lean-pr-testing-$LEAN_PR -f base=nightly-testing -f title="leanprover/lean4#$LEAN_PR benchmarking" -f draft=true -f body="ignore me")
PR_RESPONSE=$(gh api repos/leanprover-community/mathlib4-nightly-testing/pulls -X POST -f head=lean-pr-testing-$LEAN_PR -f base=nightly-testing -f title="leanprover/lean4#$LEAN_PR benchmarking" -f draft=true -f body="ignore me")
PR_NUMBER=$(echo "$PR_RESPONSE" | jq '.number')
echo "opened https://github.com/leanprover-community/mathlib4/pull/$PR_NUMBER"
gh api repos/leanprover-community/mathlib4/issues/$PR_NUMBER/comments -X POST -f body="!bench" > /dev/null
echo "opened https://github.com/leanprover-community/mathlib4-nightly-testing/pull/$PR_NUMBER"
gh api repos/leanprover-community/mathlib4-nightly-testing/issues/$PR_NUMBER/comments -X POST -f body="!bench" > /dev/null

View File

@@ -50,5 +50,4 @@ echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -Wl,-Bstatic
# when not using the above flags, link GMP dynamically/as usual. Always link ICU dynamically.
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp $(pkg-config --libs libuv) -lucrtbase'"
# do not set `LEAN_CC` for tests
echo -n " -DAUTO_THREAD_FINALIZATION=OFF -DSTAGE0_AUTO_THREAD_FINALIZATION=OFF"
echo -n " -DLEAN_TEST_VARS=''"

View File

@@ -53,6 +53,23 @@ def tag_exists(repo_url, tag_name, github_token):
matching_tags = response.json()
return any(tag["ref"] == f"refs/tags/{tag_name}" for tag in matching_tags)
def commit_hash_for_tag(repo_url, tag_name, github_token):
# Use /git/matching-refs/tags/ to get all matching tags
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/git/matching-refs/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
response = requests.get(api_url, headers=headers)
if response.status_code != 200:
return False
# Check if any of the returned refs exactly match our tag
matching_tags = response.json()
matching_commits = [tag["object"]["sha"] for tag in matching_tags if tag["ref"] == f"refs/tags/{tag_name}"]
if len(matching_commits) != 1:
return None
else:
return matching_commits[0]
def release_page_exists(repo_url, tag_name, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
@@ -286,6 +303,14 @@ def main():
lean4_success = False
else:
print(f" ✅ Tag {toolchain} exists")
commit_hash = commit_hash_for_tag(lean_repo_url, toolchain, github_token)
SHORT_HASH_LENGTH = 7 # Lake abbreviates the Lean commit to 7 characters.
if commit_hash is None:
print(f" ❌ Could not resolve tag {toolchain} to a commit.")
lean4_success = False
elif commit_hash[0] == '0' and commit_hash[:SHORT_HASH_LENGTH].isnumeric():
print(f" ❌ Short commit hash {commit_hash[:SHORT_HASH_LENGTH]} is numeric and starts with 0, causing issues for version parsing. Try regenerating the last commit to get a new hash.")
lean4_success = False
if not release_page_exists(lean_repo_url, toolchain, github_token):
print(f" ❌ Release page for {toolchain} does not exist")

View File

@@ -94,6 +94,7 @@ def generate_script(repo, version, config):
"echo 'This repo has nightly-testing infrastructure'",
f"git merge origin/bump/{version.split('-rc')[0]}",
"echo 'Please resolve any conflicts.'",
"grep nightly-testing lakefile.* && echo 'Please ensure the lakefile does not include nightly-testing versions.'",
""
])
if re.search(r'rc\d+$', version) and repo_name in ["verso", "reference-manual"]:

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 21)
set(LEAN_VERSION_MINOR 22)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -58,9 +58,6 @@ option(USE_GITHASH "GIT_HASH" ON)
option(INSTALL_LICENSE "INSTALL_LICENSE" ON)
# When ON we install a copy of cadical
option(INSTALL_CADICAL "Install a copy of cadical" ON)
# When ON thread storage is automatically finalized, it assumes platform support pthreads.
# This option is important when using Lean as library that is invoked from a different programming language (e.g., Haskell).
option(AUTO_THREAD_FINALIZATION "AUTO_THREAD_FINALIZATION" ON)
# FLAGS for disabling optimizations and debugging
option(FREE_VAR_RANGE_OPT "FREE_VAR_RANGE_OPT" ON)
@@ -182,10 +179,6 @@ else()
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_MULTI_THREAD")
endif()
if(AUTO_THREAD_FINALIZATION AND NOT MSVC)
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_AUTO_THREAD_FINALIZATION")
endif()
# Set Module Path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")

View File

@@ -37,6 +37,7 @@ import Init.Ext
import Init.Omega
import Init.MacroTrace
import Init.Grind
import Init.GrindInstances
import Init.While
import Init.Syntax
import Init.Internal

View File

@@ -7,8 +7,10 @@ Authors: Joachim Breitner
module
prelude
import Init.Prelude
import Init.Tactics
public import Init.Prelude
public import Init.Tactics
public section
set_option linter.unusedVariables false in
/--

View File

@@ -6,7 +6,9 @@ Authors: Gabriel Ebner
module
prelude
import Init.NotationExtra
public import Init.NotationExtra
public section
namespace Lean

View File

@@ -6,7 +6,9 @@ Authors: Leonardo de Moura, Mario Carneiro
module
prelude
import Init.Classical
public import Init.Classical
public section
/-! # by_cases tactic and if-then-else support -/

View File

@@ -6,7 +6,9 @@ Authors: Leonardo de Moura, Mario Carneiro
module
prelude
import Init.PropLemmas
public import Init.PropLemmas
public section
universe u v
@@ -45,7 +47,7 @@ theorem em (p : Prop) : p ¬p :=
| Or.inr h, _ => Or.inr h
| _, Or.inr h => Or.inr h
| Or.inl hut, Or.inl hvf =>
have hne : u v := by simp [hvf, hut, true_ne_false]
have hne : u v := by simp [hvf, hut]
Or.inl hne
have p_implies_uv : p u = v :=
fun hp =>
@@ -107,8 +109,8 @@ noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α → Prop) : α
theorem epsilon_spec_aux {α : Sort u} (h : Nonempty α) (p : α Prop) : ( y, p y) p (@epsilon α h p) :=
(strongIndefiniteDescription p h).property
theorem epsilon_spec {α : Sort u} {p : α Prop} (hex : y, p y) : p (@epsilon α (nonempty_of_exists hex) p) :=
epsilon_spec_aux (nonempty_of_exists hex) p hex
theorem epsilon_spec {α : Sort u} {p : α Prop} (hex : y, p y) : p (@epsilon α hex.nonempty p) :=
epsilon_spec_aux hex.nonempty p hex
theorem epsilon_singleton {α : Sort u} (x : α) : @epsilon α x (fun y => y = x) = x :=
@epsilon_spec α (fun y => y = x) x, rfl

View File

@@ -6,7 +6,10 @@ Authors: Leonardo de Moura, Mario Carneiro
module
prelude
import Init.Prelude
public import Init.Prelude
public meta import Init.Prelude
public section
set_option linter.missingDocs true -- keep it documented
/-!

View File

@@ -6,13 +6,15 @@ Authors: Leonardo de Moura
module
prelude
import Init.Control.Basic
import Init.Control.State
import Init.Control.StateRef
import Init.Control.Id
import Init.Control.Except
import Init.Control.Reader
import Init.Control.Option
import Init.Control.Lawful
import Init.Control.StateCps
import Init.Control.ExceptCps
public import Init.Control.Basic
public import Init.Control.State
public import Init.Control.StateRef
public import Init.Control.Id
public import Init.Control.Except
public import Init.Control.Reader
public import Init.Control.Option
public import Init.Control.Lawful
public import Init.Control.StateCps
public import Init.Control.ExceptCps
public section

View File

@@ -6,8 +6,10 @@ Author: Leonardo de Moura, Sebastian Ullrich
module
prelude
import Init.Core
import Init.BinderNameHint
public import Init.Core
public import Init.BinderNameHint
public section
universe u v w
@@ -49,7 +51,7 @@ abbrev forIn_eq_forin' := @forIn_eq_forIn'
/--
Extracts the value from a `ForInStep`, ignoring whether it is `ForInStep.done` or `ForInStep.yield`.
-/
def ForInStep.value (x : ForInStep α) : α :=
@[expose] def ForInStep.value (x : ForInStep α) : α :=
match x with
| ForInStep.done b => b
| ForInStep.yield b => b

View File

@@ -6,9 +6,11 @@ Authors: Leonardo de Moura
module
prelude
import Init.Control.State
import Init.Control.Except
import Init.Data.ToString.Basic
public import Init.Control.State
public import Init.Control.Except
public import Init.Data.ToString.Basic
public section
universe u v
namespace EStateM

View File

@@ -8,9 +8,11 @@ The Except monad transformer.
module
prelude
import Init.Control.Basic
import Init.Control.Id
import Init.Coe
public import Init.Control.Basic
public import Init.Control.Id
public import Init.Coe
public section
namespace Except
variable {ε : Type u}
@@ -136,7 +138,7 @@ may throw the corresponding exception.
This is the inverse of `ExceptT.run`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
/--
@@ -144,7 +146,7 @@ Use a monadic action that may throw an exception as an action that may return an
This is the inverse of `ExceptT.mk`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
namespace ExceptT
@@ -154,14 +156,14 @@ variable {ε : Type u} {m : Type u → Type v} [Monad m]
/--
Returns the value `a` without throwing exceptions or having any other effect.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
ExceptT.mk <| pure (Except.ok a)
/--
Handles exceptions thrown by an action that can have no effects _other_ than throwing exceptions.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
| Except.ok a => f a
| Except.error e => pure (Except.error e)
@@ -170,14 +172,14 @@ protected def bindCont {α β : Type u} (f : α → ExceptT ε m β) : Except ε
Sequences two actions that may throw exceptions. Typically used via `do`-notation or the `>>=`
operator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β :=
ExceptT.mk <| ma >>= ExceptT.bindCont f
/--
Transforms a successful computation's value using `f`. Typically used via the `<$>` operator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β :=
ExceptT.mk <| x >>= fun a => match a with
| (Except.ok a) => pure <| Except.ok (f a)
@@ -186,7 +188,7 @@ protected def map {α β : Type u} (f : α → β) (x : ExceptT ε m α) : Excep
/--
Runs a computation from an underlying monad in the transformed monad with exceptions.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
ExceptT.mk <| Except.ok <$> t
@@ -197,7 +199,7 @@ instance : MonadLift m (ExceptT ε m) := ⟨ExceptT.lift⟩
/--
Handles exceptions produced in the `ExceptT ε` transformer.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
ExceptT.mk <| ma >>= fun res => match res with
| Except.ok a => pure (Except.ok a)

View File

@@ -6,7 +6,9 @@ Authors: Leonardo de Moura
module
prelude
import Init.Control.Lawful.Basic
public import Init.Control.Lawful.Basic
public section
/-!
The Exception monad transformer using CPS style.
@@ -25,7 +27,7 @@ namespace ExceptCpsT
/--
Use a monadic action that may throw an exception as an action that may return an exception's value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
@@ -43,7 +45,7 @@ Returns the value of a computation, forgetting whether it was an exception or a
This corresponds to early return.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
x α pure pure
@@ -63,7 +65,7 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
/--
Run an action from the transformed monad in the exception monad.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
fun _ k _ => x >>= k

View File

@@ -8,7 +8,9 @@ The identity Monad.
module
prelude
import Init.Core
public import Init.Core
public section
universe u
@@ -62,4 +64,7 @@ protected def run (x : Id α) : α := x
instance [OfNat α n] : OfNat (Id α) n :=
inferInstanceAs (OfNat α n)
instance {m : Type u Type v} [Pure m] : MonadLiftT Id m where
monadLift x := pure x.run
end Id

View File

@@ -6,6 +6,9 @@ Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
module
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful.Instances
import Init.Control.Lawful.Lemmas
public import Init.Control.Lawful.Basic
public import Init.Control.Lawful.Instances
public import Init.Control.Lawful.Lemmas
public import Init.Control.Lawful.MonadLift
public section

View File

@@ -6,8 +6,11 @@ Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
module
prelude
import Init.SimpLemmas
import Init.Meta
public import Init.Ext
public import Init.SimpLemmas
public import Init.Meta
public section
open Function
@@ -49,7 +52,7 @@ attribute [simp] id_map
(comp_map _ _ _).symm
theorem Functor.map_unit [Functor f] [LawfulFunctor f] {a : f PUnit} : (fun _ => PUnit.unit) <$> a = a := by
simp [map]
simp
/--
An applicative functor satisfies the laws of an applicative functor.
@@ -147,7 +150,7 @@ attribute [simp] pure_bind bind_assoc bind_pure_comp
attribute [grind] pure_bind
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x
change x >>= (fun a => pure (id a)) = x
rw [bind_pure_comp, id_map]
/--
@@ -241,13 +244,23 @@ theorem LawfulMonad.mk' (m : Type u → Type v) [Monad m]
namespace Id
@[simp] theorem map_eq (x : Id α) (f : α β) : f <$> x = f x := rfl
@[simp] theorem bind_eq (x : Id α) (f : α id β) : x >>= f = f x := rfl
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
@[ext] theorem ext {x y : Id α} (h : x.run = y.run) : x = y := h
instance : LawfulMonad Id := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
@[simp] theorem run_map (x : Id α) (f : α β) : (f <$> x).run = f x.run := rfl
@[simp] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl
@[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl
-- These lemmas are bad as they abuse the defeq of `Id α` and `α`
@[deprecated run_map (since := "2025-03-05")] theorem map_eq (x : Id α) (f : α β) : f <$> x = f x := rfl
@[deprecated run_bind (since := "2025-03-05")] theorem bind_eq (x : Id α) (f : α id β) : x >>= f = f x := rfl
@[deprecated run_pure (since := "2025-03-05")] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
end Id
/-! # Option -/

View File

@@ -6,11 +6,13 @@ Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
module
prelude
import Init.Control.Lawful.Basic
import all Init.Control.Except
import all Init.Control.State
import Init.Control.StateRef
import Init.Ext
public import Init.Control.Lawful.Basic
public import all Init.Control.Except
public import all Init.Control.State
public import Init.Control.StateRef
public import Init.Ext
public section
open Function
@@ -58,7 +60,7 @@ protected theorem bind_pure_comp [Monad m] (f : α → β) (x : ExceptT ε m α)
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
show (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
change (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
@@ -67,10 +69,10 @@ protected theorem seqLeft_eq {α β ε : Type u} {m : Type u → Type v} [Monad
| Except.error _ => simp
| Except.ok _ =>
simp [bind_pure_comp]; apply bind_congr; intro b;
cases b <;> simp [comp, Except.map, const]
cases b <;> simp [Except.map, const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
show (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
change (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
rw [ ExceptT.bind_pure_comp]
apply ext
simp [run_bind]
@@ -206,15 +208,15 @@ theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f :
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _
change (f >>= fun g => g <$> x).run s = _
simp
@[simp] theorem run_seqRight [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
change (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
change (x >>= fun a => y >>= fun _ => pure a).run s = _
simp
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by

View File

@@ -6,9 +6,11 @@ Authors: Kim Morrison
module
prelude
import Init.Control.Lawful.Basic
import Init.RCases
import Init.ByCases
public import Init.Control.Lawful.Basic
public import Init.RCases
public import Init.ByCases
public section
-- Mapping by a function with a left inverse is injective.
theorem map_inj_of_left_inverse [Functor m] [LawfulFunctor m] {f : α β}

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.Control.Lawful.MonadLift.Basic
public import Init.Control.Lawful.MonadLift.Lemmas
public import Init.Control.Lawful.MonadLift.Instances
public section

View File

@@ -0,0 +1,54 @@
/-
Copyright (c) 2025 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao
-/
module
prelude
public import Init.Control.Basic
public section
/-!
# LawfulMonadLift and LawfulMonadLiftT
This module provides classes asserting that `MonadLift` and `MonadLiftT` are lawful, which means
that `monadLift` is compatible with `pure` and `bind`.
-/
section MonadLift
/-- The `MonadLift` typeclass only contains the lifting operation. `LawfulMonadLift` further
asserts that lifting commutes with `pure` and `bind`:
```
monadLift (pure a) = pure a
monadLift (ma >>= f) = monadLift ma >>= monadLift ∘ f
```
-/
class LawfulMonadLift (m : semiOutParam (Type u Type v)) (n : Type u Type w)
[Monad m] [Monad n] [inst : MonadLift m n] : Prop where
/-- Lifting preserves `pure` -/
monadLift_pure {α : Type u} (a : α) : inst.monadLift (pure a) = pure a
/-- Lifting preserves `bind` -/
monadLift_bind {α β : Type u} (ma : m α) (f : α m β) :
inst.monadLift (ma >>= f) = inst.monadLift ma >>= (fun x => inst.monadLift (f x))
/-- The `MonadLiftT` typeclass only contains the transitive lifting operation.
`LawfulMonadLiftT` further asserts that lifting commutes with `pure` and `bind`:
```
monadLift (pure a) = pure a
monadLift (ma >>= f) = monadLift ma >>= monadLift ∘ f
```
-/
class LawfulMonadLiftT (m : Type u Type v) (n : Type u Type w) [Monad m] [Monad n]
[inst : MonadLiftT m n] : Prop where
/-- Lifting preserves `pure` -/
monadLift_pure {α : Type u} (a : α) : inst.monadLift (pure a) = pure a
/-- Lifting preserves `bind` -/
monadLift_bind {α β : Type u} (ma : m α) (f : α m β) :
inst.monadLift (ma >>= f) = monadLift ma >>= (fun x => monadLift (f x))
export LawfulMonadLiftT (monadLift_pure monadLift_bind)
end MonadLift

View File

@@ -0,0 +1,148 @@
/-
Copyright (c) 2025 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao, Paul Reichert
-/
module
prelude
public import all Init.Control.Option
public import all Init.Control.Except
public import all Init.Control.ExceptCps
public import all Init.Control.StateRef
public import all Init.Control.StateCps
public import all Init.Control.Id
public import Init.Control.Lawful.MonadLift.Lemmas
public import Init.Control.Lawful.Instances
public section
universe u v w x
variable {m : Type u Type v} {n : Type u Type w} {o : Type u Type x}
variable (m n o) in
instance [Monad m] [Monad n] [Monad o] [MonadLift n o] [MonadLiftT m n]
[LawfulMonadLift n o] [LawfulMonadLiftT m n] : LawfulMonadLiftT m o where
monadLift_pure := fun a => by
simp only [monadLift, LawfulMonadLift.monadLift_pure, liftM_pure]
monadLift_bind := fun ma f => by
simp only [monadLift, LawfulMonadLift.monadLift_bind, liftM_bind]
variable (m) in
instance [Monad m] : LawfulMonadLiftT m m where
monadLift_pure _ := rfl
monadLift_bind _ _ := rfl
namespace StateT
variable [Monad m] [LawfulMonad m]
instance {σ : Type u} : LawfulMonadLift m (StateT σ m) where
monadLift_pure _ := by ext; simp [MonadLift.monadLift]
monadLift_bind _ _ := by ext; simp [MonadLift.monadLift]
end StateT
namespace ReaderT
variable [Monad m]
instance {ρ : Type u} : LawfulMonadLift m (ReaderT ρ m) where
monadLift_pure _ := rfl
monadLift_bind _ _ := rfl
end ReaderT
namespace OptionT
variable [Monad m] [LawfulMonad m]
@[simp]
theorem lift_pure {α : Type u} (a : α) : OptionT.lift (pure a : m α) = pure a := by
simp only [OptionT.lift, OptionT.mk, bind_pure_comp, map_pure, pure, OptionT.pure]
@[simp]
theorem lift_bind {α β : Type u} (ma : m α) (f : α m β) :
OptionT.lift (ma >>= f) = OptionT.lift ma >>= (fun a => OptionT.lift (f a)) := by
simp only [instMonad, OptionT.bind, OptionT.mk, OptionT.lift, bind_pure_comp, bind_map_left,
map_bind]
instance : LawfulMonadLift m (OptionT m) where
monadLift_pure := lift_pure
monadLift_bind := lift_bind
end OptionT
namespace ExceptT
variable [Monad m] [LawfulMonad m]
@[simp]
theorem lift_bind {α β ε : Type u} (ma : m α) (f : α m β) :
ExceptT.lift (ε := ε) (ma >>= f) = ExceptT.lift ma >>= (fun a => ExceptT.lift (f a)) := by
simp only [instMonad, ExceptT.bind, mk, ExceptT.lift, bind_map_left, ExceptT.bindCont, map_bind]
instance : LawfulMonadLift m (ExceptT ε m) where
monadLift_pure := lift_pure
monadLift_bind := lift_bind
instance : LawfulMonadLift (Except ε) (ExceptT ε m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, mk, pure, Except.pure, ExceptT.pure]
monadLift_bind ma _ := by
simp only [instMonad, ExceptT.bind, mk, MonadLift.monadLift, pure_bind, ExceptT.bindCont,
Except.instMonad, Except.bind]
rcases ma with _ | _ <;> simp
end ExceptT
namespace StateRefT'
instance {ω σ : Type} {m : Type Type} [Monad m] : LawfulMonadLift m (StateRefT' ω σ m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, pure]
unfold StateRefT'.lift ReaderT.pure
simp only
monadLift_bind _ _ := by
simp only [MonadLift.monadLift, bind]
unfold StateRefT'.lift ReaderT.bind
simp only
end StateRefT'
namespace StateCpsT
instance {σ : Type u} [Monad m] [LawfulMonad m] : LawfulMonadLift m (StateCpsT σ m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, pure]
unfold StateCpsT.lift
simp only [pure_bind]
monadLift_bind _ _ := by
simp only [MonadLift.monadLift, bind]
unfold StateCpsT.lift
simp only [bind_assoc]
end StateCpsT
namespace ExceptCpsT
instance {ε : Type u} [Monad m] [LawfulMonad m] : LawfulMonadLift m (ExceptCpsT ε m) where
monadLift_pure _ := by
simp only [MonadLift.monadLift, pure]
unfold ExceptCpsT.lift
simp only [pure_bind]
monadLift_bind _ _ := by
simp only [MonadLift.monadLift, bind]
unfold ExceptCpsT.lift
simp only [bind_assoc]
end ExceptCpsT
namespace Id
instance [Monad m] [LawfulMonad m] : LawfulMonadLiftT Id m where
monadLift_pure a := by simp [monadLift]
monadLift_bind a f := by simp [monadLift]
end Id

View File

@@ -0,0 +1,65 @@
/-
Copyright (c) 2025 Quang Dao. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao
-/
module
prelude
public import Init.Control.Lawful.Basic
public import Init.Control.Lawful.MonadLift.Basic
public section
universe u v w
variable {m : Type u Type v} {n : Type u Type w} [Monad m] [Monad n] [MonadLiftT m n]
[LawfulMonadLiftT m n] {α β : Type u}
theorem monadLift_map [LawfulMonad m] [LawfulMonad n] (f : α β) (ma : m α) :
monadLift (f <$> ma) = f <$> (monadLift ma : n α) := by
rw [ bind_pure_comp, bind_pure_comp, monadLift_bind]
simp only [bind_pure_comp, monadLift_pure]
theorem monadLift_seq [LawfulMonad m] [LawfulMonad n] (mf : m (α β)) (ma : m α) :
monadLift (mf <*> ma) = monadLift mf <*> (monadLift ma : n α) := by
simp only [seq_eq_bind, monadLift_map, monadLift_bind]
theorem monadLift_seqLeft [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
monadLift (x <* y) = (monadLift x : n α) <* (monadLift y : n β) := by
simp only [seqLeft_eq, monadLift_map, monadLift_seq]
theorem monadLift_seqRight [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
monadLift (x *> y) = (monadLift x : n α) *> (monadLift y : n β) := by
simp only [seqRight_eq, monadLift_map, monadLift_seq]
/-! We duplicate the theorems for `monadLift` to `liftM` since `rw` matches on syntax only. -/
@[simp]
theorem liftM_pure (a : α) : liftM (pure a : m α) = pure (f := n) a :=
monadLift_pure _
@[simp]
theorem liftM_bind (ma : m α) (f : α m β) :
liftM (n := n) (ma >>= f) = liftM ma >>= (fun a => liftM (f a)) :=
monadLift_bind _ _
@[simp]
theorem liftM_map [LawfulMonad m] [LawfulMonad n] (f : α β) (ma : m α) :
liftM (f <$> ma) = f <$> (liftM ma : n α) :=
monadLift_map _ _
@[simp]
theorem liftM_seq [LawfulMonad m] [LawfulMonad n] (mf : m (α β)) (ma : m α) :
liftM (mf <*> ma) = liftM mf <*> (liftM ma : n α) :=
monadLift_seq _ _
@[simp]
theorem liftM_seqLeft [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
liftM (x <* y) = (liftM x : n α) <* (liftM y : n β) :=
monadLift_seqLeft _ _
@[simp]
theorem liftM_seqRight [LawfulMonad m] [LawfulMonad n] (x : m α) (y : m β) :
liftM (x *> y) = (liftM x : n α) *> (liftM y : n β) :=
monadLift_seqRight _ _

View File

@@ -6,9 +6,11 @@ Authors: Leonardo de Moura, Sebastian Ullrich
module
prelude
import Init.Data.Option.Basic
import Init.Control.Basic
import Init.Control.Except
public import Init.Data.Option.Basic
public import Init.Control.Basic
public import Init.Control.Except
public section
set_option linter.missingDocs true

View File

@@ -8,9 +8,11 @@ The Reader monad transformer for passing immutable State.
module
prelude
import Init.Control.Basic
import Init.Control.Id
import Init.Control.Except
public import Init.Control.Basic
public import Init.Control.Id
public import Init.Control.Except
public section
set_option linter.missingDocs true

View File

@@ -8,9 +8,11 @@ The State monad transformer.
module
prelude
import Init.Control.Basic
import Init.Control.Id
import Init.Control.Except
public import Init.Control.Basic
public import Init.Control.Id
public import Init.Control.Except
public section
set_option linter.missingDocs true
@@ -29,7 +31,7 @@ of a value and a state.
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value paired with the final state.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
x s
@@ -37,7 +39,7 @@ def StateT.run {σ : Type u} {m : Type u → Type v} {α : Type u} (x : StateT
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value, discarding the final state.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
(·.1) <$> x s
@@ -66,21 +68,21 @@ variable [Monad m] {α β : Type u}
/--
Returns the given value without modifying the state. Typically used via `Pure.pure`.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def pure (a : α) : StateT σ m α :=
fun s => pure (a, s)
/--
Sequences two actions. Typically used via the `>>=` operator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def bind (x : StateT σ m α) (f : α StateT σ m β) : StateT σ m β :=
fun s => do let (a, s) x s; f a s
/--
Modifies the value returned by a computation. Typically used via the `<$>` operator.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def map (f : α β) (x : StateT σ m α) : StateT σ m β :=
fun s => do let (a, s) x s; pure (f a, s)
@@ -114,14 +116,14 @@ Retrieves the current value of the monad's mutable state.
This increments the reference count of the state, which may inhibit in-place updates.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def get : StateT σ m σ :=
fun s => pure (s, s)
/--
Replaces the mutable state with a new value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def set : σ StateT σ m PUnit :=
fun s' _ => pure (, s')
@@ -133,7 +135,7 @@ It is equivalent to `do let (a, s) := f (← StateT.get); StateT.set s; pure a`.
`StateT.modifyGet` may lead to better performance because it doesn't add a new reference to the
state value, and additional references can inhibit in-place updates of data.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def modifyGet (f : σ α × σ) : StateT σ m α :=
fun s => pure (f s)
@@ -143,7 +145,7 @@ Runs an action from the underlying monad in the monad with state. The state is n
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def lift {α : Type u} (t : m α) : StateT σ m α :=
fun s => do let a t; pure (a, s)

View File

@@ -6,7 +6,9 @@ Authors: Leonardo de Moura
module
prelude
import Init.Control.Lawful.Basic
public import Init.Control.Lawful.Basic
public section
set_option linter.missingDocs true
@@ -28,7 +30,7 @@ variable {α σ : Type u} {m : Type u → Type v}
Runs a stateful computation that's represented using continuation passing style by providing it with
an initial state and a continuation.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k
@@ -39,7 +41,7 @@ state, it returns a value paired with the final state.
While the state is internally represented in continuation passing style, the resulting value is the
same as for a non-CPS state monad.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s))
@@ -47,7 +49,7 @@ def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value, discarding the final state.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a)
@@ -72,7 +74,7 @@ Runs an action from the underlying monad in the monad with state. The state is n
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
fun _ s k => x >>= (k . s)

View File

@@ -8,7 +8,9 @@ The State monad transformer using IO references.
module
prelude
import Init.System.ST
public import Init.System.ST
public section
set_option linter.missingDocs true

View File

@@ -8,8 +8,10 @@ Notation for operators defined at Prelude.lean
module
prelude
import Init.Tactics
import Init.Meta
public import Init.Tactics
public meta import Init.Meta
public section
namespace Lean.Parser.Tactic.Conv
@@ -339,6 +341,12 @@ This is the conv mode version of the `lift_lets` tactic.
-/
syntax (name := liftLets) "lift_lets " optConfig : conv
/--
Transforms `let` expressions into `have` expressions within th etarget expression when possible.
This is the conv mode version of the `let_to_have` tactic.
-/
syntax (name := letToHave) "let_to_have" : conv
/--
`conv => ...` allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.

View File

@@ -8,8 +8,10 @@ notation, basic datatypes and type classes
module
prelude
import Init.Prelude
import Init.SizeOf
public meta import Init.Prelude
public import Init.SizeOf
public section
set_option linter.missingDocs true -- keep it documented
@[expose] section
@@ -43,14 +45,14 @@ and `flip (·<·)` is the greater-than relation.
theorem Function.comp_def {α β δ} (f : β δ) (g : α β) : f g = fun x => f (g x) := rfl
@[simp] theorem Function.const_comp {f : α β} {c : γ} :
(Function.const β c f) = Function.const α c := by
(Function.const β c f) = Function.const α c :=
rfl
@[simp] theorem Function.comp_const {f : β γ} {b : β} :
(f Function.const α b) = Function.const α (f b) := by
(f Function.const α b) = Function.const α (f b) :=
rfl
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true := by
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true :=
rfl
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false := by
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false :=
rfl
@[simp] theorem Function.comp_id (f : α β) : f id = f := rfl
@@ -95,7 +97,8 @@ structure Thunk (α : Type u) : Type u where
-/
mk ::
/-- Extract the getter function out of a thunk. Use `Thunk.get` instead. -/
private fn : Unit α
-- The field is public so as to allow computation through it.
fn : Unit α
attribute [extern "lean_mk_thunk"] Thunk.mk
@@ -117,6 +120,10 @@ Computed values are cached, so the value is not recomputed.
@[extern "lean_thunk_get_own"] protected def Thunk.get (x : @& Thunk α) : α :=
x.fn ()
-- Ensure `Thunk.fn` is still computable even if it shouldn't be accessed directly.
@[inline] private def Thunk.fnImpl (x : Thunk α) : Unit α := fun _ => x.get
@[csimp] private theorem Thunk.fn_eq_fnImpl : @Thunk.fn = @Thunk.fnImpl := rfl
/--
Constructs a new thunk that forces `x` and then applies `x` to the result. Upon forcing, the result
of `f` is cached and the reference to the thunk `x` is dropped.
@@ -897,43 +904,43 @@ section
variable {α β φ : Sort u} {a a' : α} {b b' : β} {c : φ}
/-- Non-dependent recursor for `HEq` -/
noncomputable def HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} (m : motive a) {β : Sort u2} {b : β} (h : HEq a b) : motive b :=
noncomputable def HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} (m : motive a) {β : Sort u2} {b : β} (h : a b) : motive b :=
h.rec m
/-- `HEq.ndrec` variant -/
noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : HEq a b) (m : motive a) : motive b :=
noncomputable def HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : a b) (m : motive a) : motive b :=
h.rec m
/-- `HEq.ndrec` variant -/
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : HEq a b) (h₂ : p a) : p b :=
noncomputable def HEq.elim {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : a b) (h₂ : p a) : p b :=
eq_of_heq h₁ h₂
/-- Substitution with heterogeneous equality. -/
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : a b) (h₂ : p α a) : p β b :=
HEq.ndrecOn h₁ h₂
/-- Heterogeneous equality is symmetric. -/
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a :=
@[symm] theorem HEq.symm (h : a b) : b a :=
h.rec (HEq.refl a)
/-- Propositionally equal terms are also heterogeneously equal. -/
theorem heq_of_eq (h : a = a') : HEq a a' :=
theorem heq_of_eq (h : a = a') : a a' :=
Eq.subst h (HEq.refl a)
/-- Heterogeneous equality is transitive. -/
theorem HEq.trans (h₁ : HEq a b) (h₂ : HEq b c) : HEq a c :=
theorem HEq.trans (h₁ : a b) (h₂ : b c) : a c :=
HEq.subst h₂ h₁
/-- Heterogeneous equality precomposes with propositional equality. -/
theorem heq_of_heq_of_eq (h₁ : HEq a b) (h₂ : b = b') : HEq a b' :=
theorem heq_of_heq_of_eq (h₁ : a b) (h₂ : b = b') : a b' :=
HEq.trans h₁ (heq_of_eq h₂)
/-- Heterogeneous equality postcomposes with propositional equality. -/
theorem heq_of_eq_of_heq (h₁ : a = a') (h₂ : HEq a' b) : HEq a b :=
theorem heq_of_eq_of_heq (h₁ : a = a') (h₂ : a' b) : a b :=
HEq.trans (heq_of_eq h₁) h₂
/-- If two terms are heterogeneously equal then their types are propositionally equal. -/
theorem type_eq_of_heq (h : HEq a b) : α = β :=
theorem type_eq_of_heq (h : a b) : α = β :=
h.rec (Eq.refl α)
end
@@ -942,7 +949,7 @@ end
Rewriting inside `φ` using `Eq.recOn` yields a term that's heterogeneously equal to the original
term.
-/
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) HEq (Eq.recOn (motive := fun x _ => φ x) h p) p
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) Eq.recOn (motive := fun x _ => φ x) h p p
| rfl, p => HEq.refl p
/--
@@ -950,8 +957,8 @@ Heterogeneous equality with an `Eq.rec` application on the left is equivalent to
equality on the original term.
-/
theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq (@Eq.rec α a motive refl b h) c HEq refl c :=
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h}
: @Eq.rec α a motive refl b h c refl c :=
h.rec (fun _ => id, id) c
/--
@@ -960,7 +967,7 @@ equality on the original term.
-/
theorem heq_eqRec_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq c (@Eq.rec α a motive refl b h) HEq c refl :=
c @Eq.rec α a motive refl b h c refl :=
h.rec (fun _ => id, id) c
/--
@@ -977,7 +984,7 @@ theorem apply_eqRec {α : Sort u} {a : α} (motive : (b : α) → a = b → Sort
If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two
terms are heterogeneously equal.
-/
theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : Eq.rec (motive := fun α _ => α) a h₁ = b) : HEq a b := by
theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : Eq.rec (motive := fun α _ => α) a h₁ = b) : a b := by
subst h₁
apply heq_of_eq
exact h₂
@@ -985,7 +992,7 @@ theorem heq_of_eqRec_eq {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h
/--
The result of casting a term with `cast` is heterogeneously equal to the original term.
-/
theorem cast_heq {α β : Sort u} : (h : α = β) (a : α) HEq (cast h a) a
theorem cast_heq {α β : Sort u} : (h : α = β) (a : α) cast h a a
| rfl, a => HEq.refl a
variable {a b c d : Prop}
@@ -1014,8 +1021,8 @@ instance : Trans Iff Iff Iff where
theorem Eq.comm {a b : α} : a = b b = a := Iff.intro Eq.symm Eq.symm
theorem eq_comm {a b : α} : a = b b = a := Eq.comm
theorem HEq.comm {a : α} {b : β} : HEq a b HEq b a := Iff.intro HEq.symm HEq.symm
theorem heq_comm {a : α} {b : β} : HEq a b HEq b a := HEq.comm
theorem HEq.comm {a : α} {b : β} : a b b a := Iff.intro HEq.symm HEq.symm
theorem heq_comm {a : α} {b : β} : a b b a := HEq.comm
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem Iff.comm : (a b) (b a) := Iff.intro Iff.symm Iff.symm
@@ -1048,11 +1055,6 @@ theorem Exists.elim {α : Sort u} {p : α → Prop} {b : Prop}
| isFalse _ => rfl
| isTrue h => False.elim h
set_option linter.missingDocs false in
@[deprecated decide_true (since := "2024-11-05")] abbrev decide_true_eq_true := decide_true
set_option linter.missingDocs false in
@[deprecated decide_false (since := "2024-11-05")] abbrev decide_false_eq_false := decide_false
/-- Similar to `decide`, but uses an explicit instance -/
@[inline] def toBoolUsing {p : Prop} (d : Decidable p) : Bool :=
decide (h := d)
@@ -1212,10 +1214,7 @@ abbrev noConfusionEnum {α : Sort u} {β : Sort v} [inst : DecidableEq β] (f :
instance : Inhabited Prop where
default := True
deriving instance Inhabited for NonScalar, PNonScalar, True, ForInStep
theorem nonempty_of_exists {α : Sort u} {p : α Prop} : Exists (fun x => p x) Nonempty α
| w, _ => w
deriving instance Inhabited for NonScalar, PNonScalar, True
/-! # Subsingleton -/
@@ -1242,7 +1241,7 @@ protected theorem Subsingleton.elim {α : Sort u} [h : Subsingleton α] : (a b :
If two types are equal and one of them is a subsingleton, then all of their elements are
[heterogeneously equal](lean-manual://section/HEq).
-/
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : HEq a b := by
protected theorem Subsingleton.helim {α β : Sort u} [h₁ : Subsingleton α] (h₂ : α = β) (a : α) (b : β) : a b := by
subst h₂
apply heq_of_eq
apply Subsingleton.elim
@@ -1389,16 +1388,7 @@ instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
Nonempty.elim h (fun b => Sum.inr b)
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
match a, b with
| Sum.inl a, Sum.inl b =>
if h : a = b then isTrue (h rfl)
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
| Sum.inr a, Sum.inr b =>
if h : a = b then isTrue (h rfl)
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
| Sum.inr _, Sum.inl _ => isFalse fun h => Sum.noConfusion h
| Sum.inl _, Sum.inr _ => isFalse fun h => Sum.noConfusion h
deriving instance DecidableEq for Sum
end
@@ -1702,7 +1692,7 @@ theorem true_iff_false : (True ↔ False) ↔ False := iff_false_intro (·.mp T
theorem false_iff_true : (False True) False := iff_false_intro (·.mpr True.intro)
theorem iff_not_self : ¬(a ¬a) | H => let f h := H.1 h h; f (H.2 f)
theorem heq_self_iff_true (a : α) : HEq a a True := iff_true_intro HEq.rfl
theorem heq_self_iff_true (a : α) : a a True := iff_true_intro HEq.rfl
/-! ## implies -/
@@ -1902,7 +1892,7 @@ a structure.
protected abbrev hrecOn
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
(c : (a b : α) (p : r a b) HEq (f a) (f b))
(c : (a b : α) (p : r a b) f a f b)
: motive q :=
Quot.recOn q f fun a b p => eq_of_heq (eqRec_heq_iff.mpr (c a b p))
@@ -2100,7 +2090,7 @@ a structure.
protected abbrev hrecOn
(q : Quotient s)
(f : (a : α) motive (Quotient.mk s a))
(c : (a b : α) (p : a b) HEq (f a) (f b))
(c : (a b : α) (p : a b) f a f b)
: motive q :=
Quot.hrecOn q f c
end
@@ -2264,7 +2254,7 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
Quot.liftOn f
(fun (f : (x : α), β x) => f x)
(fun _ _ h => h x)
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
change extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
exact congrArg extfunApp (Quot.sound h)
/--

View File

@@ -6,43 +6,48 @@ Authors: Leonardo de Moura
module
prelude
import Init.Data.Basic
import Init.Data.Nat
import Init.Data.Bool
import Init.Data.BitVec
import Init.Data.Cast
import Init.Data.Char
import Init.Data.String
import Init.Data.List
import Init.Data.Int
import Init.Data.Array
import Init.Data.Array.Subarray.Split
import Init.Data.ByteArray
import Init.Data.FloatArray
import Init.Data.Fin
import Init.Data.UInt
import Init.Data.SInt
import Init.Data.Float
import Init.Data.Float32
import Init.Data.Option
import Init.Data.Ord
import Init.Data.Random
import Init.Data.ToString
import Init.Data.Range
import Init.Data.Hashable
import Init.Data.OfScientific
import Init.Data.Format
import Init.Data.Stream
import Init.Data.Prod
import Init.Data.AC
import Init.Data.Queue
import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype
import Init.Data.ULift
import Init.Data.PLift
import Init.Data.Zero
import Init.Data.NeZero
import Init.Data.Function
import Init.Data.RArray
import Init.Data.Vector
public import Init.Data.Basic
public import Init.Data.Nat
public import Init.Data.Bool
public import Init.Data.BitVec
public import Init.Data.Cast
public import Init.Data.Char
public import Init.Data.String
public import Init.Data.List
public import Init.Data.Int
public import Init.Data.Array
public import Init.Data.Array.Subarray.Split
public import Init.Data.ByteArray
public import Init.Data.FloatArray
public import Init.Data.Fin
public import Init.Data.UInt
public import Init.Data.SInt
public import Init.Data.Float
public import Init.Data.Float32
public import Init.Data.Option
public import Init.Data.Ord
public import Init.Data.Random
public import Init.Data.ToString
public import Init.Data.Range
public import Init.Data.Hashable
public import Init.Data.OfScientific
public import Init.Data.Format
public import Init.Data.Stream
public import Init.Data.Prod
public import Init.Data.AC
public import Init.Data.Queue
public import Init.Data.Sum
public import Init.Data.BEq
public import Init.Data.Subtype
public import Init.Data.ULift
public import Init.Data.PLift
public import Init.Data.Zero
public import Init.Data.NeZero
public import Init.Data.Function
public import Init.Data.RArray
public import Init.Data.Vector
public import Init.Data.Iterators
public import Init.Data.Range.Polymorphic
public import Init.Data.Slice
public section

View File

@@ -7,8 +7,10 @@ Authors: Dany Fabian
module
prelude
import Init.Classical
import Init.ByCases
public import Init.Classical
public import Init.ByCases
public section
namespace Lean.Data.AC
inductive Expr
@@ -209,7 +211,7 @@ theorem Context.evalList_sort_congr
induction c generalizing a b with
| nil => simp [sort.loop, h₂]
| cons c _ ih =>
simp [sort.loop]; apply ih; simp [evalList_insert ctx h, evalList]
simp [sort.loop]; apply ih; simp [evalList_insert ctx h]
cases a with
| nil => apply absurd h₃; simp
| cons a as =>
@@ -282,7 +284,7 @@ theorem Context.toList_nonEmpty (e : Expr) : e.toList ≠ [] := by
simp [Expr.toList]
cases h : l.toList with
| nil => contradiction
| cons => simp [List.append]
| cons => simp
theorem Context.unwrap_isNeutral
{ctx : Context α}
@@ -328,13 +330,13 @@ theorem Context.eval_toList (ctx : Context α) (e : Expr) : evalList α ctx e.to
induction e with
| var x => rfl
| op l r ih₁ ih₂ =>
simp [evalList, Expr.toList, eval, ih₁, ih₂]
simp [Expr.toList, eval, ih₁, ih₂]
apply evalList_append <;> apply toList_nonEmpty
theorem Context.eval_norm (ctx : Context α) (e : Expr) : evalList α ctx (norm ctx e) = eval α ctx e := by
simp [norm]
cases h₁ : ContextInformation.isIdem ctx <;> cases h₂ : ContextInformation.isComm ctx <;>
simp_all [evalList_removeNeutrals, eval_toList, toList_nonEmpty, evalList_mergeIdem, evalList_sort]
simp_all [evalList_removeNeutrals, eval_toList, evalList_mergeIdem, evalList_sort]
theorem Context.eq_of_norm (ctx : Context α) (a b : Expr) (h : norm ctx a == norm ctx b) : eval α ctx a = eval α ctx b := by
have h := congrArg (evalList α ctx) (eq_of_beq h)

View File

@@ -6,27 +6,29 @@ Authors: Gabriel Ebner
module
prelude
import Init.Data.Array.Basic
import Init.Data.Array.QSort
import Init.Data.Array.BinSearch
import Init.Data.Array.InsertionSort
import Init.Data.Array.DecidableEq
import Init.Data.Array.Mem
import Init.Data.Array.Attach
import Init.Data.Array.BasicAux
import Init.Data.Array.Lemmas
import Init.Data.Array.TakeDrop
import Init.Data.Array.Bootstrap
import Init.Data.Array.GetLit
import Init.Data.Array.MapIdx
import Init.Data.Array.Set
import Init.Data.Array.Monadic
import Init.Data.Array.FinRange
import Init.Data.Array.Perm
import Init.Data.Array.Find
import Init.Data.Array.Lex
import Init.Data.Array.Range
import Init.Data.Array.Erase
import Init.Data.Array.Zip
import Init.Data.Array.InsertIdx
import Init.Data.Array.Extract
public import Init.Data.Array.Basic
public import Init.Data.Array.QSort
public import Init.Data.Array.BinSearch
public import Init.Data.Array.InsertionSort
public import Init.Data.Array.DecidableEq
public import Init.Data.Array.Mem
public import Init.Data.Array.Attach
public import Init.Data.Array.BasicAux
public import Init.Data.Array.Lemmas
public import Init.Data.Array.TakeDrop
public import Init.Data.Array.Bootstrap
public import Init.Data.Array.GetLit
public import Init.Data.Array.MapIdx
public import Init.Data.Array.Set
public import Init.Data.Array.Monadic
public import Init.Data.Array.FinRange
public import Init.Data.Array.Perm
public import Init.Data.Array.Find
public import Init.Data.Array.Lex
public import Init.Data.Array.Range
public import Init.Data.Array.Erase
public import Init.Data.Array.Zip
public import Init.Data.Array.InsertIdx
public import Init.Data.Array.Extract
public section

View File

@@ -6,10 +6,12 @@ Authors: Joachim Breitner, Mario Carneiro
module
prelude
import Init.Data.Array.Mem
import Init.Data.Array.Lemmas
import Init.Data.Array.Count
import all Init.Data.List.Attach
public import Init.Data.Array.Mem
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Count
public import all Init.Data.List.Attach
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -22,7 +24,7 @@ an array `xs : Array α`, given a proof that every element of `xs` in fact satis
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
-/
@[expose]
def pmap {P : α Prop} (f : a, P a β) (xs : Array α) (H : a xs, P a) : Array β :=
(xs.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
@@ -39,7 +41,7 @@ of elements in the corresponding subtype `{ x // P x }`.
`O(1)`.
-/
@[implemented_by attachWithImpl] def attachWith
@[implemented_by attachWithImpl, expose] def attachWith
(xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} :=
xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)
@@ -54,7 +56,7 @@ recursion](lean-manual://section/well-founded-recursion) that use higher-order f
`Array.map`) to prove that an value taken from a list is smaller than the list. This allows the
well-founded recursion mechanism to prove that the function terminates.
-/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
@[inline, expose] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
@[simp, grind =] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
@@ -68,15 +70,15 @@ well-founded recursion mechanism to prove that the function terminates.
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
simp [pmap]
@[simp] theorem toList_attachWith {xs : Array α} {P : α Prop} {H : x xs, P x} :
(xs.attachWith P H).toList = xs.toList.attachWith P (by simpa [mem_toList] using H) := by
@[simp, grind =] theorem toList_attachWith {xs : Array α} {P : α Prop} {H : x xs, P x} :
(xs.attachWith P H).toList = xs.toList.attachWith P (by simpa [mem_toList_iff] using H) := by
simp [attachWith]
@[simp] theorem toList_attach {xs : Array α} :
xs.attach.toList = xs.toList.attachWith (· xs) (by simp [mem_toList]) := by
@[simp, grind =] theorem toList_attach {xs : Array α} :
xs.attach.toList = xs.toList.attachWith (· xs) (by simp [mem_toList_iff]) := by
simp [attach]
@[simp] theorem toList_pmap {xs : Array α} {P : α Prop} {f : a, P a β} {H : a xs, P a} :
@[simp, grind =] theorem toList_pmap {xs : Array α} {P : α Prop} {f : a, P a β} {H : a xs, P a} :
(xs.pmap f H).toList = xs.toList.pmap f (fun a m => H a (mem_def.mpr m)) := by
simp [pmap]
@@ -92,16 +94,16 @@ well-founded recursion mechanism to prove that the function terminates.
intro a m h₁ h₂
congr
@[simp] theorem pmap_empty {P : α Prop} (f : a, P a β) : pmap f #[] (by simp) = #[] := rfl
@[simp, grind =] theorem pmap_empty {P : α Prop} (f : a, P a β) : pmap f #[] (by simp) = #[] := rfl
@[simp] theorem pmap_push {P : α Prop} (f : a, P a β) (a : α) (xs : Array α) (h : b xs.push a, P b) :
@[simp, grind =] theorem pmap_push {P : α Prop} (f : a, P a β) (a : α) (xs : Array α) (h : b xs.push a, P b) :
pmap f (xs.push a) h =
(pmap f xs (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
simp [pmap]
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@[simp, grind =] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@[simp] theorem attachWith_empty {P : α Prop} (H : x #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
@[simp, grind =] theorem attachWith_empty {P : α Prop} (H : x #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
@[simp] theorem _root_.List.attachWith_mem_toArray {l : List α} :
l.attachWith (fun x => x l.toArray) (fun x h => by simpa using h) =
@@ -122,11 +124,13 @@ theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a,
simp only [List.pmap_toArray, mk.injEq]
rw [List.pmap_congr_left _ h]
@[grind =]
theorem map_pmap {p : α Prop} {g : β γ} {f : a, p a β} {xs : Array α} (H) :
map g (pmap f xs H) = pmap (fun a h => g (f a h)) xs H := by
cases xs
simp [List.map_pmap]
@[grind =]
theorem pmap_map {p : β Prop} {g : b, p b γ} {f : α β} {xs : Array α} (H) :
pmap g (map f xs) H = pmap (fun a h => g (f a) h) xs fun _ h => H _ (mem_map_of_mem h) := by
cases xs
@@ -142,18 +146,18 @@ theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H :
subst w
simp
@[simp] theorem attach_push {a : α} {xs : Array α} :
@[simp, grind =] theorem attach_push {a : α} {xs : Array α} :
(xs.push a).attach =
(xs.attach.map (fun x, h => x, mem_push_of_mem a h)).push a, by simp := by
cases xs
rw [attach_congr (List.push_toArray _ _)]
simp [Function.comp_def]
@[simp] theorem attachWith_push {a : α} {xs : Array α} {P : α Prop} {H : x xs.push a, P x} :
@[simp, grind =] theorem attachWith_push {a : α} {xs : Array α} {P : α Prop} {H : x xs.push a, P x} :
(xs.push a).attachWith P H =
(xs.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push a, H a (by simp) := by
cases xs
simp [attachWith_congr (List.push_toArray _ _)]
simp
theorem pmap_eq_map_attach {p : α Prop} {f : a, p a β} {xs : Array α} (H) :
pmap f xs H = xs.attach.map fun x => f x.1 (H _ x.2) := by
@@ -189,38 +193,39 @@ theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a
(xs.attachWith p H).map Subtype.val = xs := by
cases xs; simp
@[simp]
@[simp, grind]
theorem mem_attach (xs : Array α) : x, x xs.attach
| a, h => by
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
rcases this with _, _, m, rfl
exact m
@[simp]
@[simp, grind]
theorem mem_attachWith {xs : Array α} {q : α Prop} (H) (x : {x // q x}) :
x xs.attachWith q H x.1 xs := by
cases xs
simp
@[simp]
@[simp, grind =]
theorem mem_pmap {p : α Prop} {f : a, p a β} {xs H b} :
b pmap f xs H (a : _) (h : a xs), f a (H a h) = b := by
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
@[grind]
theorem mem_pmap_of_mem {p : α Prop} {f : a, p a β} {xs H} {a} (h : a xs) :
f a (H a h) pmap f xs H := by
rw [mem_pmap]
exact a, h, rfl
@[simp]
@[simp, grind =]
theorem size_pmap {p : α Prop} {f : a, p a β} {xs H} : (pmap f xs H).size = xs.size := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem size_attach {xs : Array α} : xs.attach.size = xs.size := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem size_attachWith {p : α Prop} {xs : Array α} {H} : (xs.attachWith p H).size = xs.size := by
cases xs; simp
@@ -252,13 +257,13 @@ theorem attachWith_ne_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈
xs.attachWith P H #[] xs #[] := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem getElem?_pmap {p : α Prop} {f : a, p a β} {xs : Array α} (h : a xs, p a) (i : Nat) :
(pmap f xs h)[i]? = Option.pmap f xs[i]? fun x H => h x (mem_of_getElem? H) := by
cases xs; simp
-- The argument `f` is explicit to allow rewriting from right to left.
@[simp]
@[simp, grind =]
theorem getElem_pmap {p : α Prop} (f : a, p a β) {xs : Array α} (h : a xs, p a) {i : Nat}
(hi : i < (pmap f xs h).size) :
(pmap f xs h)[i] =
@@ -266,57 +271,59 @@ theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {xs : Array α} (
(h _ (getElem_mem (@size_pmap _ _ p f xs h hi))) := by
cases xs; simp
@[simp]
@[simp, grind =]
theorem getElem?_attachWith {xs : Array α} {i : Nat} {P : α Prop} {H : a xs, P a} :
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (mem_of_getElem? a)) :=
getElem?_pmap ..
@[simp]
@[simp, grind =]
theorem getElem?_attach {xs : Array α} {i : Nat} :
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => mem_of_getElem? a) :=
getElem?_attachWith
@[simp]
@[simp, grind =]
theorem getElem_attachWith {xs : Array α} {P : α Prop} {H : a xs, P a}
{i : Nat} (h : i < (xs.attachWith P H).size) :
(xs.attachWith P H)[i] = xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h)) :=
getElem_pmap _ _ h
@[simp]
@[simp, grind =]
theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
xs.attach[i] = xs[i]'(by simpa using h), getElem_mem (by simpa using h) :=
getElem_attachWith h
@[simp] theorem pmap_attach {xs : Array α} {p : {x // x xs} Prop} {f : a, p a β} (H) :
@[simp, grind =] theorem pmap_attach {xs : Array α} {p : {x // x xs} Prop} {f : a, p a β} (H) :
pmap f xs.attach H =
xs.pmap (P := fun a => h : a xs, p a, h)
(fun a h => f a, h.1 h.2) (fun a h => h, H a, h (by simp)) := by
ext <;> simp
@[simp] theorem pmap_attachWith {xs : Array α} {p : {x // q x} Prop} {f : a, p a β} (H₁ H₂) :
@[simp, grind =] theorem pmap_attachWith {xs : Array α} {p : {x // q x} Prop} {f : a, p a β} (H₁ H₂) :
pmap f (xs.attachWith q H₁) H₂ =
xs.pmap (P := fun a => h : q a, p a, h)
(fun a h => f a, h.1 h.2) (fun a h => H₁ _ h, H₂ a, H₁ _ h (by simpa)) := by
ext <;> simp
@[grind =]
theorem foldl_pmap {xs : Array α} {P : α Prop} {f : (a : α) P a β}
(H : (a : α), a xs P a) (g : γ β γ) (x : γ) :
(xs.pmap f H).foldl g x = xs.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
rw [pmap_eq_map_attach, foldl_map]
@[grind =]
theorem foldr_pmap {xs : Array α} {P : α Prop} {f : (a : α) P a β}
(H : (a : α), a xs P a) (g : β γ γ) (x : γ) :
(xs.pmap f H).foldr g x = xs.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
rw [pmap_eq_map_attach, foldr_map]
@[simp] theorem foldl_attachWith
@[simp, grind =] theorem foldl_attachWith
{xs : Array α} {q : α Prop} (H : a, a xs q a) {f : β { x // q x} β} {b} (w : stop = xs.size) :
(xs.attachWith q H).foldl f b 0 stop = xs.attach.foldl (fun b a, h => f b a, H _ h) b := by
subst w
rcases xs with xs
simp [List.foldl_attachWith, List.foldl_map]
@[simp] theorem foldr_attachWith
@[simp, grind =] theorem foldr_attachWith
{xs : Array α} {q : α Prop} (H : a, a xs q a) {f : { x // q x} β β} {b} (w : start = xs.size) :
(xs.attachWith q H).foldr f b start 0 = xs.attach.foldr (fun a acc => f a.1, H _ a.2 acc) b := by
subst w
@@ -337,7 +344,7 @@ theorem foldl_attach {xs : Array α} {f : β → α → β} {b : β} :
xs.attach.foldl (fun acc t => f acc t.1) b = xs.foldl f b := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
List.length_pmap, List.foldl_toArray', mem_toArray, List.foldl_subtype]
List.foldl_toArray', mem_toArray, List.foldl_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
@@ -356,23 +363,25 @@ theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
xs.attach.foldr (fun t acc => f t.1 acc) b = xs.foldr f b := by
rcases xs with xs
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
List.length_pmap, List.foldr_toArray', mem_toArray, List.foldr_subtype]
List.foldr_toArray', mem_toArray, List.foldr_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
@[grind =]
theorem attach_map {xs : Array α} {f : α β} :
(xs.map f).attach = xs.attach.map (fun x, h => f x, mem_map_of_mem h) := by
cases xs
ext <;> simp
@[grind =]
theorem attachWith_map {xs : Array α} {f : α β} {P : β Prop} (H : (b : β), b xs.map f P b) :
(xs.map f).attachWith P H = (xs.attachWith (P f) (fun _ h => H _ (mem_map_of_mem h))).map
fun x, h => f x, h := by
cases xs
simp [List.attachWith_map]
@[simp] theorem map_attachWith {xs : Array α} {P : α Prop} {H : (a : α), a xs P a}
@[simp, grind =] theorem map_attachWith {xs : Array α} {P : α Prop} {H : (a : α), a xs P a}
{f : { x // P x } β} :
(xs.attachWith P H).map f = xs.attach.map fun x, h => f x, H _ h := by
cases xs <;> simp_all
@@ -393,6 +402,7 @@ theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
@[grind =]
theorem attach_filterMap {xs : Array α} {f : α Option β} :
(xs.filterMap f).attach = xs.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
@@ -400,6 +410,7 @@ theorem attach_filterMap {xs : Array α} {f : α → Option β} :
rw [attach_congr List.filterMap_toArray]
simp [List.attach_filterMap, List.map_filterMap, Function.comp_def]
@[grind =]
theorem attach_filter {xs : Array α} (p : α Bool) :
(xs.filter p).attach = xs.attach.filterMap
fun x => if w : p x.1 then some x.1, mem_filter.mpr x.2, w else none := by
@@ -409,7 +420,7 @@ theorem attach_filter {xs : Array α} (p : α → Bool) :
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
@[simp]
@[simp, grind =]
theorem filterMap_attachWith {q : α Prop} {xs : Array α} {f : {x // q x} Option β} (H)
(w : stop = (xs.attachWith q H).size) :
(xs.attachWith q H).filterMap f 0 stop = xs.attach.filterMap (fun x, h => f x, H _ h) := by
@@ -417,7 +428,7 @@ theorem filterMap_attachWith {q : α → Prop} {xs : Array α} {f : {x // q x}
cases xs
simp [Function.comp_def]
@[simp]
@[simp, grind =]
theorem filter_attachWith {q : α Prop} {xs : Array α} {p : {x // q x} Bool} (H)
(w : stop = (xs.attachWith q H).size) :
(xs.attachWith q H).filter p 0 stop =
@@ -426,6 +437,7 @@ theorem filter_attachWith {q : α → Prop} {xs : Array α} {p : {x // q x} →
cases xs
simp [Function.comp_def, List.filter_map]
@[grind =]
theorem pmap_pmap {p : α Prop} {q : β Prop} {g : a, p a β} {f : b, q b γ} {xs} (H₁ H₂) :
pmap f (pmap g xs H₁) H₂ =
pmap (α := { x // x xs }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) xs.attach
@@ -433,7 +445,7 @@ theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f
cases xs
simp [List.pmap_pmap, List.pmap_map]
@[simp] theorem pmap_append {p : ι Prop} {f : a : ι, p a α} {xs ys : Array ι}
@[simp, grind =] theorem pmap_append {p : ι Prop} {f : a : ι, p a α} {xs ys : Array ι}
(h : a xs ++ ys, p a) :
(xs ++ ys).pmap f h =
(xs.pmap f fun a ha => h a (mem_append_left ys ha)) ++
@@ -448,7 +460,7 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
xs.pmap f h₁ ++ ys.pmap f h₂ :=
pmap_append _
@[simp] theorem attach_append {xs ys : Array α} :
@[simp, grind =] theorem attach_append {xs ys : Array α} :
(xs ++ ys).attach = xs.attach.map (fun x, h => x, mem_append_left ys h) ++
ys.attach.map fun x, h => x, mem_append_right xs h := by
cases xs
@@ -456,59 +468,62 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
rw [attach_congr (List.append_toArray _ _)]
simp [List.attach_append, Function.comp_def]
@[simp] theorem attachWith_append {P : α Prop} {xs ys : Array α}
@[simp, grind =] theorem attachWith_append {P : α Prop} {xs ys : Array α}
{H : (a : α), a xs ++ ys P a} :
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_left ys h)) ++
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
simp [attachWith, attach_append, map_pmap, pmap_append]
simp [attachWith]
@[simp] theorem pmap_reverse {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp, grind =] theorem pmap_reverse {P : α Prop} {f : (a : α) P a β} {xs : Array α}
(H : (a : α), a xs.reverse P a) :
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
induction xs <;> simp_all
@[grind =]
theorem reverse_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
(H : (a : α), a xs P a) :
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
rw [pmap_reverse]
@[simp] theorem attachWith_reverse {P : α Prop} {xs : Array α}
@[simp, grind =] theorem attachWith_reverse {P : α Prop} {xs : Array α}
{H : (a : α), a xs.reverse P a} :
xs.reverse.attachWith P H =
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse := by
cases xs
simp
@[grind =]
theorem reverse_attachWith {P : α Prop} {xs : Array α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) := by
cases xs
simp
@[simp] theorem attach_reverse {xs : Array α} :
@[simp, grind =] theorem attach_reverse {xs : Array α} :
xs.reverse.attach = xs.attach.reverse.map fun x, h => x, by simpa using h := by
cases xs
rw [attach_congr List.reverse_toArray]
simp
@[grind =]
theorem reverse_attach {xs : Array α} :
xs.attach.reverse = xs.reverse.attach.map fun x, h => x, by simpa using h := by
cases xs
simp
@[simp] theorem back?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp, grind =] theorem back?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
(H : (a : α), a xs P a) :
(xs.pmap f H).back? = xs.attach.back?.map fun a, m => f a (H a m) := by
cases xs
simp
@[simp] theorem back?_attachWith {P : α Prop} {xs : Array α}
@[simp, grind =] theorem back?_attachWith {P : α Prop} {xs : Array α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some a, H _ (mem_of_back? h)) := by
cases xs
simp
@[simp]
@[simp, grind =]
theorem back?_attach {xs : Array α} :
xs.attach.back? = xs.back?.pbind fun a h => some a, mem_of_back? h := by
cases xs
@@ -526,7 +541,7 @@ theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Array α} {H
cases xs
simp
@[simp]
@[simp, grind =]
theorem count_attach [BEq α] {xs : Array α} {a : {x // x xs}} :
xs.attach.count a = xs.count a := by
rcases xs with xs
@@ -535,13 +550,13 @@ theorem count_attach [BEq α] {xs : Array α} {a : {x // x ∈ xs}} :
simp only [Subtype.beq_iff]
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
@[simp]
@[simp, grind =]
theorem count_attachWith [BEq α] {p : α Prop} {xs : Array α} (H : a xs, p a) {a : {x // p x}} :
(xs.attachWith p H).count a = xs.count a := by
cases xs
simp
@[simp] theorem countP_pmap {p : α Prop} {g : a, p a β} {f : β Bool} {xs : Array α} (H₁) :
@[simp, grind =] theorem countP_pmap {p : α Prop} {g : a, p a β} {f : β Bool} {xs : Array α} (H₁) :
(xs.pmap g H₁).countP f =
xs.attach.countP (fun a, m => f (g a (H₁ a m))) := by
simp [pmap_eq_map_attach, countP_map, Function.comp_def]
@@ -574,9 +589,12 @@ state, the right approach is usually the tactic `simp [Array.unattach, -Array.ma
-/
def unattach {α : Type _} {p : α Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val)
@[simp] theorem unattach_nil {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := by
@[simp] theorem unattach_empty {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := by
simp [unattach]
@[deprecated unattach_empty (since := "2025-05-26")]
abbrev unattach_nil := @unattach_empty
@[simp] theorem unattach_push {p : α Prop} {a : { x // p x }} {xs : Array { x // p x }} :
(xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Array.map_push]
@@ -687,7 +705,7 @@ and simplifies these to the function directly taking the value.
{f : { x // p x } Array β} {g : α Array β} (hf : x h, f x, h = g x) :
(xs.flatMap f) = xs.unattach.flatMap g := by
cases xs
simp only [List.size_toArray, List.flatMap_toArray, List.unattach_toArray, List.length_unattach,
simp only [List.flatMap_toArray, List.unattach_toArray,
mk.injEq]
rw [List.flatMap_subtype]
simp [hf]

View File

@@ -6,15 +6,17 @@ Authors: Leonardo de Moura
module
prelude
import Init.WFTactics
import Init.Data.Nat.Basic
import Init.Data.Fin.Basic
import Init.Data.UInt.BasicAux
import Init.Data.Repr
import Init.Data.ToString.Basic
import Init.GetElem
import all Init.Data.List.ToArrayImpl
import all Init.Data.Array.Set
public import Init.WFTactics
public import Init.Data.Nat.Basic
public import Init.Data.Fin.Basic
public import Init.Data.UInt.BasicAux
public import Init.Data.Repr
public import Init.Data.ToString.Basic
public import Init.GetElem
public import all Init.Data.List.ToArrayImpl
public import all Init.Data.Array.Set
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -91,7 +93,8 @@ theorem ext' {xs ys : Array α} (h : xs.toList = ys.toList) : xs = ys := by
@[simp, grind =] theorem getElem_toList {xs : Array α} {i : Nat} (h : i < xs.size) : xs.toList[i] = xs[i] := rfl
@[simp, grind =] theorem getElem?_toList {xs : Array α} {i : Nat} : xs.toList[i]? = xs[i]? := by
simp [getElem?_def]
simp only [getElem?_def, getElem_toList]
simp only [Array.size]
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
-- NB: This is defined as a structure rather than a plain def so that a lemma
@@ -112,6 +115,10 @@ theorem mem_def {a : α} {as : Array α} : a ∈ as ↔ a ∈ as.toList :=
rw [Array.mem_def, getElem_toList]
apply List.getElem_mem
@[simp, grind =] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
@[simp] theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
end Array
namespace List
@@ -163,7 +170,7 @@ Low-level indexing operator which is as fast as a C array read.
This avoids overhead due to unboxing a `Nat` used as an index.
-/
@[extern "lean_array_uget", simp]
@[extern "lean_array_uget", simp, expose]
def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
a[i.toNat]
@@ -186,7 +193,7 @@ Examples:
* `#["orange", "yellow"].pop = #["orange"]`
* `(#[] : Array String).pop = #[]`
-/
@[extern "lean_array_pop"]
@[extern "lean_array_pop", expose]
def pop (xs : Array α) : Array α where
toList := xs.toList.dropLast
@@ -205,7 +212,7 @@ Examples:
* `Array.replicate 3 () = #[(), (), ()]`
* `Array.replicate 0 "anything" = #[]`
-/
@[extern "lean_mk_array"]
@[extern "lean_mk_array", expose]
def replicate {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
@@ -233,7 +240,7 @@ Examples:
* `#["red", "green", "blue", "brown"].swap 1 2 = #["red", "blue", "green", "brown"]`
* `#["red", "green", "blue", "brown"].swap 3 0 = #["brown", "green", "blue", "red"]`
-/
@[extern "lean_array_fswap"]
@[extern "lean_array_fswap", expose]
def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic) (hj : j < xs.size := by get_elem_tactic) : Array α :=
let v₁ := xs[i]
let v₂ := xs[j]
@@ -241,7 +248,7 @@ def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic)
xs'.set j v₁ (Nat.lt_of_lt_of_eq hj (size_set _).symm)
@[simp] theorem size_swap {xs : Array α} {i j : Nat} {hi hj} : (xs.swap i j hi hj).size = xs.size := by
show ((xs.set i xs[j]).set j xs[i]
change ((xs.set i xs[j]).set j xs[i]
(Nat.lt_of_lt_of_eq hj (size_set _).symm)).size = xs.size
rw [size_set, size_set]
@@ -263,8 +270,6 @@ def swapIfInBounds (xs : Array α) (i j : @& Nat) : Array α :=
else xs
else xs
@[deprecated swapIfInBounds (since := "2024-11-24")] abbrev swap! := @swapIfInBounds
/-! ### GetElem instance for `USize`, backed by `uget` -/
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
@@ -286,6 +291,7 @@ Examples:
* `#[1, 2].isEmpty = false`
* `#[()].isEmpty = false`
-/
@[expose]
def isEmpty (xs : Array α) : Bool :=
xs.size = 0
@@ -327,12 +333,16 @@ Examples:
* `Array.ofFn (n := 3) toString = #["0", "1", "2"]`
* `Array.ofFn (fun i => #["red", "green", "blue"].get i.val i.isLt) = #["red", "green", "blue"]`
-/
def ofFn {n} (f : Fin n α) : Array α := go 0 (emptyWithCapacity n) where
/-- Auxiliary for `ofFn`. `ofFn.go f i acc = acc ++ #[f i, ..., f(n - 1)]` -/
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
go (i : Nat) (acc : Array α) : Array α :=
if h : i < n then go (i+1) (acc.push (f i, h)) else acc
decreasing_by simp_wf; decreasing_trivial_pre_omega
def ofFn {n} (f : Fin n α) : Array α := go (emptyWithCapacity n) n (Nat.le_refl n) where
/-- Auxiliary for `ofFn`. `ofFn.go f acc i h = acc ++ #[f (n - i), ..., f(n - 1)]` -/
go (acc : Array α) : (i : Nat) i n Array α
| i + 1, h =>
have w : n - i - 1 < n :=
Nat.lt_of_lt_of_le (Nat.sub_one_lt (Nat.sub_ne_zero_iff_lt.mpr h)) (Nat.sub_le n i)
go (acc.push (f n - i - 1, w)) i (Nat.le_of_succ_le h)
| 0, _ => acc
-- See also `Array.ofFnM` defined in `Init.Data.Array.OfFn`.
/--
Constructs an array that contains all the numbers from `0` to `n`, exclusive.
@@ -367,7 +377,7 @@ Examples:
* `Array.singleton 5 = #[5]`
* `Array.singleton "one" = #["one"]`
-/
@[inline] protected def singleton (v : α) : Array α := #[v]
@[inline, expose] protected def singleton (v : α) : Array α := #[v]
/--
Returns the last element of an array, or panics if the array is empty.
@@ -396,7 +406,7 @@ that requires a proof the array is non-empty.
def back? (xs : Array α) : Option α :=
xs[xs.size - 1]?
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12")]
@[deprecated "Use `a[i]?` instead." (since := "2025-02-12"), expose]
def get? (xs : Array α) (i : Nat) : Option α :=
if h : i < xs.size then some xs[i] else none
@@ -410,7 +420,7 @@ Examples:
* `#["spinach", "broccoli", "carrot"].swapAt 1 "pepper" = ("broccoli", #["spinach", "pepper", "carrot"])`
* `#["spinach", "broccoli", "carrot"].swapAt 2 "pepper" = ("carrot", #["spinach", "broccoli", "pepper"])`
-/
@[inline] def swapAt (xs : Array α) (i : Nat) (v : α) (hi : i < xs.size := by get_elem_tactic) : α × Array α :=
@[inline, expose] def swapAt (xs : Array α) (i : Nat) (v : α) (hi : i < xs.size := by get_elem_tactic) : α × Array α :=
let e := xs[i]
let xs' := xs.set i v
(e, xs')
@@ -425,7 +435,7 @@ Examples:
* `#["spinach", "broccoli", "carrot"].swapAt! 1 "pepper" = (#["spinach", "pepper", "carrot"], "broccoli")`
* `#["spinach", "broccoli", "carrot"].swapAt! 2 "pepper" = (#["spinach", "broccoli", "pepper"], "carrot")`
-/
@[inline]
@[inline, expose]
def swapAt! (xs : Array α) (i : Nat) (v : α) : α × Array α :=
if h : i < xs.size then
swapAt xs i v
@@ -538,7 +548,7 @@ Examples:
-/
@[inline]
def modify (xs : Array α) (i : Nat) (f : α α) : Array α :=
Id.run <| modifyM xs i f
Id.run <| modifyM xs i (pure <| f ·)
set_option linter.indexVariables false in -- Changing `idx` causes bootstrapping issues, haven't investigated.
/--
@@ -571,7 +581,7 @@ def modifyOp (xs : Array α) (idx : Nat) (f : αα) : Array α :=
loop 0 b
/-- Reference implementation for `forIn'` -/
@[implemented_by Array.forIn'Unsafe]
@[implemented_by Array.forIn'Unsafe, expose]
protected def forIn' {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (b : β) (f : (a : α) a as β m (ForInStep β)) : m β :=
let rec loop (i : Nat) (h : i as.size) (b : β) : m β := do
match i, h with
@@ -638,7 +648,7 @@ example [Monad m] (f : α → β → m α) :
```
-/
-- Reference implementation for `foldlM`
@[implemented_by foldlMUnsafe]
@[implemented_by foldlMUnsafe, expose]
def foldlM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : β α m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β :=
let fold (stop : Nat) (h : stop as.size) :=
let rec loop (i : Nat) (j : Nat) (b : β) : m β := do
@@ -703,7 +713,7 @@ example [Monad m] (f : α → β → m β) :
```
-/
-- Reference implementation for `foldrM`
@[implemented_by foldrMUnsafe]
@[implemented_by foldrMUnsafe, expose]
def foldrM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α β m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m β :=
let rec fold (i : Nat) (h : i as.size) (b : β) : m β := do
if i == stop then
@@ -758,13 +768,11 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
decreasing_by simp_wf; decreasing_trivial_pre_omega
map 0 (emptyWithCapacity as.size)
@[deprecated mapM (since := "2024-11-11")] abbrev sequenceMap := @mapM
/--
Applies the monadic action `f` to every element in the array, along with the element's index and a
proof that the index is in bounds, from left to right. Returns the array of results.
-/
@[inline]
@[inline, expose]
def mapFinIdxM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m]
(as : Array α) (f : (i : Nat) α (h : i < as.size) m β) : m (Array β) :=
let rec @[specialize] map (i : Nat) (j : Nat) (inv : i + j = as.size) (bs : Array β) : m (Array β) := do
@@ -782,7 +790,7 @@ def mapFinIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
Applies the monadic action `f` to every element in the array, along with the element's index, from
left to right. Returns the array of results.
-/
@[inline]
@[inline, expose]
def mapIdxM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : Nat α m β) (as : Array α) : m (Array β) :=
as.mapFinIdxM fun i a _ => f i a
@@ -828,7 +836,7 @@ Almost! 5
some 10
```
-/
@[inline]
@[inline, expose]
def findSomeM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m (Option β)) (as : Array α) : m (Option β) := do
for a in as do
match ( f a) with
@@ -909,7 +917,7 @@ The optional parameters `start` and `stop` control the region of the array to be
elements with indices from `start` (inclusive) to `stop` (exclusive) are checked. By default, the
entire array is checked.
-/
@[implemented_by anyMUnsafe]
@[implemented_by anyMUnsafe, expose]
def anyM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
let any (stop : Nat) (h : stop as.size) :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
@@ -1051,9 +1059,9 @@ Examples:
* `#[1, 2, 3].foldl (· ++ toString ·) "" = "123"`
* `#[1, 2, 3].foldl (s!"({·} {·})") "" = "((( 1) 2) 3)"`
-/
@[inline]
@[inline, expose]
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Array α) (start := 0) (stop := as.size) : β :=
Id.run <| as.foldlM f init start stop
Id.run <| as.foldlM (pure <| f · ·) init start stop
/--
Folds a function over an array from the right, accumulating a value starting with `init`. The
@@ -1068,9 +1076,9 @@ Examples:
* `#[1, 2, 3].foldr (toString · ++ ·) "" = "123"`
* `#[1, 2, 3].foldr (s!"({·} {·})") "!" = "(1 (2 (3 !)))"`
-/
@[inline]
@[inline, expose]
def foldr {α : Type u} {β : Type v} (f : α β β) (init : β) (as : Array α) (start := as.size) (stop := 0) : β :=
Id.run <| as.foldrM f init start stop
Id.run <| as.foldrM (pure <| f · ·) init start stop
/--
Computes the sum of the elements of an array.
@@ -1079,7 +1087,7 @@ Examples:
* `#[a, b, c].sum = a + (b + (c + 0))`
* `#[1, 2, 5].sum = 8`
-/
@[inline]
@[inline, expose]
def sum {α} [Add α] [Zero α] : Array α α :=
foldr (· + ·) 0
@@ -1091,7 +1099,7 @@ Examples:
* `#[1, 2, 3, 4, 5].countP (· < 5) = 4`
* `#[1, 2, 3, 4, 5].countP (· > 5) = 0`
-/
@[inline]
@[inline, expose]
def countP {α : Type u} (p : α Bool) (as : Array α) : Nat :=
as.foldr (init := 0) fun a acc => bif p a then acc + 1 else acc
@@ -1103,7 +1111,7 @@ Examples:
* `#[1, 1, 2, 3, 5].count 5 = 1`
* `#[1, 1, 2, 3, 5].count 4 = 0`
-/
@[inline]
@[inline, expose]
def count {α : Type u} [BEq α] (a : α) (as : Array α) : Nat :=
countP (· == a) as
@@ -1116,9 +1124,9 @@ Examples:
* `#["one", "two", "three"].map (·.length) = #[3, 3, 5]`
* `#["one", "two", "three"].map (·.reverse) = #["eno", "owt", "eerht"]`
-/
@[inline]
@[inline, expose]
def map {α : Type u} {β : Type v} (f : α β) (as : Array α) : Array β :=
Id.run <| as.mapM f
Id.run <| as.mapM (pure <| f ·)
instance : Functor Array where
map := map
@@ -1131,9 +1139,9 @@ that the index is valid.
`Array.mapIdx` is a variant that does not provide the function with evidence that the index is
valid.
-/
@[inline]
@[inline, expose]
def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : (i : Nat) α (h : i < as.size) β) : Array β :=
Id.run <| as.mapFinIdxM f
Id.run <| as.mapFinIdxM (pure <| f · · ·)
/--
Applies a function to each element of the array along with the index at which that element is found,
@@ -1142,9 +1150,9 @@ returning the array of results.
`Array.mapFinIdx` is a variant that additionally provides the function with a proof that the index
is valid.
-/
@[inline]
@[inline, expose]
def mapIdx {α : Type u} {β : Type v} (f : Nat α β) (as : Array α) : Array β :=
Id.run <| as.mapIdxM f
Id.run <| as.mapIdxM (pure <| f · ·)
/--
Pairs each element of an array with its index, optionally starting from an index other than `0`.
@@ -1153,6 +1161,7 @@ Examples:
* `#[a, b, c].zipIdx = #[(a, 0), (b, 1), (c, 2)]`
* `#[a, b, c].zipIdx 5 = #[(a, 5), (b, 6), (c, 7)]`
-/
@[expose]
def zipIdx (xs : Array α) (start := 0) : Array (α × Nat) :=
xs.mapIdx fun i a => (a, start + i)
@@ -1166,7 +1175,7 @@ Examples:
* `#[7, 6, 5, 8, 1, 2, 6].find? (· < 5) = some 1`
* `#[7, 6, 5, 8, 1, 2, 6].find? (· < 1) = none`
-/
@[inline]
@[inline, expose]
def find? {α : Type u} (p : α Bool) (as : Array α) : Option α :=
Id.run do
for a in as do
@@ -1190,9 +1199,9 @@ Example:
some 10
```
-/
@[inline]
@[inline, expose]
def findSome? {α : Type u} {β : Type v} (f : α Option β) (as : Array α) : Option β :=
Id.run <| as.findSomeM? f
Id.run <| as.findSomeM? (pure <| f ·)
/--
Returns the first non-`none` result of applying the function `f` to each element of the
@@ -1226,7 +1235,7 @@ Examples:
-/
@[inline]
def findSomeRev? {α : Type u} {β : Type v} (f : α Option β) (as : Array α) : Option β :=
Id.run <| as.findSomeRevM? f
Id.run <| as.findSomeRevM? (pure <| f ·)
/--
Returns the last element of the array for which the predicate `p` returns `true`, or `none` if no
@@ -1238,7 +1247,7 @@ Examples:
-/
@[inline]
def findRev? {α : Type} (p : α Bool) (as : Array α) : Option α :=
Id.run <| as.findRevM? p
Id.run <| as.findRevM? (pure <| p ·)
/--
Returns the index of the first element for which `p` returns `true`, or `none` if there is no such
@@ -1248,7 +1257,7 @@ Examples:
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = some 4`
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = none`
-/
@[inline]
@[inline, expose]
def findIdx? {α : Type u} (p : α Bool) (as : Array α) : Option Nat :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
loop (j : Nat) :=
@@ -1302,7 +1311,7 @@ Examples:
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = 4`
* `#[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = 7`
-/
@[inline]
@[inline, expose]
def findIdx (p : α Bool) (as : Array α) : Nat := (as.findIdx? p).getD as.size
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
@@ -1356,10 +1365,6 @@ Examples:
def idxOf? [BEq α] (xs : Array α) (v : α) : Option Nat :=
(xs.finIdxOf? v).map (·.val)
@[deprecated idxOf? (since := "2024-11-20")]
def getIdx? [BEq α] (xs : Array α) (v : α) : Option Nat :=
xs.findIdx? fun a => a == v
/--
Returns `true` if `p` returns `true` for any element of `as`.
@@ -1375,9 +1380,9 @@ Examples:
* `#[2, 4, 5, 6].any (· % 2 = 0) = true`
* `#[2, 4, 5, 6].any (· % 2 = 1) = true`
-/
@[inline]
@[inline, expose]
def any (as : Array α) (p : α Bool) (start := 0) (stop := as.size) : Bool :=
Id.run <| as.anyM p start stop
Id.run <| as.anyM (pure <| p ·) start stop
/--
Returns `true` if `p` returns `true` for every element of `as`.
@@ -1395,7 +1400,7 @@ Examples:
-/
@[inline]
def all (as : Array α) (p : α Bool) (start := 0) (stop := as.size) : Bool :=
Id.run <| as.allM p start stop
Id.run <| as.allM (pure <| p ·) start stop
/--
Checks whether `a` is an element of `as`, using `==` to compare elements.
@@ -1406,6 +1411,7 @@ Examples:
* `#[1, 4, 2, 3, 3, 7].contains 3 = true`
* `Array.contains #[1, 4, 2, 3, 3, 7] 5 = false`
-/
@[expose]
def contains [BEq α] (as : Array α) (a : α) : Bool :=
as.any (a == ·)
@@ -1454,6 +1460,7 @@ Examples:
* `#[] ++ #[4, 5] = #[4, 5]`.
* `#[1, 2, 3] ++ #[] = #[1, 2, 3]`.
-/
@[expose]
protected def append (as : Array α) (bs : Array α) : Array α :=
bs.foldl (init := as) fun xs v => xs.push v
@@ -1491,7 +1498,7 @@ Examples:
* `#[2, 3, 2].flatMap Array.range = #[0, 1, 0, 1, 2, 0, 1]`
* `#[['a', 'b'], ['c', 'd', 'e']].flatMap List.toArray = #['a', 'b', 'c', 'd', 'e']`
-/
@[inline]
@[inline, expose]
def flatMap (f : α Array β) (as : Array α) : Array β :=
as.foldl (init := empty) fun bs a => bs ++ f a
@@ -1504,7 +1511,7 @@ Examples:
* `#[#[0, 1], #[], #[2], #[1, 0, 1]].flatten = #[0, 1, 2, 1, 0, 1]`
* `(#[] : Array Nat).flatten = #[]`
-/
@[inline] def flatten (xss : Array (Array α)) : Array α :=
@[inline, expose] def flatten (xss : Array (Array α)) : Array α :=
xss.foldl (init := empty) fun acc xs => acc ++ xs
/--
@@ -1517,6 +1524,7 @@ Examples:
* `#[0, 1].reverse = #[1, 0]`
* `#[0, 1, 2].reverse = #[2, 1, 0]`
-/
@[expose]
def reverse (as : Array α) : Array α :=
if h : as.size 1 then
as
@@ -1549,7 +1557,7 @@ Examples:
* `#[1, 2, 5, 2, 7, 7].filter (fun _ => true) (start := 3) = #[2, 7, 7]`
* `#[1, 2, 5, 2, 7, 7].filter (fun _ => true) (stop := 3) = #[1, 2, 5]`
-/
@[inline]
@[inline, expose]
def filter (p : α Bool) (as : Array α) (start := 0) (stop := as.size) : Array α :=
as.foldl (init := #[]) (start := start) (stop := stop) fun acc a =>
if p a then acc.push a else acc
@@ -1642,7 +1650,7 @@ Examining 7
#[10, 14, 14]
```
-/
@[specialize]
@[specialize, expose]
def filterMapM [Monad m] (f : α m (Option β)) (as : Array α) (start := 0) (stop := as.size) : m (Array β) :=
as.foldlM (init := #[]) (start := start) (stop := stop) fun bs a => do
match ( f a) with
@@ -1662,9 +1670,9 @@ Example:
#[10, 14, 14]
```
-/
@[inline]
@[inline, expose]
def filterMap (f : α Option β) (as : Array α) (start := 0) (stop := as.size) : Array β :=
Id.run <| as.filterMapM f (start := start) (stop := stop)
Id.run <| as.filterMapM (pure <| f ·) (start := start) (stop := stop)
/--
Returns the largest element of the array, as determined by the comparison `lt`, or `none` if
@@ -1782,7 +1790,7 @@ decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ h
induction xs, i, h using Array.eraseIdx.induct with
| @case1 xs i h h' xs' ih =>
unfold eraseIdx
simp +zetaDelta [h', xs', ih]
simp +zetaDelta [h', ih]
| case2 xs i h h' =>
unfold eraseIdx
simp [h']
@@ -1875,8 +1883,6 @@ Examples:
let as := as.push a
loop as j, size_push .. j.lt_succ_self
@[deprecated insertIdx (since := "2024-11-20")] abbrev insertAt := @insertIdx
/--
Inserts an element into an array at the specified index. Panics if the index is greater than the
size of the array.
@@ -1897,8 +1903,6 @@ def insertIdx! (as : Array α) (i : Nat) (a : α) : Array α :=
insertIdx as i a
else panic! "invalid index"
@[deprecated insertIdx! (since := "2024-11-20")] abbrev insertAt! := @insertIdx!
/--
Inserts an element into an array at the specified index. The array is returned unmodified if the
index is greater than the size of the array.
@@ -2021,11 +2025,6 @@ Examples:
def unzip (as : Array (α × β)) : Array α × Array β :=
as.foldl (init := (#[], #[])) fun (as, bs) (a, b) => (as.push a, bs.push b)
@[deprecated partition (since := "2024-11-06")]
def split (as : Array α) (p : α Bool) : Array α × Array α :=
as.foldl (init := (#[], #[])) fun (as, bs) a =>
if p a then (as.push a, bs) else (as, bs.push a)
/--
Replaces the first occurrence of `a` with `b` in an array. The modification is performed in-place
when the reference to the array is unique. Returns the array unmodified when `a` is not present.

View File

@@ -6,9 +6,11 @@ Authors: Leonardo de Moura
module
prelude
import all Init.Data.Array.Basic
import Init.Data.Nat.Linear
import Init.NotationExtra
public import all Init.Data.Array.Basic
public import Init.Data.Nat.Linear
public import Init.NotationExtra
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -88,4 +90,4 @@ pointer equality, and does not allocate a new array if the result of each functi
pointer-equal to its argument.
-/
@[inline] def Array.mapMono (as : Array α) (f : α α) : Array α :=
Id.run <| as.mapMonoM f
Id.run <| as.mapMonoM (pure <| f ·)

View File

@@ -6,9 +6,11 @@ Authors: Leonardo de Moura
module
prelude
import Init.Data.Array.Basic
import Init.Data.Int.DivMod.Lemmas
import Init.Omega
public import Init.Data.Array.Basic
public import Init.Data.Int.DivMod.Lemmas
public import Init.Omega
public section
universe u v
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
@@ -129,6 +131,6 @@ Examples:
* `#[].binInsert (· < ·) 1 = #[1]`
-/
@[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α :=
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k
Id.run <| binInsertM lt (fun _ => pure k) (fun _ => pure k) as k
end Array

View File

@@ -7,8 +7,10 @@ Authors: Mario Carneiro
module
prelude
import Init.Data.List.TakeDrop
import all Init.Data.Array.Basic
public import Init.Data.List.TakeDrop
public import all Init.Data.Array.Basic
public section
/-!
## Bootstrapping theorems about arrays
@@ -40,7 +42,7 @@ Use the indexing notation `a[i]!` instead.
Access an element from an array, or panic if the index is out of bounds.
-/
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17")]
@[deprecated "Use indexing notation `as[i]!` instead" (since := "2025-02-17"), expose]
def get! {α : Type u} [Inhabited α] (a : @& Array α) (i : @& Nat) : α :=
Array.getD a i default
@@ -78,7 +80,8 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
have : xs = #[] 0 < xs.size :=
match xs with | [] => .inl rfl | a::l => .inr (Nat.zero_lt_succ _)
match xs, this with | _, .inl rfl => simp [foldrM] | xs, .inr h => ?_
simp [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
simp only [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux]
simp [Array.size]
@[simp, grind =] theorem foldrM_toList [Monad m]
{f : α β m β} {init : β} {xs : Array α} :
@@ -89,9 +92,13 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
xs.toList.foldr f init = xs.foldr f init :=
List.foldr_eq_foldrM .. foldrM_toList ..
@[simp, grind =] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
@[simp, grind =] theorem toList_push {xs : Array α} {x : α} : (xs.push x).toList = xs.toList ++ [x] := by
rcases xs with xs
simp [push, List.concat_eq_append]
@[deprecated toList_push (since := "2025-05-26")]
abbrev push_toList := @toList_push
@[simp, grind =] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
simp [toListAppend, foldr_toList]
@@ -114,13 +121,13 @@ abbrev pop_toList := @Array.toList_pop
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
@[simp, grind =] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
apply ext'; simp only [toList_append, List.append_nil]
@[deprecated append_empty (since := "2025-01-13")]
abbrev append_nil := @append_empty
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
apply ext'; simp only [toList_append, List.nil_append]
@[deprecated empty_append (since := "2025-01-13")]
abbrev nil_append := @empty_append
@@ -138,26 +145,4 @@ abbrev nil_append := @empty_append
@[deprecated toList_appendList (since := "2024-12-11")]
abbrev appendList_toList := @toList_appendList
@[deprecated "Use the reverse direction of `foldrM_toList`." (since := "2024-11-13")]
theorem foldrM_eq_foldrM_toList [Monad m]
{f : α β m β} {init : β} {xs : Array α} :
xs.foldrM f init = xs.toList.foldrM f init := by
simp
@[deprecated "Use the reverse direction of `foldlM_toList`." (since := "2024-11-13")]
theorem foldlM_eq_foldlM_toList [Monad m]
{f : β α m β} {init : β} {xs : Array α} :
xs.foldlM f init = xs.toList.foldlM f init:= by
simp
@[deprecated "Use the reverse direction of `foldr_toList`." (since := "2024-11-13")]
theorem foldr_eq_foldr_toList {f : α β β} {init : β} {xs : Array α} :
xs.foldr f init = xs.toList.foldr f init := by
simp
@[deprecated "Use the reverse direction of `foldl_toList`." (since := "2024-11-13")]
theorem foldl_eq_foldl_toList {f : β α β} {init : β} {xs : Array α} :
xs.foldl f init = xs.toList.foldl f init:= by
simp
end Array

View File

@@ -6,9 +6,11 @@ Authors: Kim Morrison
module
prelude
import all Init.Data.Array.Basic
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.Count
public import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Count
public section
/-!
# Lemmas about `Array.countP` and `Array.count`.
@@ -52,17 +54,20 @@ theorem countP_push {a : α} {xs : Array α} : countP p (xs.push a) = countP p x
rcases xs with xs
simp_all
@[simp] theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
simp [countP_push]
@[grind =]
theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
simp
theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + countP (fun a => ¬p a) xs := by
rcases xs with xs
simp [List.length_eq_countP_add_countP (p := p)]
@[grind _=_]
theorem countP_eq_size_filter {xs : Array α} : countP p xs = (filter p xs).size := by
rcases xs with xs
simp [List.countP_eq_length_filter]
@[grind =]
theorem countP_eq_size_filter' : countP p = size filter p := by
funext xs
apply countP_eq_size_filter
@@ -71,7 +76,7 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
simp only [countP_eq_size_filter]
apply size_filter_le
@[simp] theorem countP_append {xs ys : Array α} : countP p (xs ++ ys) = countP p xs + countP p ys := by
@[simp, grind =] theorem countP_append {xs ys : Array α} : countP p (xs ++ ys) = countP p xs + countP p ys := by
rcases xs with xs
rcases ys with ys
simp
@@ -102,9 +107,11 @@ theorem boole_getElem_le_countP {xs : Array α} {i : Nat} (h : i < xs.size) :
rcases xs with xs
simp [List.boole_getElem_le_countP]
@[grind =]
theorem countP_set {xs : Array α} {i : Nat} {a : α} (h : i < xs.size) :
(xs.set i a).countP p = xs.countP p - (if p xs[i] then 1 else 0) + (if p a then 1 else 0) := by
rcases xs with xs
simp at h
simp [List.countP_set, h]
theorem countP_filter {xs : Array α} :
@@ -145,7 +152,7 @@ theorem countP_flatMap {p : β → Bool} {xs : Array α} {f : α → Array β} :
rcases xs with xs
simp [List.countP_flatMap, Function.comp_def]
@[simp] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
@[simp, grind =] theorem countP_reverse {xs : Array α} : countP p xs.reverse = countP p xs := by
rcases xs with xs
simp [List.countP_reverse]
@@ -172,7 +179,7 @@ variable [BEq α]
cases xs
simp
@[simp] theorem count_empty {a : α} : count a #[] = 0 := rfl
@[simp, grind =] theorem count_empty {a : α} : count a #[] = 0 := rfl
theorem count_push {a b : α} {xs : Array α} :
count a (xs.push b) = count a xs + if b == a then 1 else 0 := by
@@ -185,21 +192,28 @@ theorem count_eq_countP' {a : α} : count a = countP (· == a) := by
theorem count_le_size {a : α} {xs : Array α} : count a xs xs.size := countP_le_size
grind_pattern count_le_size => count a xs
@[grind =]
theorem count_eq_size_filter {a : α} {xs : Array α} : count a xs = (filter (· == a) xs).size := by
simp [count, countP_eq_size_filter]
theorem count_le_count_push {a b : α} {xs : Array α} : count a xs count a (xs.push b) := by
simp [count_push]
@[grind =]
theorem count_singleton {a b : α} : count a #[b] = if b == a then 1 else 0 := by
simp [count_eq_countP]
@[simp] theorem count_append {a : α} {xs ys : Array α} : count a (xs ++ ys) = count a xs + count a ys :=
@[simp, grind =] theorem count_append {a : α} {xs ys : Array α} : count a (xs ++ ys) = count a xs + count a ys :=
countP_append
@[simp] theorem count_flatten {a : α} {xss : Array (Array α)} :
@[simp, grind =] theorem count_flatten {a : α} {xss : Array (Array α)} :
count a xss.flatten = (xss.map (count a)).sum := by
cases xss using array₂_induction
simp [List.count_flatten, Function.comp_def]
@[simp] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
@[simp, grind =] theorem count_reverse {a : α} {xs : Array α} : count a xs.reverse = count a xs := by
rcases xs with xs
simp
@@ -208,9 +222,10 @@ theorem boole_getElem_le_count {xs : Array α} {i : Nat} {a : α} (h : i < xs.si
rw [count_eq_countP]
apply boole_getElem_le_countP (p := (· == a))
@[grind =]
theorem count_set {xs : Array α} {i : Nat} {a b : α} (h : i < xs.size) :
(xs.set i a).count b = xs.count b - (if xs[i] == b then 1 else 0) + (if a == b then 1 else 0) := by
simp [count_eq_countP, countP_set, h]
simp [count_eq_countP, countP_set]
variable [LawfulBEq α]
@@ -218,7 +233,7 @@ variable [LawfulBEq α]
simp [count_push]
@[simp] theorem count_push_of_ne {xs : Array α} (h : b a) : count a (xs.push b) = count a xs := by
simp_all [count_push, h]
simp_all [count_push]
theorem count_singleton_self {a : α} : count a #[a] = 1 := by simp
@@ -279,17 +294,17 @@ abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
theorem count_le_count_map [BEq β] [LawfulBEq β] {xs : Array α} {f : α β} {x : α} :
count x xs count (f x) (map f xs) := by
rcases xs with xs
simp [List.count_le_count_map, countP_map]
simp [List.count_le_count_map]
theorem count_filterMap {α} [BEq β] {b : β} {f : α Option β} {xs : Array α} :
count b (filterMap f xs) = countP (fun a => f a == some b) xs := by
rcases xs with xs
simp [List.count_filterMap, countP_filterMap]
simp [List.count_filterMap]
theorem count_flatMap {α} [BEq β] {xs : Array α} {f : α Array β} {x : β} :
count x (xs.flatMap f) = sum (map (count x f) xs) := by
rcases xs with xs
simp [List.count_flatMap, countP_flatMap, Function.comp_def]
simp [List.count_flatMap, Function.comp_def]
theorem countP_replace {a b : α} {xs : Array α} {p : α Bool} :
(xs.replace a b).countP p =

View File

@@ -6,10 +6,12 @@ Authors: Leonardo de Moura
module
prelude
import all Init.Data.Array.Basic
import Init.Data.BEq
import Init.Data.List.Nat.BEq
import Init.ByCases
public import all Init.Data.Array.Basic
public import Init.Data.BEq
public import Init.Data.List.Nat.BEq
public import Init.ByCases
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -23,7 +25,7 @@ private theorem rel_of_isEqvAux
induction i with
| zero => contradiction
| succ i ih =>
simp only [Array.isEqvAux, Bool.and_eq_true, decide_eq_true_eq] at heqv
simp only [Array.isEqvAux, Bool.and_eq_true] at heqv
by_cases hj' : j < i
next =>
exact ih _ heqv.right hj'
@@ -69,7 +71,7 @@ theorem isEqv_eq_decide (xs ys : Array α) (r) :
simpa [isEqv_iff_rel] using h'
@[simp, grind =] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
simp [isEqv_eq_decide, List.isEqv_eq_decide]
simp [isEqv_eq_decide, List.isEqv_eq_decide, Array.size]
theorem eq_of_isEqv [DecidableEq α] (xs ys : Array α) (h : Array.isEqv xs ys (fun x y => x = y)) : xs = ys := by
have h, h' := rel_of_isEqv h
@@ -100,7 +102,7 @@ theorem beq_eq_decide [BEq α] (xs ys : Array α) :
simp [BEq.beq, isEqv_eq_decide]
@[simp, grind =] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
simp [beq_eq_decide, List.beq_eq_decide]
simp [beq_eq_decide, List.beq_eq_decide, Array.size]
end Array

View File

@@ -6,10 +6,12 @@ Authors: Kim Morrison
module
prelude
import all Init.Data.Array.Basic
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.Erase
import Init.Data.List.Nat.Basic
public import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Erase
public import Init.Data.List.Nat.Basic
public section
/-!
# Lemmas about `Array.eraseP`, `Array.erase`, and `Array.eraseIdx`.
@@ -24,7 +26,8 @@ open Nat
/-! ### eraseP -/
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
@[grind =]
theorem eraseP_empty : #[].eraseP p = #[] := by simp
theorem eraseP_of_forall_mem_not {xs : Array α} (h : a, a xs ¬p a) : xs.eraseP p = xs := by
rcases xs with xs
@@ -64,6 +67,7 @@ theorem exists_or_eq_self_of_eraseP (p) (xs : Array α) :
let _, ys, zs, _, _, e₁, e₂ := exists_of_eraseP al pa
rw [e₂]; simp [size_append, e₁]
@[grind =]
theorem size_eraseP {xs : Array α} : (xs.eraseP p).size = if xs.any p then xs.size - 1 else xs.size := by
split <;> rename_i h
· simp only [any_eq_true] at h
@@ -81,11 +85,12 @@ theorem le_size_eraseP {xs : Array α} : xs.size - 1 ≤ (xs.eraseP p).size := b
rcases xs with xs
simpa using List.le_length_eraseP
@[grind ]
theorem mem_of_mem_eraseP {xs : Array α} : a xs.eraseP p a xs := by
rcases xs with xs
simpa using List.mem_of_mem_eraseP
@[simp] theorem mem_eraseP_of_neg {xs : Array α} (pa : ¬p a) : a xs.eraseP p a xs := by
@[simp, grind] theorem mem_eraseP_of_neg {xs : Array α} (pa : ¬p a) : a xs.eraseP p a xs := by
rcases xs with xs
simpa using List.mem_eraseP_of_neg pa
@@ -93,15 +98,18 @@ theorem mem_of_mem_eraseP {xs : Array α} : a ∈ xs.eraseP p → a ∈ xs := by
rcases xs with xs
simp
@[grind _=_]
theorem eraseP_map {f : β α} {xs : Array β} : (xs.map f).eraseP p = (xs.eraseP (p f)).map f := by
rcases xs with xs
simpa using List.eraseP_map
@[grind =]
theorem eraseP_filterMap {f : α Option β} {xs : Array α} :
(filterMap f xs).eraseP p = filterMap f (xs.eraseP (fun x => match f x with | some y => p y | none => false)) := by
rcases xs with xs
simpa using List.eraseP_filterMap
@[grind =]
theorem eraseP_filter {f : α Bool} {xs : Array α} :
(filter f xs).eraseP p = filter f (xs.eraseP (fun x => p x && f x)) := by
rcases xs with xs
@@ -119,6 +127,7 @@ theorem eraseP_append_right {xs : Array α} ys (h : ∀ b ∈ xs, ¬p b) :
rcases ys with ys
simpa using List.eraseP_append_right ys (by simpa using h)
@[grind =]
theorem eraseP_append {xs : Array α} {ys : Array α} :
(xs ++ ys).eraseP p = if xs.any p then xs.eraseP p ++ ys else xs ++ ys.eraseP p := by
rcases xs with xs
@@ -126,6 +135,7 @@ theorem eraseP_append {xs : Array α} {ys : Array α} :
simp only [List.append_toArray, List.eraseP_toArray, List.eraseP_append, List.any_toArray]
split <;> simp
@[grind =]
theorem eraseP_replicate {n : Nat} {a : α} {p : α Bool} :
(replicate n a).eraseP p = if p a then replicate (n - 1) a else replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
@@ -165,6 +175,7 @@ theorem eraseP_eq_iff {p} {xs : Array α} :
· exact Or.inl h
· exact Or.inr a, l₁, by simpa using h₁, h₂, l, by simp
@[grind =]
theorem eraseP_comm {xs : Array α} (h : a xs, ¬ p a ¬ q a) :
(xs.eraseP p).eraseP q = (xs.eraseP q).eraseP p := by
rcases xs with xs
@@ -197,7 +208,7 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) (xs : Array α) : xs.erase a = x
theorem erase_ne_empty_iff [LawfulBEq α] {xs : Array α} {a : α} :
xs.erase a #[] xs #[] xs #[a] := by
rcases xs with xs
simp [List.erase_ne_nil_iff]
simp
theorem exists_erase_eq [LawfulBEq α] {a : α} {xs : Array α} (h : a xs) :
ys zs, a ys xs = ys.push a ++ zs xs.erase a = ys ++ zs := by
@@ -208,6 +219,7 @@ theorem exists_erase_eq [LawfulBEq α] {a : α} {xs : Array α} (h : a ∈ xs) :
(xs.erase a).size = xs.size - 1 := by
rw [erase_eq_eraseP]; exact size_eraseP_of_mem h (beq_self_eq_true a)
@[grind =]
theorem size_erase [LawfulBEq α] {a : α} {xs : Array α} :
(xs.erase a).size = if a xs then xs.size - 1 else xs.size := by
rw [erase_eq_eraseP, size_eraseP]
@@ -222,11 +234,12 @@ theorem le_size_erase [LawfulBEq α] {a : α} {xs : Array α} : xs.size - 1 ≤
rcases xs with xs
simpa using List.le_length_erase
@[grind ]
theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a xs.erase b) : a xs := by
rcases xs with xs
simpa using List.mem_of_mem_erase (by simpa using h)
@[simp] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {xs : Array α} (ab : a b) :
@[simp, grind] theorem mem_erase_of_ne [LawfulBEq α] {a b : α} {xs : Array α} (ab : a b) :
a xs.erase b a xs :=
erase_eq_eraseP b xs mem_eraseP_of_neg (mt eq_of_beq ab.symm)
@@ -234,6 +247,7 @@ theorem mem_of_mem_erase {a b : α} {xs : Array α} (h : a ∈ xs.erase b) : a
rw [erase_eq_eraseP', eraseP_eq_self_iff]
simp [forall_mem_ne']
@[grind _=_]
theorem erase_filter [LawfulBEq α] {f : α Bool} {xs : Array α} :
(filter f xs).erase a = filter f (xs.erase a) := by
rcases xs with xs
@@ -251,6 +265,7 @@ theorem erase_append_right [LawfulBEq α] {a : α} {xs : Array α} (ys : Array
rcases ys with ys
simpa using List.erase_append_right ys (by simpa using h)
@[grind =]
theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
(xs ++ ys).erase a = if a xs then xs.erase a ++ ys else xs ++ ys.erase a := by
rcases xs with xs
@@ -258,6 +273,7 @@ theorem erase_append [LawfulBEq α] {a : α} {xs ys : Array α} :
simp only [List.append_toArray, List.erase_toArray, List.erase_append, mem_toArray]
split <;> simp
@[grind =]
theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
(replicate n a).erase b = if b == a then replicate (n - 1) a else replicate n a := by
simp only [ List.toArray_replicate, List.erase_toArray]
@@ -269,6 +285,7 @@ abbrev erase_mkArray := @erase_replicate
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
@[grind =]
theorem erase_comm [LawfulBEq α] (a b : α) {xs : Array α} :
(xs.erase a).erase b = (xs.erase b).erase a := by
rcases xs with xs
@@ -291,7 +308,7 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by
simp only [ List.toArray_replicate, List.erase_toArray]
simp [List.erase_replicate]
simp
@[deprecated erase_replicate_self (since := "2025-03-18")]
abbrev erase_mkArray_self := @erase_replicate_self
@@ -312,6 +329,7 @@ theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.siz
xs.eraseIdx i h = xs.eraseIdxIfInBounds i := by
simp [eraseIdxIfInBounds, h]
@[grind =]
theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
xs.eraseIdx i h = xs.take i ++ xs.drop (i + 1) := by
rcases xs with xs
@@ -322,6 +340,7 @@ theorem eraseIdx_eq_take_drop_succ {xs : Array α} {i : Nat} (h) :
rw [List.take_of_length_le]
simp
@[grind =]
theorem getElem?_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} :
(xs.eraseIdx i)[j]? = if j < i then xs[j]? else xs[j + 1]? := by
rcases xs with xs
@@ -335,10 +354,11 @@ theorem getElem?_eraseIdx_of_lt {xs : Array α} {i : Nat} (h : i < xs.size) {j :
theorem getElem?_eraseIdx_of_ge {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : i j) :
(xs.eraseIdx i)[j]? = xs[j + 1]? := by
rw [getElem?_eraseIdx]
simp only [dite_eq_ite, ite_eq_right_iff]
simp only [ite_eq_right_iff]
intro h'
omega
@[grind =]
theorem getElem_eraseIdx {xs : Array α} {i : Nat} (h : i < xs.size) {j : Nat} (h' : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i)[j] = if h'' : j < i then
xs[j]
@@ -362,6 +382,7 @@ theorem eraseIdx_ne_empty_iff {xs : Array α} {i : Nat} {h} : xs.eraseIdx i ≠
simp [h]
· simp
@[grind ]
theorem mem_of_mem_eraseIdx {xs : Array α} {i : Nat} {h} {a : α} (h : a xs.eraseIdx i) : a xs := by
rcases xs with xs
simpa using List.mem_of_mem_eraseIdx (by simpa using h)
@@ -373,13 +394,29 @@ theorem eraseIdx_append_of_lt_size {xs : Array α} {k : Nat} (hk : k < xs.size)
simp at hk
simp [List.eraseIdx_append_of_lt_length, *]
theorem eraseIdx_append_of_length_le {xs : Array α} {k : Nat} (hk : xs.size k) (ys : Array α) (h) :
theorem eraseIdx_append_of_size_le {xs : Array α} {k : Nat} (hk : xs.size k) (ys : Array α) (h) :
eraseIdx (xs ++ ys) k = xs ++ eraseIdx ys (k - xs.size) (by simp at h; omega) := by
rcases xs with l
rcases ys with l'
simp at hk
simp [List.eraseIdx_append_of_length_le, *]
@[deprecated eraseIdx_append_of_size_le (since := "2025-06-11")]
abbrev eraseIdx_append_of_length_le := @eraseIdx_append_of_size_le
@[grind =]
theorem eraseIdx_append {xs ys : Array α} (h : k < (xs ++ ys).size) :
eraseIdx (xs ++ ys) k =
if h' : k < xs.size then
eraseIdx xs k ++ ys
else
xs ++ eraseIdx ys (k - xs.size) (by simp at h; omega) := by
split <;> rename_i h
· simp [eraseIdx_append_of_lt_size h]
· rw [eraseIdx_append_of_size_le]
omega
@[grind =]
theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
(replicate n a).eraseIdx k = replicate (n - 1) a := by
simp at h
@@ -428,6 +465,48 @@ theorem eraseIdx_set_gt {xs : Array α} {i : Nat} {j : Nat} {a : α} (h : i < j)
rcases xs with xs
simp [List.eraseIdx_set_gt, *]
@[grind =]
theorem eraseIdx_set {xs : Array α} {i : Nat} {a : α} {hi : i < xs.size} {j : Nat} {hj : j < (xs.set i a).size} :
(xs.set i a).eraseIdx j =
if h' : j < i then
(xs.eraseIdx j).set (i - 1) a (by simp; omega)
else if h'' : j = i then
xs.eraseIdx i
else
(xs.eraseIdx j (by simp at hj; omega)).set i a (by simp at hj ; omega) := by
split <;> rename_i h'
· rw [eraseIdx_set_lt]
omega
· split <;> rename_i h''
· subst h''
rw [eraseIdx_set_eq]
· rw [eraseIdx_set_gt]
omega
theorem set_eraseIdx_le {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : i j) (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a = (xs.set (j + 1) a (by simp at hj; omega)).eraseIdx i (by simp at ; omega) := by
rw [eraseIdx_set_lt]
· simp
· omega
theorem set_eraseIdx_gt {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (h : j < i) (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a = (xs.set j a).eraseIdx i (by simp at ; omega) := by
rw [eraseIdx_set_gt]
omega
@[grind =]
theorem set_eraseIdx {xs : Array α} {i : Nat} {w : i < xs.size} {j : Nat} {a : α} (hj : j < (xs.eraseIdx i).size) :
(xs.eraseIdx i).set j a =
if h' : i j then
(xs.set (j + 1) a (by simp at hj; omega)).eraseIdx i (by simp at ; omega)
else
(xs.set j a).eraseIdx i (by simp at ; omega) := by
split <;> rename_i h'
· rw [set_eraseIdx_le]
omega
· rw [set_eraseIdx_gt]
omega
@[simp] theorem set_getElem_succ_eraseIdx_succ
{xs : Array α} {i : Nat} (h : i + 1 < xs.size) :
(xs.eraseIdx (i + 1)).set i xs[i + 1] (by simp; omega) = xs.eraseIdx i := by

View File

@@ -6,8 +6,10 @@ Authors: Kim Morrison
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.TakeDrop
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.TakeDrop
public section
/-!
# Lemmas about `Array.extract`
@@ -29,7 +31,7 @@ namespace Array
· simp
omega
· simp only [size_extract] at h₁ h₂
simp [h]
simp
theorem size_extract_le {as : Array α} {i j : Nat} :
(as.extract i j).size j - i := by
@@ -46,17 +48,47 @@ theorem size_extract_of_le {as : Array α} {i j : Nat} (h : j ≤ as.size) :
simp
omega
@[simp]
theorem extract_push {as : Array α} {b : α} {start stop : Nat} (h : stop as.size) :
(as.push b).extract start stop = as.extract start stop := by
ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_push] at h₁ h₂
simp only [getElem_extract, getElem_push]
rw [dif_pos (by omega)]
@[grind =]
theorem extract_push {as : Array α} {b : α} {start stop : Nat} :
(as.push b).extract start stop =
if stop as.size then
as.extract start stop
else if start as.size then
(as.extract start as.size).push b
else #[] := by
split
· ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_push] at h₁ h₂
simp only [getElem_extract, getElem_push]
rw [dif_pos (by omega)]
· split
· ext i h₁ h₂
· simp
omega
· simp only [size_extract, size_push] at h₁ h₂
simp only [getElem_extract, getElem_push]
split <;> rename_i h₃
· split
· rfl
· simp_all
omega
· split <;> rename_i h₄
· simp at h₄
omega
· rfl
· ext i h₁ h₂
· simp
omega
· simp at h₂
@[simp]
theorem extract_push_of_le {as : Array α} {b : α} {start stop : Nat} (h : stop as.size) :
(as.push b).extract start stop = as.extract start stop := by
rw [extract_push, if_pos h]
@[simp, grind =]
theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
as.extract 0 stop = as.pop := by
ext i h₁ h₂
@@ -65,7 +97,7 @@ theorem extract_eq_pop {as : Array α} {stop : Nat} (h : stop = as.size - 1) :
· simp only [size_extract, size_pop] at h₁ h₂
simp [getElem_extract, getElem_pop]
@[simp]
@[simp, grind _=_]
theorem extract_append_extract {as : Array α} {i j k : Nat} :
as.extract i j ++ as.extract j k = as.extract (min i j) (max j k) := by
ext l h₁ h₂
@@ -162,14 +194,14 @@ theorem extract_sub_one {as : Array α} {i j : Nat} (h : j < as.size) :
@[simp]
theorem getElem?_extract_of_lt {as : Array α} {i j k : Nat} (h : k < min j as.size - i) :
(as.extract i j)[k]? = some (as[i + k]'(by omega)) := by
simp [getElem?_extract, h]
simp [h]
theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
(as.extract 0 (j + 1))[j]? = as[j]? := by
simp [getElem?_extract]
omega
@[simp] theorem extract_extract {as : Array α} {i j k l : Nat} :
@[simp, grind =] theorem extract_extract {as : Array α} {i j k l : Nat} :
(as.extract i j).extract k l = as.extract (i + k) (min (i + l) j) := by
ext m h₁ h₂
· simp
@@ -185,6 +217,7 @@ theorem ne_empty_of_extract_ne_empty {as : Array α} {i j : Nat} (h : as.extract
as #[] :=
mt extract_eq_empty_of_eq_empty h
@[grind =]
theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
(as.set k a).extract i j =
if _ : k < i then
@@ -211,13 +244,14 @@ theorem extract_set {as : Array α} {i j k : Nat} (h : k < as.size) {a : α} :
simp [getElem_set]
omega
@[grind =]
theorem set_extract {as : Array α} {i j k : Nat} (h : k < (as.extract i j).size) {a : α} :
(as.extract i j).set k a = (as.set (i + k) a (by simp at h; omega)).extract i j := by
ext l h₁ h₂
· simp
· simp_all [getElem_set]
@[simp]
@[simp, grind =]
theorem extract_append {as bs : Array α} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext l h₁ h₂
@@ -238,20 +272,18 @@ theorem extract_append_left {as bs : Array α} :
(as ++ bs).extract 0 as.size = as.extract 0 as.size := by
simp
@[simp] theorem extract_append_right {as bs : Array α} :
theorem extract_append_right {as bs : Array α} :
(as ++ bs).extract as.size (as.size + i) = bs.extract 0 i := by
simp only [extract_append, extract_size_left, Nat.sub_self, empty_append]
congr 1
omega
simp
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
@[simp, grind =] theorem map_extract {as : Array α} {i j : Nat} :
(as.extract i j).map f = (as.map f).extract i j := by
ext l h₁ h₂
· simp
· simp only [size_map, size_extract] at h₁ h₂
simp only [getElem_map, getElem_extract]
@[simp] theorem extract_replicate {a : α} {n i j : Nat} :
@[simp, grind =] theorem extract_replicate {a : α} {n i j : Nat} :
(replicate n a).extract i j = replicate (min j n - i) a := by
ext l h₁ h₂
· simp
@@ -299,6 +331,7 @@ theorem set_eq_push_extract_append_extract {as : Array α} {i : Nat} (h : i < as
simp at h
simp [List.set_eq_take_append_cons_drop, h, List.take_of_length_le]
@[grind =]
theorem extract_reverse {as : Array α} {i j : Nat} :
as.reverse.extract i j = (as.extract (as.size - j) (as.size - i)).reverse := by
ext l h₁ h₂
@@ -309,6 +342,7 @@ theorem extract_reverse {as : Array α} {i j : Nat} :
congr 1
omega
@[grind =]
theorem reverse_extract {as : Array α} {i j : Nat} :
(as.extract i j).reverse = as.reverse.extract (as.size - j) (as.size - i) := by
rw [extract_reverse]

View File

@@ -6,8 +6,10 @@ Authors: François G. Dorais
module
prelude
import Init.Data.List.FinRange
import Init.Data.Array.OfFn
public import Init.Data.List.FinRange
public import Init.Data.Array.OfFn
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -23,10 +25,10 @@ Examples:
-/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
@[simp] theorem size_finRange {n} : (Array.finRange n).size = n := by
@[simp, grind =] theorem size_finRange {n} : (Array.finRange n).size = n := by
simp [Array.finRange]
@[simp] theorem getElem_finRange {i : Nat} (h : i < (Array.finRange n).size) :
@[simp, grind =] theorem getElem_finRange {i : Nat} (h : i < (Array.finRange n).size) :
(Array.finRange n)[i] = Fin.cast size_finRange i, h := by
simp [Array.finRange]
@@ -49,6 +51,7 @@ theorem finRange_succ_last {n} :
· simp_all
omega
@[grind _=_]
theorem finRange_reverse {n} : (Array.finRange n).reverse = (Array.finRange n).map Fin.rev := by
ext i h
· simp

View File

@@ -6,11 +6,13 @@ Authors: Kim Morrison
module
prelude
import Init.Data.List.Nat.Find
import all Init.Data.Array.Basic
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
import Init.Data.Array.Range
public import Init.Data.List.Nat.Find
public import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.Array.Range
public section
/-!
# Lemmas about `Array.findSome?`, `Array.find?, `Array.findIdx`, `Array.findIdx?`, `Array.idxOf`.
@@ -38,11 +40,22 @@ theorem findSome?_singleton {a : α} {f : α → Option β} : #[a].findSome? f =
@[simp] theorem findSomeRev?_push_of_isNone {xs : Array α} (h : (f a).isNone) : (xs.push a).findSomeRev? f = xs.findSomeRev? f := by
cases xs; simp_all
@[grind =]
theorem findSomeRev?_push {xs : Array α} {a : α} {f : α Option β} :
(xs.push a).findSomeRev? f = (f a).or (xs.findSomeRev? f) := by
match h : f a with
| some b =>
rw [findSomeRev?_push_of_isSome]
all_goals simp_all
| none =>
rw [findSomeRev?_push_of_isNone]
all_goals simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {xs : Array α} (w : xs.findSome? f = some b) :
a, a xs f a = some b := by
cases xs; simp_all [List.exists_of_findSome?_eq_some]
@[simp] theorem findSome?_eq_none_iff : findSome? p xs = none x xs, p x = none := by
@[simp, grind =] theorem findSome?_eq_none_iff : findSome? p xs = none x xs, p x = none := by
cases xs; simp
@[simp] theorem findSome?_isSome_iff {f : α Option β} {xs : Array α} :
@@ -59,36 +72,39 @@ theorem findSome?_eq_some_iff {f : α → Option β} {xs : Array α} {b : β} :
· rintro xs, a, ys, h₀, h₁, h₂
exact xs.toList, a, ys.toList, by simpa using congrArg toList h₀, h₁, by simpa
@[simp] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard fun x => p x) xs = find? p xs := by
@[simp, grind =] theorem findSome?_guard {xs : Array α} : findSome? (Option.guard p) xs = find? p xs := by
cases xs; simp
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard fun x => p x) xs :=
theorem find?_eq_findSome?_guard {xs : Array α} : find? p xs = findSome? (Option.guard p) xs :=
findSome?_guard.symm
@[simp] theorem getElem?_zero_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
@[simp, grind =] theorem getElem?_zero_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f)[0]? = xs.findSome? f := by
cases xs; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filterMap {f : α Option β} {xs : Array α} (h) :
@[simp, grind =] theorem getElem_zero_filterMap {f : α Option β} {xs : Array α} (h) :
(xs.filterMap f)[0] = (xs.findSome? f).get (by cases xs; simpa [List.length_filterMap_eq_countP] using h) := by
cases xs; simp [ List.head_eq_getElem, getElem?_zero_filterMap]
cases xs; simp [ getElem?_zero_filterMap]
@[simp] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
@[simp, grind =] theorem back?_filterMap {f : α Option β} {xs : Array α} : (xs.filterMap f).back? = xs.findSomeRev? f := by
cases xs; simp
@[simp] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
@[simp, grind =] theorem back!_filterMap [Inhabited β] {f : α Option β} {xs : Array α} :
(xs.filterMap f).back! = (xs.findSomeRev? f).getD default := by
cases xs; simp
@[simp] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
@[simp, grind _=_] theorem map_findSome? {f : α Option β} {g : β γ} {xs : Array α} :
(xs.findSome? f).map g = xs.findSome? (Option.map g f) := by
cases xs; simp
@[grind _=_]
theorem findSome?_map {f : β γ} {xs : Array β} : findSome? p (xs.map f) = xs.findSome? (p f) := by
cases xs; simp [List.findSome?_map]
@[grind =]
theorem findSome?_append {xs ys : Array α} : (xs ++ ys).findSome? f = (xs.findSome? f).or (ys.findSome? f) := by
cases xs; cases ys; simp [List.findSome?_append]
@[grind =]
theorem getElem?_zero_flatten (xss : Array (Array α)) :
(flatten xss)[0]? = xss.findSome? fun xs => xs[0]? := by
cases xss using array₂_induction
@@ -104,12 +120,14 @@ theorem getElem_zero_flatten.proof {xss : Array (Array α)} (h : 0 < xss.flatten
obtain _, xs, m, rfl, h := h
exact xs, m, by simpa using h
@[grind =]
theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
(flatten xss)[0] = (xss.findSome? fun xs => xs[0]?).get (getElem_zero_flatten.proof h) := by
have t := getElem?_zero_flatten xss
simp [getElem?_eq_getElem, h] at t
simp at t
simp [ t]
@[grind =]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@@ -140,21 +158,37 @@ abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp] theorem find?_empty : find? p #[] = none := rfl
@[simp, grind =] theorem find?_empty : find? p #[] = none := rfl
@[simp] theorem find?_singleton {a : α} {p : α Bool} :
@[grind =]
theorem find?_singleton {a : α} {p : α Bool} :
#[a].find? p = if p a then some a else none := by
simp [singleton_eq_toArray_singleton]
simp
@[simp] theorem findRev?_push_of_pos {xs : Array α} (h : p a) :
findRev? p (xs.push a) = some a := by
cases xs; simp [h]
@[simp] theorem findRev?_cons_of_neg {xs : Array α} (h : ¬p a) :
@[simp] theorem findRev?_push_of_neg {xs : Array α} (h : ¬p a) :
findRev? p (xs.push a) = findRev? p xs := by
cases xs; simp [h]
@[simp] theorem find?_eq_none : find? p xs = none x xs, ¬ p x := by
@[deprecated findRev?_push_of_neg (since := "2025-06-12")]
abbrev findRev?_cons_of_neg := @findRev?_push_of_neg
@[grind =]
theorem finRev?_push {xs : Array α} :
findRev? p (xs.push a) = (Option.guard p a).or (xs.findRev? p) := by
cases h : p a
· rw [findRev?_push_of_neg, Option.guard_eq_none_iff.mpr h]
all_goals simp [h]
· rw [findRev?_push_of_pos, Option.guard_eq_some_iff.mpr rfl, h]
all_goals simp [h]
@[deprecated finRev?_push (since := "2025-06-12")]
abbrev findRev?_cons := @finRev?_push
@[simp, grind =] theorem find?_eq_none : find? p xs = none x xs, ¬ p x := by
cases xs; simp
theorem find?_eq_some_iff_append {xs : Array α} :
@@ -178,60 +212,63 @@ theorem find?_push_eq_some {xs : Array α} :
(xs.push a).find? p = some b xs.find? p = some b (xs.find? p = none (p a a = b)) := by
cases xs; simp
@[simp] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
@[simp, grind =] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
cases xs; simp
@[grind ]
theorem find?_some {xs : Array α} (h : find? p xs = some a) : p a := by
cases xs
simp at h
exact List.find?_some h
@[grind ]
theorem mem_of_find?_eq_some {xs : Array α} (h : find? p xs = some a) : a xs := by
cases xs
simp at h
simpa using List.mem_of_find?_eq_some h
@[grind]
theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h xs := by
cases xs
simp [List.get_find?_mem]
@[simp] theorem find?_filter {xs : Array α} (p q : α Bool) :
@[simp, grind =] theorem find?_filter {xs : Array α} (p q : α Bool) :
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
cases xs; simp
@[simp] theorem getElem?_zero_filter {p : α Bool} {xs : Array α} :
@[simp, grind =] theorem getElem?_zero_filter {p : α Bool} {xs : Array α} :
(xs.filter p)[0]? = xs.find? p := by
cases xs; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filter {p : α Bool} {xs : Array α} (h) :
@[simp, grind =] theorem getElem_zero_filter {p : α Bool} {xs : Array α} (h) :
(xs.filter p)[0] =
(xs.find? p).get (by cases xs; simpa [ List.countP_eq_length_filter] using h) := by
cases xs
simp [List.getElem_zero_eq_head]
@[simp] theorem back?_filter {p : α Bool} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
@[simp, grind =] theorem back?_filter {p : α Bool} {xs : Array α} : (xs.filter p).back? = xs.findRev? p := by
cases xs; simp
@[simp] theorem back!_filter [Inhabited α] {p : α Bool} {xs : Array α} :
@[simp, grind =] theorem back!_filter [Inhabited α] {p : α Bool} {xs : Array α} :
(xs.filter p).back! = (xs.findRev? p).get! := by
cases xs; simp [Option.get!_eq_getD]
@[simp] theorem find?_filterMap {xs : Array α} {f : α Option β} {p : β Bool} :
@[simp, grind =] theorem find?_filterMap {xs : Array α} {f : α Option β} {p : β Bool} :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
cases xs; simp
@[simp] theorem find?_map {f : β α} {xs : Array β} :
@[simp, grind =] theorem find?_map {f : β α} {xs : Array β} :
find? p (xs.map f) = (xs.find? (p f)).map f := by
cases xs; simp
@[simp] theorem find?_append {xs ys : Array α} :
@[simp, grind =] theorem find?_append {xs ys : Array α} :
(xs ++ ys).find? p = (xs.find? p).or (ys.find? p) := by
cases xs
cases ys
simp
@[simp] theorem find?_flatten {xss : Array (Array α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (·.find? p) := by
@[simp, grind _=_] theorem find?_flatten {xss : Array (Array α)} {p : α Bool} :
xss.flatten.find? p = xss.findSome? (find? p) := by
cases xss using array₂_induction
simp [List.findSome?_map, Function.comp_def]
@@ -270,10 +307,10 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
@[simp] theorem find?_flatMap {xs : Array α} {f : α Array β} {p : β Bool} :
@[simp, grind =] theorem find?_flatMap {xs : Array α} {f : α Array β} {p : β Bool} :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
cases xs
simp [List.find?_flatMap, Array.flatMap_toArray]
simp [List.find?_flatMap]
theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α Array β} {p : β Bool} :
(xs.flatMap f).find? p = none x xs, y f x, !p y := by
@@ -282,6 +319,7 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
@[deprecated find?_flatMap_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatMap_eq_none := @find?_flatMap_eq_none_iff
@[grind =]
theorem find?_replicate :
find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
simp [ List.toArray_replicate, List.find?_replicate]
@@ -312,7 +350,7 @@ abbrev find?_mkArray_of_neg := @find?_replicate_of_neg
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α Bool} :
(replicate n a).find? p = none n = 0 !p a := by
simp [ List.toArray_replicate, List.find?_replicate_eq_none_iff, Classical.or_iff_not_imp_left]
simp [ List.toArray_replicate, Classical.or_iff_not_imp_left]
@[deprecated find?_replicate_eq_none_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_none_iff := @find?_replicate_eq_none_iff
@@ -334,6 +372,7 @@ abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
@[deprecated get_find?_replicate (since := "2025-03-18")]
abbrev get_find?_mkArray := @get_find?_replicate
@[grind =]
theorem find?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
(H : (a : α), a xs P a) {p : β Bool} :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
@@ -347,11 +386,15 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
/-! ### findIdx -/
@[simp] theorem findIdx_empty : findIdx p #[] = 0 := rfl
@[grind =]
theorem findIdx_empty : findIdx p #[] = 0 := rfl
@[grind =]
theorem findIdx_singleton {a : α} {p : α Bool} :
#[a].findIdx p = if p a then 0 else 1 := by
simp
@[grind ]
theorem findIdx_of_getElem?_eq_some {xs : Array α} (w : xs[xs.findIdx p]? = some y) : p y := by
rcases xs with xs
exact List.findIdx_of_getElem?_eq_some (by simpa using w)
@@ -360,6 +403,8 @@ theorem findIdx_getElem {xs : Array α} {w : xs.findIdx p < xs.size} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
grind_pattern findIdx_getElem => xs[xs.findIdx p]
theorem findIdx_lt_size_of_exists {xs : Array α} (h : x xs, p x) :
xs.findIdx p < xs.size := by
rcases xs with xs
@@ -386,18 +431,24 @@ theorem findIdx_le_size {p : α → Bool} {xs : Array α} : xs.findIdx p ≤ xs.
· simp at e
exact Nat.le_of_eq (findIdx_eq_size.mpr e)
grind_pattern findIdx_le_size => xs.findIdx p, xs.size
@[simp]
theorem findIdx_lt_size {p : α Bool} {xs : Array α} :
xs.findIdx p < xs.size x xs, p x := by
rcases xs with xs
simp
grind_pattern findIdx_lt_size => xs.findIdx p, xs.size
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
theorem not_of_lt_findIdx {p : α Bool} {xs : Array α} {i : Nat} (h : i < xs.findIdx p) :
p (xs[i]'(Nat.le_trans h findIdx_le_size)) = false := by
rcases xs with xs
simpa using List.not_of_lt_findIdx (by simpa using h)
grind_pattern not_of_lt_findIdx => xs.findIdx p, xs[i]
/-- If `¬ p xs[j]` for all `j < i`, then `i ≤ xs.findIdx p`. -/
theorem le_findIdx_of_not {p : α Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
(h2 : j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false) : i xs.findIdx p := by
@@ -425,6 +476,7 @@ theorem findIdx_eq {p : α → Bool} {xs : Array α} {i : Nat} (h : i < xs.size)
simp at h3
simp_all [not_of_lt_findIdx h3]
@[grind =]
theorem findIdx_append {p : α Bool} {xs ys : Array α} :
(xs ++ ys).findIdx p =
if xs.findIdx p < xs.size then xs.findIdx p else ys.findIdx p + xs.size := by
@@ -432,12 +484,13 @@ theorem findIdx_append {p : α → Bool} {xs ys : Array α} :
rcases ys with ys
simp [List.findIdx_append]
@[grind =]
theorem findIdx_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx p = if xs.findIdx p < xs.size then xs.findIdx p else xs.size + if p a then 0 else 1 := by
simp only [push_eq_append, findIdx_append]
split <;> rename_i h
· rfl
· simp [findIdx_singleton, Nat.add_comm]
· simp [Nat.add_comm]
theorem findIdx_le_findIdx {xs : Array α} {p q : α Bool} (h : x xs, p x q x) : xs.findIdx q xs.findIdx p := by
rcases xs with xs
@@ -454,7 +507,7 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
rcases xs with xs
exact List.false_of_mem_take_findIdx (by simpa using h)
@[simp] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
@[simp, grind =] theorem findIdx_extract {xs : Array α} {i : Nat} {p : α Bool} :
(xs.extract 0 i).findIdx p = min i (xs.findIdx p) := by
cases xs
simp
@@ -466,24 +519,24 @@ theorem false_of_mem_extract_findIdx {xs : Array α} {p : α → Bool} (h : x
/-! ### findIdx? -/
@[simp] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
theorem findIdx?_singleton {a : α} {p : α Bool} :
@[simp, grind =] theorem findIdx?_empty : (#[] : Array α).findIdx? p = none := by simp
@[grind =] theorem findIdx?_singleton {a : α} {p : α Bool} :
#[a].findIdx? p = if p a then some 0 else none := by
simp
@[simp]
@[simp, grind =]
theorem findIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
xs.findIdx? p = none x, x xs p x = false := by
rcases xs with xs
simp
@[simp]
@[simp, grind =]
theorem findIdx?_isSome {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
rcases xs with xs
simp [List.findIdx?_isSome]
@[simp]
@[simp, grind =]
theorem findIdx?_isNone {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
rcases xs with xs
@@ -502,7 +555,7 @@ theorem findIdx?_eq_some_of_exists {xs : Array α} {p : α → Bool} (h : ∃ x,
theorem findIdx?_eq_none_iff_findIdx_eq {xs : Array α} {p : α Bool} :
xs.findIdx? p = none xs.findIdx p = xs.size := by
rcases xs with xs
simp [List.findIdx?_eq_none_iff_findIdx_eq]
simp
theorem findIdx?_eq_guard_findIdx_lt {xs : Array α} {p : α Bool} :
xs.findIdx? p = Option.guard (fun i => i < xs.size) (xs.findIdx p) := by
@@ -525,18 +578,19 @@ theorem of_findIdx?_eq_none {xs : Array α} {p : α → Bool} (w : xs.findIdx? p
rcases xs with xs
simpa using List.of_findIdx?_eq_none (by simpa using w)
@[simp] theorem findIdx?_map {f : β α} {xs : Array β} {p : α Bool} :
@[simp, grind =] theorem findIdx?_map {f : β α} {xs : Array β} {p : α Bool} :
findIdx? p (xs.map f) = xs.findIdx? (p f) := by
rcases xs with xs
simp [List.findIdx?_map]
@[simp] theorem findIdx?_append :
@[simp, grind =] theorem findIdx?_append :
(xs ++ ys : Array α).findIdx? p =
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.size) := by
rcases xs with xs
rcases ys with ys
simp [List.findIdx?_append]
@[grind =]
theorem findIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findIdx? p = (xs.findIdx? p).or (if p a then some xs.size else none) := by
simp only [push_eq_append, findIdx?_append]
@@ -552,7 +606,7 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
cases xss using array₂_induction
simp [List.findIdx?_flatten, Function.comp_def]
@[simp] theorem findIdx?_replicate :
@[simp, grind =] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n p a then some 0 else none := by
rw [ List.toArray_replicate]
simp only [List.findIdx?_toArray]
@@ -577,6 +631,7 @@ theorem findIdx?_eq_none_of_findIdx?_eq_none {xs : Array α} {p q : α → Bool}
rcases xs with xs
simpa using List.findIdx?_eq_none_of_findIdx?_eq_none (by simpa using w)
@[grind =]
theorem findIdx_eq_getD_findIdx? {xs : Array α} {p : α Bool} :
xs.findIdx p = (xs.findIdx? p).getD xs.size := by
rcases xs with xs
@@ -593,14 +648,17 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
cases xs
simp [hf]
@[simp] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
@[simp, grind =] theorem findIdx?_take {xs : Array α} {i : Nat} {p : α Bool} :
(xs.take i).findIdx? p = (xs.findIdx? p).bind (Option.guard (fun j => j < i)) := by
cases xs
simp
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
@[grind =]
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
@[grind =]
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp
@@ -618,7 +676,7 @@ theorem findFinIdx?_eq_pmap_findIdx? {xs : Array α} {p : α → Bool} :
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
@[simp] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
@[simp, grind =] theorem findFinIdx?_eq_none_iff {xs : Array α} {p : α Bool} :
xs.findFinIdx? p = none x, x xs ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@@ -634,12 +692,14 @@ theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.si
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[grind =]
theorem findFinIdx?_push {xs : Array α} {a : α} {p : α Bool} :
(xs.push a).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or (if p a then some xs.size, by simp else none) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_push, Option.pmap_or]
split <;> rename_i h _ <;> split <;> simp [h]
@[grind =]
theorem findFinIdx?_append {xs ys : Array α} {p : α Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
@@ -649,17 +709,17 @@ theorem findFinIdx?_append {xs ys : Array α} {p : α → Bool} :
· simp [h, Option.pmap_map, Option.map_pmap, Nat.add_comm]
· simp [h]
@[simp]
@[simp, grind =]
theorem isSome_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by
rcases xs with xs
simp
simp [Array.size]
@[simp]
@[simp, grind =]
theorem isNone_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
rcases xs with xs
simp
simp [Array.size]
@[simp] theorem findFinIdx?_subtype {p : α Prop} {xs : Array { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
@@ -667,7 +727,8 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
cases xs
simp only [List.findFinIdx?_toArray, hf, List.findFinIdx?_subtype]
rw [findFinIdx?_congr List.unattach_toArray]
simp [Function.comp_def]
simp only [Option.map_map, Function.comp_def, Fin.cast_trans]
simp [Array.size]
/-! ### idxOf
@@ -675,6 +736,7 @@ The verification API for `idxOf` is still incomplete.
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
-/
@[grind =]
theorem idxOf_append [BEq α] [LawfulBEq α] {xs ys : Array α} {a : α} :
(xs ++ ys).idxOf a = if a xs then xs.idxOf a else ys.idxOf a + xs.size := by
rw [idxOf, findIdx_append]
@@ -688,10 +750,23 @@ theorem idxOf_eq_size [BEq α] [LawfulBEq α] {xs : Array α} (h : a ∉ xs) : x
rcases xs with xs
simp [List.idxOf_eq_length (by simpa using h)]
theorem idxOf_lt_length [BEq α] [LawfulBEq α] {xs : Array α} (h : a xs) : xs.idxOf a < xs.size := by
theorem idxOf_lt_length_of_mem [BEq α] [LawfulBEq α] {xs : Array α} (h : a xs) : xs.idxOf a < xs.size := by
rcases xs with xs
simp [List.idxOf_lt_length (by simpa using h)]
simp [List.idxOf_lt_length_of_mem (by simpa using h)]
theorem idxOf_le_size [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf a xs.size := by
rcases xs with xs
simp [List.idxOf_le_length]
grind_pattern idxOf_le_size => xs.idxOf a, xs.size
theorem idxOf_lt_size_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf a < xs.size a xs := by
rcases xs with xs
simp [List.idxOf_lt_length_iff]
grind_pattern idxOf_lt_size_iff => xs.idxOf a, xs.size
/-! ### idxOf?
@@ -699,27 +774,24 @@ The verification API for `idxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
-/
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
@[grind =] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
@[simp, grind =] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.idxOf? a = none a xs := by
rcases xs with xs
simp [List.idxOf?_eq_none_iff]
@[simp]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isSome a xs := by
rcases xs with xs
simp
@[simp]
@[grind =]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isNone = ¬ a xs := by
rcases xs with xs
simp
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
@@ -728,30 +800,31 @@ The lemmas below should be made consistent with those for `findFinIdx?` (and pro
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
simp [idxOf?, finIdxOf?]
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[grind =] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
@[simp, grind =] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by
rcases xs with xs
simp [List.finIdxOf?_eq_none_iff]
simp [List.finIdxOf?_eq_none_iff, Array.size]
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} {i : Fin xs.size} :
xs.finIdxOf? a = some i xs[i] = a j (_ : j < i), ¬xs[j] = a := by
rcases xs with xs
unfold Array.size at i
simp [List.finIdxOf?_eq_some_iff]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome a xs := by
@[simp, grind =]
theorem isSome_finIdxOf? [BEq α] [PartialEquivBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome = xs.contains a := by
rcases xs with xs
simp
simp [Array.size]
@[simp]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = ¬ a xs := by
@[simp, grind =]
theorem isNone_finIdxOf? [BEq α] [PartialEquivBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = !xs.contains a := by
rcases xs with xs
simp
simp [Array.size]
end Array

View File

@@ -7,7 +7,9 @@ Authors: Leonardo de Moura
module
prelude
import Init.Data.Array.Basic
public import Init.Data.Array.Basic
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.

View File

@@ -6,8 +6,10 @@ Authors: Kim Morrison
module
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.Nat.InsertIdx
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.InsertIdx
public section
/-!
# insertIdx
@@ -44,13 +46,19 @@ theorem insertIdx_zero {xs : Array α} {x : α} : xs.insertIdx 0 x = #[x] ++ xs
@[simp] theorem size_insertIdx {xs : Array α} (h : i xs.size) : (xs.insertIdx i a).size = xs.size + 1 := by
rcases xs with xs
simp at h
simp [List.length_insertIdx, h]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
theorem eraseIdx_insertIdx_self {i : Nat} {xs : Array α} (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
rcases xs with xs
simp_all
@[deprecated eraseIdx_insertIdx_self (since := "2025-06-15")]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
simp [eraseIdx_insertIdx_self]
theorem insertIdx_eraseIdx_of_ge {as : Array α}
(w₁ : i < as.size) (w₂ : j (as.eraseIdx i).size) (h : i j) :
(as.eraseIdx i).insertIdx j a =
@@ -65,6 +73,18 @@ theorem insertIdx_eraseIdx_of_le {as : Array α}
cases as
simpa using List.insertIdx_eraseIdx_of_le (by simpa) (by simpa)
@[grind =]
theorem insertIdx_eraseIdx {as : Array α} (h₁ : i < as.size) (h₂ : j (as.eraseIdx i).size) :
(as.eraseIdx i).insertIdx j a =
if h : i j then
(as.insertIdx (j + 1) a (by simp_all; omega)).eraseIdx i (by simp_all; omega)
else
(as.insertIdx j a).eraseIdx (i + 1) (by simp_all) := by
split <;> rename_i h'
· rw [insertIdx_eraseIdx_of_ge] <;> omega
· rw [insertIdx_eraseIdx_of_le] <;> omega
@[grind =]
theorem insertIdx_comm (a b : α) {i j : Nat} {xs : Array α} (_ : i j) (_ : j xs.size) :
(xs.insertIdx i a).insertIdx (j + 1) b (by simpa) =
(xs.insertIdx j b).insertIdx i a (by simp; omega) := by
@@ -80,6 +100,7 @@ theorem insertIdx_size_self {xs : Array α} {x : α} : xs.insertIdx xs.size x =
rcases xs with xs
simp
@[grind =]
theorem getElem_insertIdx {xs : Array α} {x : α} {i k : Nat} (w : i xs.size) (h : k < (xs.insertIdx i x).size) :
(xs.insertIdx i x)[k] =
if h₁ : k < i then
@@ -90,21 +111,22 @@ theorem getElem_insertIdx {xs : Array α} {x : α} {i k : Nat} (w : i ≤ xs.siz
else
xs[k-1]'(by simp [size_insertIdx] at h; omega) := by
cases xs
simp [List.getElem_insertIdx, w]
simp [List.getElem_insertIdx]
theorem getElem_insertIdx_of_lt {xs : Array α} {x : α} {i k : Nat} (w : i xs.size) (h : k < i) :
(xs.insertIdx i x)[k]'(by simp; omega) = xs[k] := by
simp [getElem_insertIdx, w, h]
simp [getElem_insertIdx, h]
theorem getElem_insertIdx_self {xs : Array α} {x : α} {i : Nat} (w : i xs.size) :
(xs.insertIdx i x)[i]'(by simp; omega) = x := by
simp [getElem_insertIdx, w]
simp [getElem_insertIdx]
theorem getElem_insertIdx_of_gt {xs : Array α} {x : α} {i k : Nat} (w : k xs.size) (h : k > i) :
(xs.insertIdx i x)[k]'(by simp; omega) = xs[k - 1]'(by omega) := by
simp [getElem_insertIdx, w, h]
simp [getElem_insertIdx]
rw [dif_neg (by omega), dif_neg (by omega)]
@[grind =]
theorem getElem?_insertIdx {xs : Array α} {x : α} {i k : Nat} (h : i xs.size) :
(xs.insertIdx i x)[k]? =
if k < i then
@@ -115,7 +137,7 @@ theorem getElem?_insertIdx {xs : Array α} {x : α} {i k : Nat} (h : i ≤ xs.si
else
xs[k-1]? := by
cases xs
simp [List.getElem?_insertIdx, h]
simp [List.getElem?_insertIdx]
theorem getElem?_insertIdx_of_lt {xs : Array α} {x : α} {i k : Nat} (w : i xs.size) (h : k < i) :
(xs.insertIdx i x)[k]? = xs[k]? := by

View File

@@ -6,7 +6,9 @@ Authors: Leonardo de Moura
module
prelude
import Init.Data.Array.Basic
public import Init.Data.Array.Basic
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.

File diff suppressed because it is too large Load Diff

View File

@@ -6,5 +6,7 @@ Author: Kim Morrison
module
prelude
import Init.Data.Array.Lex.Basic
import Init.Data.Array.Lex.Lemmas
public import Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lex.Lemmas
public section

View File

@@ -6,9 +6,11 @@ Author: Kim Morrison
module
prelude
import Init.Data.Array.Basic
import Init.Data.Nat.Lemmas
import Init.Data.Range
public import Init.Data.Array.Basic
public import Init.Data.Nat.Lemmas
public import Init.Data.Range
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.

View File

@@ -6,9 +6,11 @@ Author: Kim Morrison
module
prelude
import all Init.Data.Array.Lex.Basic
import Init.Data.Array.Lemmas
import Init.Data.List.Lex
public import all Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Lex
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -29,16 +31,12 @@ protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {xs ys
Decidable.not_not
@[simp] theorem lex_empty [BEq α] {lt : α α Bool} {xs : Array α} : xs.lex #[] lt = false := by
simp [lex, Id.run]
@[simp] theorem singleton_lex_singleton [BEq α] {lt : α α Bool} : #[a].lex #[b] lt = lt a b := by
simp only [lex, List.getElem_toArray, List.getElem_singleton]
cases lt a b <;> cases a != b <;> simp [Id.run]
simp [lex]
private theorem cons_lex_cons [BEq α] {lt : α α Bool} {a b : α} {xs ys : Array α} :
(#[a] ++ xs).lex (#[b] ++ ys) lt =
(lt a b || a == b && xs.lex ys lt) := by
simp only [lex, Id.run]
simp only [lex]
simp only [Std.Range.forIn'_eq_forIn'_range', size_append, List.size_toArray, List.length_singleton,
Nat.add_comm 1]
simp [Nat.add_min_add_right, List.range'_succ, getElem_append_left, List.range'_succ_left,
@@ -51,13 +49,16 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
@[simp, grind =] theorem _root_.List.lex_toArray [BEq α] {lt : α α Bool} {l₁ l₂ : List α} :
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
induction l₁ generalizing l₂ with
| nil => cases l₂ <;> simp [lex, Id.run]
| nil => cases l₂ <;> simp [lex]
| cons x l₁ ih =>
cases l₂ with
| nil => simp [lex, Id.run]
| nil => simp [lex]
| cons y l₂ =>
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
theorem singleton_lex_singleton [BEq α] {lt : α α Bool} : #[a].lex #[b] lt = lt a b := by
simp
@[simp, grind =] theorem lex_toList [BEq α] {lt : α α Bool} {xs ys : Array α} :
xs.toList.lex ys.toList lt = xs.lex ys lt := by
cases xs <;> cases ys <;> simp
@@ -163,7 +164,7 @@ instance [DecidableEq α] [LT α] [DecidableLT α]
{xs ys : Array α} : lex xs ys = false ys xs := by
cases xs
cases ys
simp [List.not_lt_iff_ge]
simp
instance [DecidableEq α] [LT α] [DecidableLT α] : DecidableLT (Array α) :=
fun xs ys => decidable_of_iff (lex xs ys = true) lex_eq_true_iff_lt

View File

@@ -6,11 +6,13 @@ Authors: Mario Carneiro, Kim Morrison
module
prelude
import all Init.Data.Array.Basic
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
import Init.Data.Array.OfFn
import all Init.Data.List.MapIdx
public import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.Array.OfFn
public import all Init.Data.List.MapIdx
public section
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
@@ -27,7 +29,7 @@ theorem mapFinIdx_induction (xs : Array α) (f : (i : Nat) → α → (h : i < x
motive xs.size eq : (Array.mapFinIdx xs f).size = xs.size,
i h, p i ((Array.mapFinIdx xs f)[i]) h := by
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : i h h', p i bs[i] h) (hm : motive j) :
let as : Array β := Array.mapFinIdxM.map (m := Id) xs f i j h bs
let as : Array β := Id.run <| Array.mapFinIdxM.map xs (pure <| f · · ·) i j h bs
motive xs.size eq : as.size = xs.size, i h, p i as[i] h := by
induction i generalizing j bs with simp [mapFinIdxM.map]
| zero =>
@@ -51,27 +53,27 @@ theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.siz
i h, p i ((Array.mapFinIdx xs f)[i]) h :=
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem size_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
(xs.mapFinIdx f).size = xs.size :=
(mapFinIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
@[simp] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
@[simp, grind =] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
@[simp] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
@[simp, grind =] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
(h : i < (xs.mapFinIdx f).size) :
(xs.mapFinIdx f)[i] = f i (xs[i]'(by simp_all)) (by simp_all) :=
(mapFinIdx_spec (p := fun i b h => b = f i xs[i] h) fun _ _ => rfl).2 i _
@[simp] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
@[simp, grind =] theorem getElem?_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat} :
(xs.mapFinIdx f)[i]? =
xs[i]?.pbind fun b h => some <| f i b (getElem?_eq_some_iff.1 h).1 := by
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
split <;> simp_all
@[simp] theorem toList_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem toList_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
(xs.mapFinIdx f).toList = xs.toList.mapFinIdx (fun i a h => f i a (by simpa)) := by
apply List.ext_getElem <;> simp
@@ -91,20 +93,20 @@ theorem mapIdx_spec {f : Nat → α → β} {xs : Array α}
i h, p i ((xs.mapIdx f)[i]) h :=
(mapIdx_induction (motive := fun _ => True) trivial fun _ _ _ => hs .., trivial).2
@[simp] theorem size_mapIdx {f : Nat α β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
@[simp, grind =] theorem size_mapIdx {f : Nat α β} {xs : Array α} : (xs.mapIdx f).size = xs.size :=
(mapIdx_spec (p := fun _ _ _ => True) (hs := fun _ _ => trivial)).1
@[simp] theorem getElem_mapIdx {f : Nat α β} {xs : Array α} {i : Nat}
@[simp, grind =] theorem getElem_mapIdx {f : Nat α β} {xs : Array α} {i : Nat}
(h : i < (xs.mapIdx f).size) :
(xs.mapIdx f)[i] = f i (xs[i]'(by simp_all)) :=
(mapIdx_spec (p := fun i b h => b = f i xs[i]) fun _ _ => rfl).2 i (by simp_all)
@[simp] theorem getElem?_mapIdx {f : Nat α β} {xs : Array α} {i : Nat} :
@[simp, grind =] theorem getElem?_mapIdx {f : Nat α β} {xs : Array α} {i : Nat} :
(xs.mapIdx f)[i]? =
xs[i]?.map (f i) := by
simp [getElem?_def, size_mapIdx, getElem_mapIdx]
@[simp] theorem toList_mapIdx {f : Nat α β} {xs : Array α} :
@[simp, grind =] theorem toList_mapIdx {f : Nat α β} {xs : Array α} :
(xs.mapIdx f).toList = xs.toList.mapIdx (fun i a => f i a) := by
apply List.ext_getElem <;> simp
@@ -126,7 +128,7 @@ namespace Array
/-! ### zipIdx -/
@[simp] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
@[simp, grind =] theorem getElem_zipIdx {xs : Array α} {k : Nat} {i : Nat} (h : i < (xs.zipIdx k).size) :
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
simp [zipIdx]
@@ -135,12 +137,12 @@ abbrev getElem_zipWithIndex := @getElem_zipIdx
@[simp, grind =] theorem zipIdx_toArray {l : List α} {k : Nat} :
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
ext i hi₁ hi₂ <;> simp [Nat.add_comm]
ext i hi₁ hi₂ <;> simp
@[deprecated zipIdx_toArray (since := "2025-01-21")]
abbrev zipWithIndex_toArray := @zipIdx_toArray
@[simp] theorem toList_zipIdx {xs : Array α} {k : Nat} :
@[simp, grind =] theorem toList_zipIdx {xs : Array α} {k : Nat} :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs
simp
@@ -185,24 +187,26 @@ abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
subst w
rfl
@[simp]
@[simp, grind =]
theorem mapFinIdx_empty {f : (i : Nat) α (h : i < 0) β} : mapFinIdx #[] f = #[] :=
rfl
theorem mapFinIdx_eq_ofFn {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
xs.mapFinIdx f = Array.ofFn fun i : Fin xs.size => f i xs[i] i.2 := by
cases xs
simp [List.mapFinIdx_eq_ofFn]
simp only [List.mapFinIdx_toArray, List.mapFinIdx_eq_ofFn, Fin.getElem_fin, List.getElem_toArray]
simp [Array.size]
@[grind =]
theorem mapFinIdx_append {xs ys : Array α} {f : (i : Nat) α (h : i < (xs ++ ys).size) β} :
(xs ++ ys).mapFinIdx f =
xs.mapFinIdx (fun i a h => f i a (by simp; omega)) ++
ys.mapFinIdx (fun i a h => f (i + xs.size) a (by simp; omega)) := by
cases xs
cases ys
simp [List.mapFinIdx_append]
simp [List.mapFinIdx_append, Array.size]
@[simp]
@[simp, grind =]
theorem mapFinIdx_push {xs : Array α} {a : α} {f : (i : Nat) α (h : i < (xs.push a).size) β} :
mapFinIdx (xs.push a) f =
(mapFinIdx xs (fun i a h => f i a (by simp; omega))).push (f xs.size a (by simp)) := by
@@ -236,7 +240,7 @@ theorem exists_of_mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) → α
rcases xs with xs
exact List.exists_of_mem_mapFinIdx (by simpa using h)
@[simp] theorem mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@[simp, grind =] theorem mem_mapFinIdx {b : β} {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
b xs.mapFinIdx f (i : Nat) (h : i < xs.size), f i xs[i] h = b := by
rcases xs with xs
simp
@@ -264,12 +268,12 @@ theorem mapFinIdx_eq_append_iff {xs : Array α} {f : (i : Nat) → α → (h : i
toArray_eq_append_iff]
constructor
· rintro l₁, l₂, rfl, rfl, rfl
refine l₁.toArray, l₂.toArray, by simp_all
refine l₁.toArray, l₂.toArray, by simp_all [Array.size]
· rintro l₁, l₂, rfl, h₁, h₂
simp [ toList_inj] at h₁ h₂
obtain rfl := h₁
obtain rfl := h₂
refine l₁, l₂, by simp_all
refine l₁, l₂, by simp_all [Array.size]
theorem mapFinIdx_eq_push_iff {xs : Array α} {b : β} {f : (i : Nat) α (h : i < xs.size) β} :
xs.mapFinIdx f = ys.push b
@@ -289,7 +293,7 @@ theorem mapFinIdx_eq_mapFinIdx_iff {xs : Array α} {f g : (i : Nat) → α → (
rw [eq_comm, mapFinIdx_eq_iff]
simp
@[simp] theorem mapFinIdx_mapFinIdx {xs : Array α}
@[simp, grind =] theorem mapFinIdx_mapFinIdx {xs : Array α}
{f : (i : Nat) α (h : i < xs.size) β}
{g : (i : Nat) β (h : i < (xs.mapFinIdx f).size) γ} :
(xs.mapFinIdx f).mapFinIdx g = xs.mapFinIdx (fun i a h => g i (f i a h) (by simpa using h)) := by
@@ -304,14 +308,14 @@ theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
@[simp] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) α (h : i < xs.reverse.size) β} :
@[simp, grind =] theorem mapFinIdx_reverse {xs : Array α} {f : (i : Nat) α (h : i < xs.reverse.size) β} :
xs.reverse.mapFinIdx f = (xs.mapFinIdx (fun i a h => f (xs.size - 1 - i) a (by simp; omega))).reverse := by
rcases xs with l
simp [List.mapFinIdx_reverse]
simp [List.mapFinIdx_reverse, Array.size]
/-! ### mapIdx -/
@[simp]
@[simp, grind =]
theorem mapIdx_empty {f : Nat α β} : mapIdx f #[] = #[] :=
rfl
@@ -331,13 +335,14 @@ theorem mapIdx_eq_zipIdx_map {xs : Array α} {f : Nat → α → β} :
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
theorem mapIdx_append {xs ys : Array α} :
(xs ++ ys).mapIdx f = xs.mapIdx f ++ ys.mapIdx (fun i => f (i + xs.size)) := by
rcases xs with xs
rcases ys with ys
simp [List.mapIdx_append]
@[simp]
@[simp, grind =]
theorem mapIdx_push {xs : Array α} {a : α} :
mapIdx f (xs.push a) = (mapIdx f xs).push (f xs.size a) := by
simp [ append_singleton, mapIdx_append]
@@ -359,7 +364,7 @@ theorem exists_of_mem_mapIdx {b : β} {xs : Array α}
rw [mapIdx_eq_mapFinIdx] at h
simpa [Fin.exists_iff] using exists_of_mem_mapFinIdx h
@[simp] theorem mem_mapIdx {b : β} {xs : Array α} :
@[simp, grind =] theorem mem_mapIdx {b : β} {xs : Array α} :
b mapIdx f xs (i : Nat) (h : i < xs.size), f i xs[i] = b := by
constructor
· intro h
@@ -413,7 +418,7 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
rcases xs with xs
simp [List.mapIdx_eq_mapIdx_iff]
@[simp] theorem mapIdx_set {xs : Array α} {i : Nat} {h : i < xs.size} {a : α} :
@[simp, grind =] theorem mapIdx_set {f : Nat α β} {xs : Array α} {i : Nat} {h : i < xs.size} {a : α} :
(xs.set i a).mapIdx f = (xs.mapIdx f).set i (f i a) (by simpa) := by
rcases xs with xs
simp [List.mapIdx_set]
@@ -423,17 +428,17 @@ theorem mapIdx_eq_mapIdx_iff {xs : Array α} :
rcases xs with xs
simp [List.mapIdx_set]
@[simp] theorem back?_mapIdx {xs : Array α} {f : Nat α β} :
@[simp, grind =] theorem back?_mapIdx {xs : Array α} {f : Nat α β} :
(mapIdx f xs).back? = (xs.back?).map (f (xs.size - 1)) := by
rcases xs with xs
simp [List.getLast?_mapIdx]
@[simp] theorem back_mapIdx {xs : Array α} {f : Nat α β} (h) :
@[simp, grind =] theorem back_mapIdx {xs : Array α} {f : Nat α β} (h) :
(xs.mapIdx f).back h = f (xs.size - 1) (xs.back (by simpa using h)) := by
rcases xs with xs
simp [List.getLast_mapIdx]
@[simp] theorem mapIdx_mapIdx {xs : Array α} {f : Nat α β} {g : Nat β γ} :
@[simp, grind =] theorem mapIdx_mapIdx {xs : Array α} {f : Nat α β} {g : Nat β γ} :
(xs.mapIdx f).mapIdx g = xs.mapIdx (fun i => g i f i) := by
simp [mapIdx_eq_iff]
@@ -446,7 +451,7 @@ theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β}
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
@[simp] theorem mapIdx_reverse {xs : Array α} {f : Nat α β} :
@[simp, grind =] theorem mapIdx_reverse {xs : Array α} {f : Nat α β} :
xs.reverse.mapIdx f = (mapIdx (fun i => f (xs.size - 1 - i)) xs).reverse := by
rcases xs with xs
simp [List.mapIdx_reverse]
@@ -455,7 +460,7 @@ end Array
namespace List
@[grind] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind =] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : (i : Nat) α (h : i < l.length) m β} :
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
@@ -476,7 +481,7 @@ namespace List
simp only [Array.mapFinIdxM, mapFinIdxM]
exact go _ #[] _
@[grind] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
@[grind =] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
{f : Nat α m β} :
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :
@@ -486,7 +491,7 @@ namespace List
| x :: xs => simp only [mapFinIdxM.go, mapIdxM.go, go]
unfold Array.mapIdxM
rw [mapFinIdxM_toArray]
simp only [mapFinIdxM, mapIdxM]
simp only [mapFinIdxM, mapIdxM, Array.size]
rw [go]
end List

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