Compare commits

..

679 Commits

Author SHA1 Message Date
Rob Simmons
5e41db4243 Add missing file 2025-11-21 16:14:05 -05:00
Rob Simmons
17ae8c790f PR comments 2025-11-21 13:37:05 -05:00
Rob Simmons
4417c24128 Chicago headline 2025-11-21 13:37:05 -05:00
Rob Simmons
72371c8d2d Fixed missed tests 2025-11-21 13:37:05 -05:00
Rob Simmons
77ae8ad8a8 docstring for useTraceSynthMsg 2025-11-21 13:37:05 -05:00
Rob Simmons
f693fbd6fd Undo accidental change 2025-11-21 13:37:05 -05:00
Rob Simmons
41755a1ac3 Workshopping error messages a bit, updating test cases 2025-11-21 13:37:05 -05:00
Rob Simmons
e8b49ee876 feat: improved error message and error explanation for typeclass synthesis failure 2025-11-21 13:37:05 -05:00
Joachim Breitner
4288aa71e0 chore: do not set unused Option.Decl.group (#11307)
This PR removes all code that sets the `Option.Decl.group` field, which
is unused and has no clearly documented meaning.

The actual removal of the field would be #11305.
2025-11-21 16:44:38 +00:00
Joachim Breitner
0471319b5a chore: tests: use filenames as test names (#11302)
This PR renames the CTests tests to use filenames as test names. So
instead of
```
        2080 - leanruntest_issue5767.lean (Failed)
```
we get
```
        2080 - tests/lean/run/issue5767.lean (Failed)
```
which allows Ctrl-Click’ing on them in the VSCode terminal.
2025-11-21 12:40:58 +00:00
Wojciech Różowski
2e22c854cb feat: add intersection on ExtDTreeMap/ExtTreeMap/ExtTreeSet (#11292)
This PR adds intersection operation on
`ExtDTreeMap`/`ExtTreeMap`/`ExtTreeSet` and proves several lemmas about
it.
2025-11-21 11:25:58 +00:00
Wojciech Różowski
e7ece45e3c refactor: rename congruence lemmas for union on DHashMap/HashMap/HashSet/DTreeMap/TreeMap/TreeSet (#11267)
This PR renames congruence lemmas for union on
`DHashMap`/`HashMap`/`HashSet`/`DTreeMap`/`TreeMap`/`TreeSet` to fit the
convention of being in the `Equiv` namespace.
2025-11-21 11:25:00 +00:00
Markus Himmel
dda6885eae refactor: String.foldl and String.isNat go through String.Slice (#11289)
This PR redefines `String.foldl`, `String.isNat` to use their
`String.Slice` counterparts.
2025-11-21 11:17:50 +00:00
Joachim Breitner
cce4873c25 chore: rename wrongly named backwards. options to backward. (#11303)
This PR renames rename wrongly named `backwards.` options to
`backward.`
2025-11-21 10:57:56 +00:00
Joachim Breitner
dedf7a8f44 feat: allow setting reducibilityCoreExt in async contexts (#11301)
This PR allows setting reducibilityCoreExt in async contexts (e.g. when
using `mkSparseCasesOn` in a realizable definition)
2025-11-21 09:23:14 +00:00
Kim Morrison
01335863e6 chore: add #grint_lint exception for sizeOf_spec lemmas (#11300) 2025-11-21 09:02:19 +00:00
Kim Morrison
1aecd85e0c chore: update stage0 2025-11-21 19:35:21 +11:00
Kim Morrison
4f7c5f4dca feat: #grind_lint skip suffix
delete old grind_lint

.

move exception to separate file

note about stage0
2025-11-21 19:35:21 +11:00
Leonardo de Moura
5306a3469d fix: bug ite/dite propagator used in grind (#11295)
This PR fixes a bug in the propagation rules for `ite` and `dite` used
in `grind`. The bug prevented equalities from being propagated to the
satellite solvers. Here is an example affected by this issue.

```lean
example
    [LE α] [LT α] [Std.IsLinearOrder α] [Std.LawfulOrderLT α]
    [Lean.Grind.CommRing α] [DecidableLE α] [Lean.Grind.OrderedRing α]
    (a b c : α) :
  (if a - b ≤ -(a - b) then -(a - b) else a - b) ≤
  ((if a - c ≤ -(a - c) then -(a - c) else a - c) + if c - d ≤ -(c - d) then -(c - d) else c - d) +
    if b - d ≤ -(b - d) then -(b - d) else b - d := by
  grind
```
2025-11-20 23:54:28 +00:00
Marc Huisinga
2f1e258a5e test: re-enable re-elab benchmarks and add watchdog re-elab benchmark (#11284) 2025-11-20 22:53:08 +00:00
Sebastian Ullrich
e97c1505f0 fix: shake: register attribute rev use independent of initialize kind (#11293) 2025-11-20 20:39:27 +00:00
Robert J. Simmons
b6399e18c3 feat: allow decidable equality for empty lists and empty arrays (#11269)
This PR adds support for decidable equality of empty lists and empty
arrays. Decidable equality for lists and arrays is suitably modified so
that all diamonds are definitionally equal.

Following #9302, the strong condition of definitionally equal under
`with_reducible_and_instances` is tested. This also moves some of the
comments added in #9302 out of docstrings.

---------

Co-authored-by: Aaron Liu <aaronliu2008@outlook.com>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
2025-11-20 20:19:31 +00:00
Markus Himmel
51b67385cc refactor: better name for String.replaceStart and variants (#11290)
This PR renames `String.replaceStartEnd` to `String.slice`,
`String.replaceStart` to `String.sliceFrom`, and `String.replaceEnd` to
`String.sliceTo`, and similar for the corresponding functions on
`String.Slice`.
2025-11-20 16:42:27 +00:00
Wojciech Różowski
556e96088e feat: add lemmas relating getMin/getMin?/getMin!/getMinD and insertion to the empty (D)TreeMap/TreeSet (#11231)
This PR adds several lemmas that relate
`getMin`/`getMin?`/`getMin!`/`getMinD` and insertion to the empty
(D)TreeMap/TreeSet and their extensional variants.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-20 16:35:07 +00:00
Paul Reichert
649d0b4eb5 refactor: remove duplicated internal lemmas (#11260)
This PR removes some duplicated internal lemmas of the hash map and tree
map infrastructure.
2025-11-20 16:29:27 +00:00
Sebastian Ullrich
e5e7a89fdc fix: shake: only record used simp theorems as dependencies, plus simprocs (#11287) 2025-11-20 15:43:25 +00:00
Sebastian Ullrich
7ef229d03d chore: shake: re-add attribute rev use (#11288)
Global `attribute` commands on non-local declarations are impossible to
track granularly a priori and so should be preserved by `shake` by
default. A new `shake` option could be added to ignore these
dependencies for evaluation.
2025-11-20 15:39:38 +00:00
Markus Himmel
7267ed707a feat: string patterns for decidable predicates on Char (#11285)
This PR adds `Std.Slice.Pattern` instances for `p : Char -> Prop` as
long as `DecidablePred p`, to allow things like `"hello".dropWhile (· =
'h')`.

To achieve this, we refactor `ForwardPattern` and friends to be
"non-uniform", i.e., the class is now `ForwardPattern pat`, not
`ForwardPattern ρ` (where `pat : ρ`).
2025-11-20 15:30:37 +00:00
Wojciech Różowski
89d4e9bd4c feat: add intersection for ExtDHashMap/ExtHashMap/ExtHashSet (#11241)
This PR provides intersection operation for
`ExtDHashMap`/`ExtHashMap`/`ExtHashSet` and proves several lemmas about
it.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-20 15:24:28 +00:00
Wojciech Różowski
108a3d1b44 feat: add intersection on DTreeMap/TreeMap/TreeSet (#11165)
This PR provides intersection on `DTreeMap`/`TreeMap`/`TreeSet`and
provides several lemmas about it.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-20 15:08:30 +00:00
Markus Himmel
f7ed158002 chore: introduce and immediately deprecate String.Slice.length (#11286)
This PR adds a function `String.Slice.length`, with the following
deprecation string: There is no constant-time length function on slices.
Use `s.positions.count` instead, or `isEmpty` if you only need to know
whether the slice is empty.
2025-11-20 14:31:46 +00:00
Markus Himmel
cf0e4441e8 chore: create alias String.Slice.any for String.Slice.contains (#11282)
This PR adds the alias `String.Slice.any` for `String.Slice.contains`.

It would probably be even better to only have one, but we don't have a
good mechanism for pointing people looking for one towards the other, so
an alias it is for now.
2025-11-20 13:21:30 +00:00
Markus Himmel
2c12bc9fdf chore: more deprecations for string migration (#11281)
This PR adds a few deprecations for functions that never existed but
that are still helpful for people migrating their code post-#11180.
2025-11-20 13:09:52 +00:00
Paul Reichert
fc6e0454c7 feat: add more lemmas about Array and List slices, support subslices (#11178)
This PR provides more lemmas about `Subarray` and `ListSlice` and it
also adds support for subslices of these two types of slices.
2025-11-20 10:46:17 +00:00
Kim Morrison
a106ea053f test: split grind_lint.lean into 7 smaller files for faster CI (#11271)
This PR splits the single grind_lint.lean test (50+ seconds) into 7
separate files that each run in under 7 seconds:

- grind_lint_list.lean (5.7s): List namespace with exceptions
- grind_lint_array.lean (4.6s): Array namespace
- grind_lint_bitvec.lean (3.9s): BitVec namespace with exceptions
- grind_lint_std_hashmap.lean (6.8s): Std hash map/set namespaces
- grind_lint_std_treemap.lean (~6s): Std tree map/set namespaces
- grind_lint_std_misc.lean (~5s): Std.Do, Std.Range, Std.Tactic
- grind_lint_misc.lean (5.5s): All other non-Lean namespaces

Each file maintains complete namespace coverage and preserves all
existing exceptions. The split enables better CI parallelization and
faster feedback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-20 05:19:02 +00:00
Leonardo de Moura
00600806ad fix: proof construction in grind ring (#11273)
This PR fixes a bug during proof construction in `grind`.
2025-11-20 04:52:18 +00:00
Aaron Liu
5c8ebd8868 feat: make Option.decidableEqNone coherent with Option.instDecidableEq (#9302)
This PR modifies `Option.instDecidableEq` and `Option.decidableEqNone`
so that the latter can be made into a global instance without causing
diamonds. It also adds `Option.decidabeNoneEq`.

See
[Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Option.2EdecidableEqNone/near/527226250).

---------

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
Co-authored-by: Rob Simmons <rob@lean-fro.org>
2025-11-20 01:48:42 +00:00
Leonardo de Moura
47228b94fd feat: arbitrary grind parameters (#11268)
This PR implements support for arbitrary `grind` parameters. The feature
is similar to the one available in `simp`, where a proof term is treated
as a local universe-polymorphic lemma. This feature relies on `grind
-revert` (see #11248). For example, users can now write:

```lean
def snd (p : α × β) : β := p.2
theorem snd_eq (a : α) (b : β) : snd (a, b) = b := rfl

/--
trace: [grind.ematch.instance] snd_eq (a + 1): snd (a + 1, Type) = Type
[grind.ematch.instance] snd_eq (a + 1): snd (a + 1, true) = true
-/
#guard_msgs (trace) in
set_option trace.grind.ematch.instance true in
example (a : Nat) : (snd (a + 1, true), snd (a + 1, Type), snd (2, 2)) = (true, Type, snd (2, 2)) := by
  grind [snd_eq (a + 1)]
```

Note that in the example above, `snd_eq` is instantiated only twice, but
with different universe parameters.
As described in #11248, the new feature cannot be used with `grind
+revert`.
2025-11-19 21:01:01 +00:00
Lean stage0 autoupdater
126fca1ec8 chore: update stage0 2025-11-19 19:40:23 +00:00
Leonardo de Moura
2ed025ade8 feat: mark sizeOf theorems as grind theorems (#11265)
This PR marks the automatically generated `sizeOf` theorems as `grind`
theorems.

closes #11259

Note: Requested update stage0, we need it to be able to solve example in
the issue above.
```lean
example (a: Nat) (b: Nat): sizeOf a < sizeOf (a, b) := by
  grind
```
2025-11-19 18:38:35 +00:00
Henrik Böving
827a96ade3 fix: several memory leaks in the new String API (#11263)
This PR fixes several memory leaks in the new `String` API.

These leaks are mostly situations where we forgot to put borrowing
annotations. The single
exception is the new `String` constructor `ofByteArray`. It cannot take
the `ByteArray` as
a borrowed argument anymore and must thus free it on its own.
2025-11-19 18:23:35 +00:00
Sebastian Ullrich
e0f96208e4 chore: typo in error message (#11262) 2025-11-19 17:15:11 +00:00
Joachim Breitner
5cc0a10346 refactor: use Match.AltParamInfo also for splitters (#11261)
This PR continues the homogenization between matchers and splitters,
following up on #11256. In particular it removes the ambiguity whether
`numParams` includes the `discrEqns` or not.
2025-11-19 16:13:53 +00:00
Lean stage0 autoupdater
1b6fba49c2 chore: update stage0 2025-11-19 15:57:48 +00:00
Joachim Breitner
63bd0b5e77 refactor: introduce Match.altInfos (#11256)
This PR replaces `MatcherInfo.numAltParams` with a more detailed data
structure that allows us, in particular, to distinguish between an
alternative for a constructor with a `Unit` field and the alternative
for a nullary constructor, where an artificial `Unit` argument is
introduced.
2025-11-19 15:09:17 +00:00
Lean stage0 autoupdater
75342961fc chore: update stage0 2025-11-19 13:58:11 +00:00
Henrik Böving
52b687cab4 perf: less allocations when using string patterns (#11255)
This PR reduces the allocations when using string patterns. In
particular
`startsWith`, `dropPrefix?`, `endsWith`, `dropSuffix?` are optimized.
2025-11-19 13:06:27 +00:00
Joachim Breitner
75570f327f refactor: thunk field-less alternatives of casesOnSameCtor (#11254)
This RP adds a `Unit` argument to `casesOnSameCtor` to make it behave
moere similar to a matcher. Follow up in spirit to #11239.
2025-11-19 09:53:09 +00:00
Markus Himmel
52d05b6972 refactor: use String.split instead of String.splitOn or String.splitToList (#11250)
This PR introduces a function `String.split` which is based on
`String.Slice.split` and therefore supports all pattern types and
returns a `Std.Iter String.Slice`.

This supersedes the functions `String.splitOn` and `String.splitToList`,
and we remove all all uses of these functions from core. They will be
deprecated in a future PR.

Migrating from `String.splitOn` and `String.splitToList` is easy: we
introduce functions `Iter.toStringList` and `Iter.toStringArray` that
can be used to conveniently go from `Std.Iter String.Slice` to `List
String` and `Array String`, so for example `s.splitOn "foo"` can be
replaced by `s.split "foo" |>.toStringList`.
2025-11-19 09:35:19 +00:00
Joachim Breitner
f7031c7aa9 perf: in match splitters, thunk alts if needed (#11239)
This PR adds a `Unit` assumption to alternatives of the splitter that
would otherwise not have arguments. This fixes #11211.

In practice these argument-less alternatives did not cause wrong
behavior, as the motive when used with `split` is always a function
type. But it is better to be safe here (maybe someone uses splitters in
other ways), it may increase the effectiveness of #10184 and simplifies
#11220.

The perf impact is insignificant in the grand scheme of things on
stdlib, but the change is effective:
```
~/lean4 $ build/release/stage1/bin/lean tests/lean/run/matchSplitStats.lean 
969 splitters found
455 splitters are const defs
~/lean4 $ build/release/stage2/bin/lean tests/lean/run/matchSplitStats.lean 
969 splitters found
829 splitters are const defs
```
2025-11-19 09:08:34 +00:00
Lean stage0 autoupdater
9fc90488ce chore: update stage0 2025-11-19 08:40:32 +00:00
Markus Himmel
59949f89ee chore: add function String.Pos.extract (#11251)
This PR is a preparatory bootstrapping PR for #11240.
2025-11-19 08:05:28 +00:00
Leonardo de Moura
61186629d6 feat: grind -revert (#11248)
This PR implements the option `revert`, which is set to `false` by
default. To recover the old `grind` behavior, you should use `grind
+revert`. Previously, `grind` used the `RevSimpIntro` idiom, i.e., it
would revert all hypotheses and then re-introduce them while simplifying
and applying eager `cases`. This idiom created several problems:

* Users reported that `grind` would include unnecessary parameters. See
[here](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Grind.20aggressively.20includes.20local.20hypotheses.2E/near/554887715).
* Unnecessary section variables were also being introduced. See the new
test contributed by Sebastian Graf.
* Finally, it prevented us from supporting arbitrary parameters as we do
in `simp`. In `simp`, I implemented a mechanism that simulates local
universe-polymorphic theorems, but this approach could not be used in
`grind` because there is no mechanism for reverting (and re-introducing)
local universe-polymorphic theorems. Adding such a mechanism would
require substantial work: I would need to modify the local context
object. I considered maintaining a substitution from the original
variables to the new ones, but this is also tricky, because the mapping
would have to be stored in the `grind` goal objects, and it is not just
a simple mapping. After reverting everything, I would need to keep a
sequence of original variables that must be added to the mapping as we
re-introduce them, but eager case splits complicate this quite a bit.
The whole approach felt overly messy.

The new behavior `grind -revert` addresses all these issues. None of the
`grind` proofs in our test suite broke after we fixed the bugs exposed
by the new feature. That said, the traces and counterexamples produced
by `grind` are different. The new proof terms are also different.
2025-11-19 05:28:31 +00:00
Robert J. Simmons
d5ecca995f chore: update some error explanations (#11225)
This PR updates some of the Error Explanations that had gotten out of
sync with actual error messages
2025-11-19 03:16:40 +00:00
Robert J. Simmons
f81e64936a feat: improve error when an identifier is unbound because autoImplicit is off (#11119)
This PR introduces a clarifying note to "undefined identifier" error
messages when the undefined identifier is in a syntactic position where
autobinding might generally apply, but where and autobinding is
disabled. A corresponding note is made in the `lean.unknownIdentifier`
error explanation.

The core intended audience for this error message change is "newcomer
who would otherwise be baffled why the thing that works in this Mathlib
project gets 'unknown identifier' errors in this non-Mathlib project."

## Modified behavior

### Example 1
```lean4
set_option autoImplicit true in
set_option relaxedAutoImplicit false in
def thisBreaks (x : α₂) (y : size₂) := ()
```

Before:
```
Unknown identifier `size₂`
```

After:
```
Unknown identifier `size₂`

Note: It is not possible to treat `size₂` as an implicitly bound variable here because it has multiple characters while the `relaxedAutoImplicit` option is set to `false`.
```

### Example 2
```lean4
set_option autoImplicit false in
def thisAlsoBreaks (x : α₃) (y : size₃) := ()
```

Before:
```
Unknown identifier `α₃`
Unknown identifier `size₃`
```

After:
```
Unknown identifier `α₃`

Note: It is not possible to treat `α₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
Unknown identifier `size₃`

Note: It is not possible to treat `size₃` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.
```

## How this works

The elaboration process knows whether it is considering syntax where we
be able to auto-bind implicits thanks to information in the
`Lean.Elab.Term.Context`.

Before this PR, this contains:
* `autoBoundImplicit`, a boolean that is true when we are considering
syntax that might be able to auto-bind implicit AND when the
`autoImplicit` flag is set to true
* `autoBoundImplicits`, an array of `Expr` variables that we've
autobound

After this PR, this contains:
* `autoBoundImplicitCtx`, an option which is `some` **whenever** we are
considering syntax that might be able to auto-bind implicit, and carries
the array of exprs as well as a copy of the `autoImplicit` flag's value.
(The latter lets us re-implement the `autoBoundImplicit` flag for
backward compatibility.)

Therefore, rather than having access to "elaboration is in an
autobinding context && flag is enabled", it's possible to recover both
of those individual values, and give different information to the user
in cases where we didn't attempt autobinding but would have if different
options had been set.

## Rationale

The revised error message avoids offering much guidance — it doesn't
actively suggest setting the option to a different value or suggest
adding an implicit binding. Care needs to be taken here to make sure
advice is not misleading; as the accepted RFC in #6462 points out, a
substantial portion of autobinding failures are just going to be
misspellings.

I considered and then rejected a code action here to that would add a
local `set_option autoImplicit true`. This seems undesirable or
counterproductive — if a project like Mathlib has proactively disabled
`autoImplicit`, its odd to be pushing local exceptions.

A hint prompting the user to add an implicit binding would be more
proper, but only in certain circumstances — we want to be conservative
in suggesting specific code actions! In a situation like this one, we'd
want to _avoid_ giving the suggestion of adding a `{HasArr}` binding,
which I think either requires tricky heuristics or means we'd want the
elaboration to play through the consequences of auto-binding and make
sure it doesn't cause any follow-on errors before suggesting adding an
implicit binding.

```
set_option autoImplicit true
set_option relaxedAutoImplicit false
instance has_arr : HasArr Preorder := { Arr := Function }
```

Additionally, it seems like it would make the most sense to offer to
auto-bind _all_ the relevant unknown identifiers at once. To avoid being
misleading, this too would seem to require playing through the
consequences of autobinding before being able to safely suggest the
change. This is enough additional complexity that I'm leaving it for
future work.

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
2025-11-19 03:11:34 +00:00
Mac Malone
5bb9839887 fix: symbol clashes between packages (#11082)
This PR prevents symbol clashes between (non-`@[export]`) definitions
from different Lean packages.

Previously, if two modules define a function with the same name and were
transitively imported (even privately) by some downstream module,
linking would fail due to a symbol clash. Similarly, if a user defined a
symbol with the same name as one in the `Lean` library, Lean would use
the core symbol even if one did not import `Lean`.

This is solved by changing Lean's name mangling algorithm to include an
optional package identifier. This identifier is provided by Lake via
`--setup` when building a module. This information is weaved through the
elaborator, interpreter, and compiler via a persistent environment
extension that associates modules with their package identifier.

With a package identifier, standard symbols have the form
`lp_<pkg-id>_<mangled-def>`. Without one, the old scheme is used (i.e.,
`l_<mangled-def>`). Module initializers are also prefixed with package
identifier (if any). For example, the initializer for a module `Foo` in
a package `test` is now `initialize_test_Foo` (instead of
`initialize_Foo`). Lake's default for native library names has also been
adjusted accordingly, so that libraries can still, by default, be used
as plugins. Thus, the default library name of the `lean_lib Foo` in
`package test` will now be `libtest_Foo`.

When using Lake to build the Lean core (i.e., `bootstrap = true`), no
package identifier will be used. Thus, definitions in user packages can
never have symbol clashes with core.

Closes #222.
2025-11-19 02:24:44 +00:00
Mac Malone
687698e79d test: module clash across packages (#11246)
This PR adds a test that covers importing modules defined in multiple
packages.

Currently, will resolve the module to its first occurrence in the its
search order. However, this will soon change, so this test is designed
to analyze that behavior.
2025-11-19 02:23:34 +00:00
Leonardo de Moura
8a0ee9aac7 fix: assigned universe metavars in grind (#11247)
This PR fixes an issue in the `grind` preprocessor. `simp` may introduce
assigned (universe) metavariables (e.g., when performing
zeta-reduction).
2025-11-19 00:19:17 +00:00
Leonardo de Moura
6dd8ad13e5 fix: grind minor issues (#11244)
This PR fixes minor issues in `grind`. In preparation for adding `grind
-revert`.
2025-11-18 22:11:20 +00:00
Markus Himmel
fa5d08b7de refactor: use String.Slice in String.take and variants (#11180)
This PR redefines `String.take` and variants to operate on
`String.Slice`. While previously functions returning a substring of the
input sometimes returned `String` and sometimes returned
`Substring.Raw`, they now uniformly return `String.Slice`.

This is a BREAKING change, because many functions now have a different
return type. So for example, if `s` is a string and `f` is a function
accepting a string, `f (s.drop 1)` will no longer compile because
`s.drop 1` is a `String.Slice`. To fix this, insert a call to `copy` to
restore the old behavior: `f (s.drop 1).copy`.

Of course, in many cases, there will be more efficient options. For
example, don't write `f <| s.drop 1 |>.copy |>.dropEnd 1 |>.copy`, write
`f <| s.drop 1 |>.dropEnd 1 |>.copy` instead. Also, instead of `(s.drop
1).copy = "Hello"`, write `s.drop 1 == "Hello".toSlice` instead.
2025-11-18 16:13:48 +00:00
Markus Himmel
03eb2f73ac chore: deprecate String.toSubstring (#11232)
This PR deprecates `String.toSubstring` in favor of
`String.toRawSubstring` (cf. #11154).
2025-11-18 13:50:50 +00:00
Wrenna Robson
36a6844625 feat: add Std.Trichotomous (#10945)
This PR adds `Std.Tricho r`, a typeclass for relations which identifies
them as trichotomous. This is preferred to `Std.Antisymm (¬ r · ·)` in
all cases (which it is equivalent to).
2025-11-18 13:20:53 +00:00
Lean stage0 autoupdater
4296f8deee chore: update stage0 2025-11-18 11:23:27 +00:00
Markus Himmel
e301f86c6c chore: add String.Pos.next (#11238)
This PR is split from a future PR and adds the function
`String.Pos.next`, an alias (and soon to be correct name) of
`String.ValidPos.next`.

This is for boring bootstrapping reasons.
2025-11-18 10:41:22 +00:00
Jovan Gerbscheid
4c972ba0d6 fix: add missing s! in UInt64.fromJson? (#11237)
This PR fixes the error thrown by `UInt64.fromJson?` and
`USize.fromJson?` to use the missing `s!`.
2025-11-18 10:31:31 +00:00
Joachim Breitner
f6e580ccf8 refactor: extract functionality from Match.MatchEqs (#11236)
This PR extracts two modules from `Match.MatchEqs`, in preparation of
#11220
and to use the module system to draw clear boundaries between concerns
here.
2025-11-18 10:02:10 +00:00
Sebastian Graf
51ed5f247c fix: register node kind for elabToSyntax functionality (#11235)
This PR registers a node kind for `Lean.Parser.Term.elabToSyntax` in
order to support the `Lean.Elab.Term.elabToSyntax` functionality without
registering a dedicated parser for user-accessible syntax.
2025-11-18 09:47:08 +00:00
Henrik Böving
1759b83929 test: regression test for #6332 (#11234)
Closes: #6332
2025-11-18 09:47:04 +00:00
Wojciech Różowski
e35d65174c feat: add intersection on DHashMap (#11112)
This PR adds intersection operation on `DHashMap`/`HashMap`/`HashSet`
and provides several lemmas about its behaviour.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-18 09:40:44 +00:00
Paul Reichert
1a4c3ca35d refactor: small iterator improvements (#11175)
This PR removes duplicated instance parameters in the standard library
and flips lemmas of the form `toList_eq_toListIter` into a form that is
suitable for `simp`.
2025-11-18 09:28:55 +00:00
Lean stage0 autoupdater
1f807969b7 chore: update stage0 2025-11-18 09:08:13 +00:00
Wojciech Różowski
f46c17fa1d feat: add lemmas for DHashMap/HashMap/HashSet about emptyWithCapacity/empty (#11223)
This PR adds missing lemmas relating `emptyWithCapacity`/`empty` and
`toList`/`keys`/`values` for `DHashMap`/`HashMap`/`HashSet`.
2025-11-18 08:17:16 +00:00
Kim Morrison
155db16572 chore: begin dev cycle for v4.27.0 (#11229)
Set LEAN_VERSION_MINOR to 27.
2025-11-18 08:12:49 +00:00
Markus Himmel
f6a9059709 chore: rename String.offsetOfPos to String.Pos.Raw.offsetOfPos (#11218)
This PR renames `String.offsetOfPos` to `String.Pos.Raw.offsetOfPos` to
align with the other `String.Pos.Raw` operations.
2025-11-18 07:24:06 +00:00
Sebastian Graf
59d2d00132 feat: turn a term elaborator into a syntax object with elabToSyntax (#11222)
This PR implements `elabToSyntax` for creating scoped syntax `s :
Syntax` for an arbitrary elaborator `el : Option Expr -> TermElabM Expr`
such that `elabTerm s = el`.

Roundtripping example implementing an elaborator imitating `let`:

```lean
elab "lett " decl:letDecl ";" e:term : term <= ty? => do
  let elabE (ty? : Option Expr) : TermElabM Expr := do elabTerm e ty?
  elabToSyntax elabE fun body => do
    elabTerm (← `(let $decl:letDecl; $body)) ty?

#guard lett x := 42; (x + 1) = 43
```
2025-11-18 07:10:31 +00:00
Leonardo de Moura
5a4226f2bd refactor: remove old grindSearchM framework (#11226)
This PR finally removes the old `grind` framework `SearchM`. It has been
replaced with the new `Action` framework.
2025-11-18 00:33:38 +00:00
Mac Malone
81d716069c fix: lake: improper uses of computeArtifact w/o text (#11216)
This PR ensures that the `text` argument of `computeArtifact` is always
provided in Lake code, fixing a hashing bug with
`buildArtifactUnlessUpToDate` in the process.

Closes #11209
2025-11-17 22:27:19 +00:00
Henrik Böving
033fa8c585 test: add additional regression test for #11131 from #10925 (#11224)
Closes #10925
2025-11-17 21:23:53 +00:00
Joachim Breitner
09001ecad6 fix: let realizeConst run withDeclNameForAuxNaming (#11221)
This PR lets `realizeConst` use `withDeclNameForAuxNaming` so that
auxilary definitions created there get non-clashing names.
2025-11-17 21:17:16 +00:00
Lean stage0 autoupdater
1c82929c34 chore: update stage0 2025-11-17 19:02:56 +00:00
Joachim Breitner
b67e8a15d0 perf: avoid quadratic calculation of notAlts in match splitter (#11196)
This PR avoids match splitter calculation from testing all quadratically
many pairs of alternatives for overlaps, by keeping track of possible
overlaps during matcher calculation, storing that information in the
`MatcherInfo`, and using that during matcher calculation.
2025-11-17 18:10:13 +00:00
Lean stage0 autoupdater
be6457284a chore: update stage0 2025-11-17 17:15:47 +00:00
Henrik Böving
07e6b99e2e fix: deallocation for closures in non default configurations (#11217)
This PR fixes fallout of the closure allocator changes in #10982. As far
as we know
this bug only meaningfully manifests in non default build configurations
without mimalloc such as:
`cmake --preset release -DUSE_MIMALLOC=OFF`

The issue is that I forgot to update the deallocation functions for
closures. However, this only
seems to matter if we disable mimalloc which is why this slipped through
testing.
2025-11-17 16:27:20 +00:00
Paul Reichert
8eb0293098 feat: add MPL specs for slice for ... in (#11141)
This PR provides a polymorphic `ForIn` instance for slices and an MPL
`spec` lemma for the iteration over slices using `for ... in`. It also
provides a version specialized to `Subarray`.
2025-11-17 15:58:29 +00:00
Markus Himmel
8671f81aa5 fix: lakefile require syntax in package not found on Reservoir error (#11198)
This PR fixes an error message in Lake which suggested incorrect
lakefile syntax.

The error message (which was very helpful by the way) looked like this:
```
error: TwoFX/batteries: package not found on Reservoir.

  If the package is on GitHub, you can add a Git source. For example:

    require ...
      from git "https://github.com/TwoFX/batteries" @ git "main"

  or, if using TOML:

    [[require]]
    git = "https://github.com/TwoFX/batteries"
    rev = "main"
    ...
```

The suggested Lakefile syntax does not work. The correct syntax,
according to the reference manual and according to my tests, is
```
    require ...
      from git "https://github.com/TwoFX/batteries" @ "main"
```
without the second `git`.
2025-11-17 15:12:23 +00:00
David Thrane Christiansen
5ce1f67261 fix: module docstring header nesting in Verso format (#11215)
This PR fixes an issue where header nesting levels were properly tracked
between, but not within, moduledocs.
2025-11-17 13:57:00 +00:00
Henrik Böving
bef8574b93 fix: be more careful when recording cases in the compiler (#11210)
This PR fixes a bug in the LCNF simplifier unearthed while working on
#11078. In some situations caused by `unsafeCast`, the simplifier would
record incorrect information about `cases`, leading to further bugs down
the line.

Suppose we have `v : NonScalar` due to an `unsafeCast` and we run
`cases` on it, expecting `Prod.mk fst snd`. The current code attempts to
record both the arguments from the constructor application in the case
arm `fst`, `snd` and the parameters for the type by inspecting the discr
`v`. However, `NonScalar` does of course not have any parameters,
causing the simplifier to record wrong information. This patch makes the
`cases` infrastructure more cautious when extracting information from
the type of `v`.
2025-11-17 11:34:16 +00:00
Joachim Breitner
27e5e21bfe perf: use Nat-based bitmask in sparse cases construction (#11200)
This PR changes how sparse case expressions represent the
none-of-the-above information. Instead of of many `x.ctorIdx ≠ i`
hypotheses, it introduces a single `Nat.hasNotBit mask x.ctorIdx`
hypothesis which compresses that information into a bitmask. This avoids
a quadratic overhead during splitter generation, where all n assumptions
would be refined through `.subst` and `.cases` constructions for all n
assumption of the splitter alternative.

The definition of `Nat.hasNotBit` uses `Nat.rightShift` which is fiddly
to get to reduce well, especially on open terms and with `Meta.whnf`.
Some experimentation was needed to find proof terms that work, these are
all put together in the `Lean.Meta.HasNotBit` module.

Fixes #11183

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-11-17 10:05:18 +00:00
Rob23oba
eba5a5a6ef fix: consider over-applications in reduceArity compiler pass (#11185)
This PR fixes the `reduceArity` compiler pass to consider
over-applications to functions that have their arity reduced.
Previously, this pass assumed that the amount of arguments to
applications was always the same as the number of parameters in the
signature. This is usually true, since the compiler eagerly introduces
parameters as long as the return type is a function type, resulting in a
function with a return type that isn't a function type. However, for
dependent types that sometimes are function types and sometimes not,
this assumption is broken, resulting in the additional parameters to be
dropped.

Closes #11131
2025-11-17 07:51:37 +00:00
Kim Morrison
bba399eefe chore: finish dealing with #grind_lint (#11207)
This ensures that no `grind` annotated theorem, simply by being
instantiated, causes a chain of >20 further instantiations, with a small
list of documented exceptions.
2025-11-17 06:58:28 +00:00
Kim Morrison
8b575dcbf2 chore: fixing grind annotations using #grind_lint (#11206)
Slightly more extensive version of #11205, for which I want separate CI.
2025-11-17 05:30:01 +00:00
Kim Morrison
d6f3ca24d3 chore: fixing grind annotations using #grind_lint (#11205) 2025-11-17 04:53:21 +00:00
Kim Morrison
8c7604f550 feat: try? runs tactics with separate heartbeats budgets (#11174)
This PR modifies the `try?` framework, so each subsidiary tactic runs
with a separate `maxHeartbeats` budget.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-11-17 01:30:43 +00:00
Kim Morrison
4b28713a44 feat: #grind_lint check produces a "Try this:" suggestion with #grind_list inspect commands (#11204)
This PR has `#grind_list check` produce a "Try this:" suggestion with
`#grind_list inspect` commands, as this is usually the next step in
dealing with problematic cases. We also fix the grind pattern for one
theorem, as part of testing the workflow. More to follow.
2025-11-17 00:52:57 +00:00
Leonardo de Moura
4c189bc8f2 fix: grind actions (#11203)
This PR fixes a few minor issues in the new `Action` framework used in
`grind`. The goal is to eventually delete the old `SearchM`
infrastructure. The main `solve` function used by `grind` is now based
on the `Action` framework. The PR also deletes dead code in `SearchM`.
2025-11-17 00:37:19 +00:00
Sebastian Ullrich
0b93b3f182 chore: record uses of user-defined attributes as shake dependencies (#11202) 2025-11-16 20:34:23 +00:00
Sebastian Ullrich
ed34ee0cd5 chore: make declMetaExt persistent for shake (#11201) 2025-11-16 20:11:56 +00:00
Joachim Breitner
8ef742647e test: benchmark for large partial match (#11199)
Creates an inductive data type with 100 constructors, and a function
that does
matches on half of its constructors, with a catch-all for the other
half, and generates the splitter.

Related to #11183.
2025-11-16 11:20:31 +00:00
Lean stage0 autoupdater
65a41c38a0 chore: update stage0 2025-11-16 10:13:26 +00:00
Markus Himmel
bf60550ce5 chore: rename Substring to Substring.Raw (#11154)
This PR renames `Substring`  to `Substring.Raw`.

This is to signify its status as a second-class citizen (not deprecated,
but no real plans for verification, like `String.Pos.Raw`) and to free
up the name `Substring` for a possible future type `String.Substring :
String -> Type` so that `s.Substring` is the type of substrings of `s`.

The functions `String.toSubstring` and `String.toSubstring'` will remain
for now for bootstrapping reasons.
2025-11-16 09:30:04 +00:00
Leonardo de Moura
ef1dc21f1c feat: use new grind? infrastructure to implement try? (#11197)
This PR implements `try?` using the new `finish?` infrastructure. It
also removes the old tracing infrastructure, which is now obsolete.
Example:

```lean
/--
info: Try these:
  [apply] grind
  [apply] grind only [findIdx, insert, = mem_indices_of_mem, = getElem?_neg, = getElem?_pos, = HashMap.mem_insert,
    = HashMap.getElem_insert, #1bba]
  [apply] grind only [findIdx, insert, = mem_indices_of_mem, = getElem?_neg, = getElem?_pos, = HashMap.mem_insert,
    = HashMap.getElem_insert]
  [apply] grind =>
    instantiate only [findIdx, insert, = mem_indices_of_mem]
    instantiate only [= getElem?_neg, = getElem?_pos]
    cases #1bba
    · instantiate only [findIdx]
    · instantiate only
      instantiate only [= HashMap.mem_insert, = HashMap.getElem_insert]
-/
#guard_msgs in
example (m : IndexMap α β) (a : α) (b : β) :
    (m.insert a b).findIdx a = if h : a ∈ m then m.findIdx a else m.size := by
  try?
```
2025-11-16 05:26:17 +00:00
Robert J. Simmons
31f09da88a feat: prioritize stuck synthetic MVar problems to improve error messages (#11184)
This PR modifies the error message that is returned when more than one
synthetic metavariable can't be resolved.

The two heuristics used for prioritization are:
- prefer typeclass problems associated with small ranges over typeclass
problems associated with large ranges (I'm pretty confident in this
heuristic)
- do not prefer typeclass problems over other kinds of errors (not as
confident in this heuristic)
2025-11-16 00:09:48 +00:00
Leonardo de Moura
2f3939f1ea fix: incorrect grind param warning (#11194)
This PR the redundant `grind` parameter warning message. It now checks
the `grind` theorem instantiation constraints too.
2025-11-15 20:17:55 +00:00
Leonardo de Moura
f4cd97ce04 feat: add grind_pattern constraint annotations (#11193)
This PR uses the new `grind_pattern` constraints to fix cases where an
unbounded number of theorem instantiations would be generated for
certain theorems in the standard library.
2025-11-15 19:08:03 +00:00
Joachim Breitner
e39894e62d feat: realizeConst to set CoreM's maxHeartbeat (#11191)
This PR makes sure that inside a `realizeConst` the `maxHeartbeat`
option is effective.
2025-11-15 17:36:09 +00:00
Johannes Tantow
100006fdd0 feat: verify all and any for hash maps (#10765)
This PR extends the `all`/`any` functions from hash sets to hash maps
and dependent hash maps and verifies them.
2025-11-15 16:59:37 +00:00
Joachim Breitner
a6f4e9156e fix: avoid unknown free variables in match error message (#11190)
This PR avoids running into an “unknown free variable” when printing the
“Failed to compile pattern matching” error. Fixes #11186.
2025-11-15 16:31:24 +00:00
Lean stage0 autoupdater
14625ec114 chore: update stage0 2025-11-15 05:46:38 +00:00
Leonardo de Moura
6f2c04b6a2 feat: grind_pattern constraints (#11189)
This PR implements `grind_pattern` constraints. They are useful for
controlling theorem instantiation in `grind`. As an example, consider
the following two theorems:
```lean
theorem extract_empty {start stop : Nat} :
    (#[] : Array α).extract start stop = #[] := …

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) := …
```

If both are used for theorem instantiation, an unbounded number of
instances is generated as soon as we add the term `#[].extract i j` to
the `grind` context.

We can now prevent this by adding a `grind_pattern` constraint to
`extract_extract`:

```lean
grind_pattern extract_extract => (as.extract i j).extract k l where
  as =/= #[]
```

With this constraint, only one instance is generated, as expected:

```lean
/-- trace: [grind.ematch.instance] extract_empty: #[].extract i j = #[] -/
#guard_msgs (drop error, trace) in
set_option trace.grind.ematch.instance true in
example (as : Array Nat) (h : #[].extract i j = as) : False := by
  grind only [= extract_empty, usr extract_extract]
```
2025-11-15 05:05:04 +00:00
Mac Malone
06f457b48a fix: lake: indeterminism in targets test (#11188)
This PR fixes a source of indeterminism in the `examples/targets` Lake
test (checking the job index).
2025-11-15 04:20:24 +00:00
Mac Malone
8ad0a61169 refactor: lake: scope all module build keys by package (#11169)
This PR changes all module build keys in Lake to be scoped by their
package. This enables building modules with the same name in different
packages (something previously only well-supported for executable
roots).

API-wise, the `BuildKey` definitions `module` and `moduleFacet` have
been deprecated and replaced with `packageModule` and
`packageModuleFacet`. The `moduleTargetIndicator` has also been removed
(with its purpose subsumed by `packageModule`).
2025-11-15 04:13:00 +00:00
Leonardo de Moura
d963d33985 feat: add grind_pattern constraints (#11187)
This PR adds syntax for specifying `grind_pattern` constraints and
extends the `EMatchTheorem` object.

--- 
Note: We need a manual stage0 update because it affects the .olean
files.
2025-11-14 18:27:17 -08:00
Robert J. Simmons
3f4e85413e doc: improved error messages when typeclass errors are stuck (#11179)
This PR removes most cases where an error message explained that it was
"probably due to metavariables," giving more explanation and a hint.

## Example

```
def square x := x * x
```

Before:

```lean4
typeclass instance problem is stuck, it is often due to metavariables
  HMul ?m.9 ?m.9 (?m.3 x)
```

After:
```
typeclass instance problem is stuck
  HMul ?m.9 ?m.9 (?m.3 x)

Note: Lean will not try to resolve this typeclass instance problem because the 
first and second type arguments to `HMul` are metavariables. These arguments 
must be fully determined before Lean will try to resolve the typeclass.

Hint: Adding type annotations and supplying implicit arguments to functions 
can give Lean more information for typeclass resolution. For example, if you 
have a variable `x` that you intend to be a `Nat`, but Lean reports it as 
having an unresolved type like `?m`, replacing `x` with `(x : Nat)` can get 
typeclass resolution un-stuck.
```

In addition to providing beginner-and-intermediate-friendly explanation
about **why** typeclass instance problems are treated as "stuck" when
metavariables appear in output positions, this PR provides
potentially-valuable improvement even to expert users: it explains
**which of the typeclass arguments are inputs** and therefore need to be
fully specified before typeclass resolution will be attempted. This
information can be tricky to find otherwise.

## Next steps, but probably after this PR

* error explanation
* detecting when the syntactic source is a binop and giving a
special-cased explanation on the binary operators and their associated
typeclasses
* detecting when the syntactic source is a function call, inspecting the
function call's type somewhat, and replacing the generic "replace `x`
with `(x : Nat)` hint with a specialized "replace `foo` with `foo (tyArg
:= Nat)`" hint
2025-11-14 21:25:46 +00:00
Alexander Bentkamp
bc2aae380c feat: add lemmas about Int range sizes (#11159)
This PR adds lemmas about the sizes of ranges of Ints, analogous to the
Nat lemmas in `Init.Data.Range.Polymorphic.NatLemmas`. See also
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Reasonning.20about.20PRange.20sizes.20.28with.20.60Int.60.29/with/546466339.

Closes #11158

---------

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-11-14 13:35:47 +00:00
Paul Reichert
b5b34ee054 feat: List slices (#11019)
This PR introduces slices of lists that are available via slice notation
(e.g., `xs[1...5]`).

* Moved the `take` combinator and the `List` iterator producer to
`Init`.
* Introduced a `toTake` combinator: `it.toTake` behaves like `it`, but
it has the same type as `it.take n`. There is a constant cost per
iteration compared to `it` itself.
* Introduced `List` slices. Their iterators are defined as
`suffixList.iter.take n` for upper-bounded slices and
`suffixList.iter.toTake` for unbounded ones.

Performance characteristics of using the slice `list[a...b]`:

* when creating it: `O(a)`
* every iterator step: `O(1)`
* `toList`: `O(b - a + 1)` (given that a <= b)

Because the slice only stores a suffix of `xs` internally, two slices
can be equal even though the underlying lists differ in an irrelevant
prefix. Because the `stop` field is allowed to be beyond the list's
upper bound, the slices `[1][0...1]` and `[1][0...2]` are not equal,
even though they effectively cover the same range of the same list.
Improving this would require us to call `List.length` when building the
slice, which would iterate through the whole list.
2025-11-14 11:33:25 +00:00
Sebastian Ullrich
5011b7bd89 chore: make compilation type mismatch error message from non-exposed defs a lot less mysterious (#11177) 2025-11-14 10:50:43 +00:00
Sebastian Ullrich
4602586b6a chore: suggest public meta import on phase check failure, which is more likely to be the correct variant (#11173) 2025-11-14 10:10:04 +00:00
Wojciech Różowski
36ee331ce2 feat: add minimal support for getEntry/getEntry?/getEntry!/getEntryD for DTreeMap (#11161)
This PR adds getEntry/getEntry?/getEntry!/getEntryD operation on
DTreeMap.
2025-11-14 09:09:53 +00:00
Markus Himmel
aca297d1c5 chore: some String API cleanup in Lake.Util.Version (#11160)
This PR performs some cleanup in `Lake.Util.Version`.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
2025-11-14 08:56:56 +00:00
Kim Morrison
de073706c5 feat: redefine Int.pow, for faster kernel reduction (#11139)
This PR replaces #11138, which just added a `@[csimp]` lemma for
`Int.pow`, this time actually replacing the definition. This means we
not only get fast runtime behaviour, but take advantage of the special
kernel support for `Nat.pow`.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-11-14 05:45:19 +00:00
Kim Morrison
f7ead9667b feat: macro for try? (#11170)
This PR adds tactic and term mode macros for `∎` (typed `\qed`) which
expand to `try?`. The term mode version captures any produced
suggestions and prepends `by`.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 05:27:23 +00:00
Kim Morrison
ffbd744c85 chore: remove simp_all? +suggestions from try? for now (#11172)
This PR removes `simp_all? +suggestions` from `try?` for now. It's
really slow out in Mathlib; too often the suggestions cause `simp` to
loop. Until we have the ability for `try?` to move past a timeing-out
tactic (or maybe even until we have parallelism), it needs to be
removed.

Alternatively, we could try modifying `simp` so that e.g. it won't use a
premise more than once. This might help avoid loops, but it would
produce less-reproducible proofs.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 04:58:23 +00:00
Kim Morrison
833aaa823e chore: tactics using library suggestions set the caller field (#11171)
This PR ensures that tactics using library suggestions set the caller
field, so the premise selection engine has access to this. We'll later
use this to filter out some modules for grind, which we know have
already been fully annotated.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 04:50:55 +00:00
François G. Dorais
7b29d976ed feat: add instances NeZero(n^0) for n : Nat and n : Int (#10739)
This PR adds two missing `NeZero` instances for `n^0` where `n : Nat`
and `n : Int`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add NeZero instances for n^0 when n : Nat and n : Int.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8305e65ba5. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-11-14 03:37:17 +00:00
Leonardo de Moura
1e84b6dff9 feat: add #grind_lint check in module <module> (#11167)
This PR implements support for `#grind_lint check in module <module>`.
Mathlib does not use namespaces, so we need to restrict the
`#grind_lint` search space using module (prefix) names. Example:

```lean
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: Array.filterMap_some
[thm] instances
  [thm] Array.filterMap_filterMap ↦ 94
  [thm] Array.size_filterMap_le ↦ 5
  [thm] Array.filterMap_some ↦ 1
---
info: instantiating `Array.range_succ` triggers 22 additional `grind` theorem instantiations
-/
#guard_msgs in
#grind_lint check (min := 20) in module Init.Data.Array
```
2025-11-14 01:44:04 +00:00
Kim Morrison
bc9cc05082 feat: include current file in default premise selector (#11168)
This PR changes the default library suggestions (e.g. for `grind
+suggestions` or `simp_all? +suggestions) to include the theorems from
the current file in addition to the output of Sine Qua Non.
2025-11-14 01:31:30 +00:00
Leonardo de Moura
46ff76aabd feat: #grind_lint refinements (#11166)
This PR implements the following improvements to the `#grind_lint`
command:
1. More informative messages when the number of instances exceeds the
minimum threshold.
2. A code action for `#grind_lint inspect` that inserts
`set_option trace.grind.ematch.instance true` whenever the number of
instances exceeds
   the minimum threshold.
3. Displaying doc strings for `grind` configuration options in
`#grind_lint`.
4. Improve doc strings for `#grind_lint inspect` and `#grind_lint
check`.

Example:
```lean
/--
info: instantiating `Array.filterMap_some` triggers more than 100 additional `grind` theorem instantiations
---
info: Array.filterMap_some
[thm] instances
  [thm] Array.filterMap_filterMap ↦ 94
  [thm] Array.size_filterMap_le ↦ 5
  [thm] Array.filterMap_some ↦ 1
---
info: Try this to display the actual theorem instances:
  [apply] set_option trace.grind.ematch.instance true in
  #grind_lint inspect Array.filterMap_some
-/
#guard_msgs in
#grind_lint inspect Array.filterMap_some
```
2025-11-13 20:36:01 +00:00
Markus Himmel
eb01aaeee4 chore: rename String.Iterator to String.Legacy.Iterator (#11152)
This PR renames `String.Iterator` to `String.Legacy.Iterator`.

From the docstring of `String.Legacy.Iterator`:

> This is a no-longer-supported legacy API that will be removed in a
future release. You should use
> `String.ValidPos` instead, which is similar, but safer. To iterate
over a string `s`, start with
> `p : s.startValidPos`, advance it using `p.next`, access the current
character using `p.get` and
> check if the position is at the end using `p = s.endValidPos` or
`p.IsAtEnd`.
2025-11-13 13:46:22 +00:00
Mac Malone
2b85e29cc9 test: version clash w/ diamond deps (#11155)
This PR adds a test replicating Kim's diamond dependency example.

The top-level package, `D`, depends on two intermediate packages, `B`
and `C`, which each require semantically different versions of another
package, `A`. The portion of `A` that `B` and `C` publicly use is
unchanged across the versions, but they both privately make use of
changed API. Currently, this causes a version clash. This will be made
to work without error later this quarter.
2025-11-13 05:40:56 +00:00
David Thrane Christiansen
ceb86b1293 fix: details in Markdown rendering of Verso docstrings (#11151)
This PR fixes some details in the Markdown renderings of Verso
docstrings, and adds tests to keep them correct. Also adds tests for
Verso docstring metadata.
2025-11-13 05:19:30 +00:00
Lean stage0 autoupdater
a00c78beea chore: update stage0 2025-11-13 02:05:09 +00:00
Leonardo de Moura
ff9c35d6ef feat: #grind_lint command (#11157)
This PR implements the `#grind_lint` command, a diagnostic tool for
analyzing the behavior of theorems annotated for theorem instantiation.
The command helps identify problematic theorems that produce excessive
or unbounded instance generation during E-matching, which can lead to
performance issues.
The main entry point is:
```
#grind_lint check
```
which analyzes all theorems marked with the `@[grind]` attribute.
For each theorem, it creates an artificial goal and runs `grind`,
collecting statistics about the number of instances produced.
Results are summarized using info messages, and detailed breakdowns are
shown for lemmas exceeding a configurable threshold.
Additional subcommands are provided for targeted inspection and control:

* `#grind_lint inspect thm`: analyzes one or more specific theorems in
detail
* `#grind_lint mute thm`: excludes a theorem from instantiation during
analysis
* `#grind_lint skip thm`: omits a theorem from being analyzed by
`#grind_lint check`
2025-11-13 00:42:18 +00:00
Kim Morrison
eb675f708b feat: user extensibility in try? (#11149)
This PR adds a user-extension mechanism for the `try?` tactic. You can
either use the `@[try_suggestion]` attribute on a declaration with
signature ``MVarId -> Try.Info -> MetaM (Array (TSyntax `tactic))`` to
produce suggestions, or the `register_try?_tactic <stx>` command with a
fixed piece of syntax. User-extensions are only tried *after* the
built-in try strategies have been tried and failed.

I wanted to ensure that if the user provides a tactic that produces a
"Try this:" suggestion, we both emit the original tactic and the
suggested replacement (this is what we already do with `grind` and
`simp`). I have this working, but it is quite hacky: we grab the message
log and parse it. I fear this will break when the "Try this:" format is
inevitably changed in the future.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds user-defined suggestion generators for `try?` via
`@[try_suggestion]` and `register_try?_tactic`, executed after built-ins
with priority and double-suggestion handling.
> 
> - **Parser/Command**:
> - Add command syntax `register_try?_tactic (priority := n)?
<tacticSeq>` in `Lean.Parser.Command`.
> - **Suggestion registry**:
> - Introduce `@[try_suggestion (prio)]` attribute with a scoped env
extension to register generators (`MVarId → Try.Info → MetaM (Array
(TSyntax `tactic))`).
>   - Priority ordering (higher first); supports local/global scope.
> - **Tactic engine (`try?`)**:
> - New unsafe pipeline to collect and run user generators after
built-in tactics; expands nested "Try this" outputs from user tactics.
> - `mkTryEvalSuggestStx` now takes `(goal, info)`; integrates user
tactics as fallback via `attempt_all`.
> - Suppress intermediate "Try this" messages during `evalAndSuggest` by
restoring the message log.
> - **Imports**:
>   - Add `meta import Lean.Elab.Command` for command elaboration.
> - **Tests**:
> - `try_register_builtin.lean`: command availability and warning
without import.
> - `try_user_suggestions.lean`: basic, priority, built-in fallback,
double-suggestion, and command registration cases.
> - Update `versoDocMissing.lean.expected.out` to include
`register_try?_tactic` in expected commands.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
302dc94544. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-12 23:49:54 +00:00
Wojciech Różowski
b39ee8a84b feat: add minimal support for getEntry/getEntry?/getEntry!/getEntryD for DHashMap (#11076)
This PR adds `getEntry`/`getEntry?`/`getEntry!`/`getEntryD` operation on
DHashMap.
2025-11-12 16:56:28 +00:00
Paul Reichert
9a3fb90e40 refactor: replace Iter(M).size with Iter(M).count (#10952)
This PR replaces `Iter(M).size` with the `Iter(M).count`. While the
former used a special `IteratorSize` type class, the latter relies on
`IteratorLoop`. The `IteratorSize` class is deprecated. The PR also
renames lemmas about ranges be replacing `_Rcc` with `_rcc`, `_Rco` with
`_roo` (and so on) in names, in order to be more consistent with the
naming convention.
2025-11-12 16:41:00 +00:00
Lean stage0 autoupdater
7f7a4d3eaf chore: update stage0 2025-11-12 15:54:53 +00:00
Sebastian Graf
09cf07b71c feat: new do elaborator, part 1: doElem_elab attribute (#11150)
This PR adds a new, inactive and unused `doElem_elab` attribute that
will allow users to register custom elaborators for `doElem`s in the
form of the new type `DoElab`. The old `do` elaborator is active by
default but can be switched off by disabling the new option
`backward.do.legacy`.
2025-11-12 14:25:28 +00:00
Leonardo de Moura
d464b13569 feat: add cases_next to grind tactic mode (#11148)
This PR addst the `cases_next` tactic to the `grind` interactive mode.
2025-11-12 03:26:18 +00:00
Leonardo de Moura
f2b3f90724 refactor: symmetric equality congruence in grind (#11147)
This PR refactors the implementation of the symmetric equality
congruence rule used in `grind`.
2025-11-12 01:10:37 +00:00
Kim Morrison
bc60b1c19d fix: don't suggest deprecated theorems (#11146)
This PR fixes a bug in #11125. Added a test this time ...

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Exclude deprecated declarations from library suggestions and add a
test verifying they are filtered out.
> 
> - **Library Suggestions**:
> - Update `isDeniedPremise` in `src/Lean/LibrarySuggestions/Basic.lean`
to treat `Lean.Linter.isDeprecated` as denied (`true`), filtering
deprecated constants from suggestions.
> - **Tests**:
> - Add `tests/lean/run/library_suggestions_deprecated.lean` to verify
deprecated theorems (e.g., `deprecatedTheorem`) are not suggested by
`currentFile`, while non-deprecated ones are.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ef7e546dbc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-12 00:58:47 +00:00
Leonardo de Moura
fa3c85ee84 fix: missing condition in isMatchCondCandidate (#11145)
This PR fixes a bug in `isMatchCondCandidate` used in `grind`. The
missing condition was causing a "not internalized term" `grind` internal
error.
2025-11-12 00:20:37 +00:00
Wojciech Różowski
34f9798b4b feat: add DTreeMap/TreeMap/TreeSet iterators and slices (#10776)
This PR adds iterators and slices for `DTreeMap`/`TreeMap`/`TreeSet`
based on zippers and provides basic lemmas about them.

---------

Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-11-11 17:49:50 +00:00
Wojciech Różowski
e0af5122f7 feat: add union on ExtDTreeMap/ExtTreeMap/ExtTreeSet (#11070)
This PR adds union operation on ExtDHashMap/ExtHashMap/ExtHashSet nd
provides lemmas about union operations.

Stacked on top of #10946.
2025-11-11 16:52:07 +00:00
Markus Himmel
f1224277e2 perf: improve performance of String.ValidPos (#11142)
This PR aims to bring the performance of `String.ValidPos` closer to
that of `String.Pos.Raw` by adding/correcting `extern` annotations as
needed.

This is in response to a regression observed after #11127. The changes
to the `String` `Parsec` module lead to different compiler behavior for
functions like `strCore` and `natCore`. The new IR *looks* better than
the old IR, but the
[numbers](1e438647ba)
are a bit mixed.
2025-11-11 15:30:47 +00:00
Marc Huisinga
c2647cdbf5 fix: pre-filter completion items mod ascii casing (#11140)
This PR ensures that we pre-filter auto-completion items modulo ASCII
casing for consistency with the VS Code fuzzy matching.
2025-11-11 14:11:05 +00:00
dependabot[bot]
aaceb3dbf5 chore: CI: bump actions/upload-artifact from 4 to 5 (#11052)
Bumps
[actions/upload-artifact](https://github.com/actions/upload-artifact)
from 4 to 5.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:41:14 +00:00
dependabot[bot]
3ae409cf81 chore: CI: bump actions/download-artifact from 5 to 6 (#11053)
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 5 to 6.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:40:56 +00:00
dependabot[bot]
a7f47db134 chore: CI: bump softprops/action-gh-release from 2.3.3 to 2.4.1 (#11054)
Bumps
[softprops/action-gh-release](https://github.com/softprops/action-gh-release)
from 2.3.3 to 2.4.1.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:40:23 +00:00
Markus Himmel
2c2fcff4f8 refactor: do not use String.Iterator (#11127)
This PR removes all uses of `String.Iterator` from core, preferring
`String.ValidPos` instead.

In an upcoming PR, `String.Iterator` will be renamed to
`String.Legacy.Iterator`.
2025-11-11 11:46:58 +00:00
Kim Morrison
d1e19f2aa0 feat: support for induction in try? (#11136)
This PR adds support for `try?` to use induction; it will only perform
induction on inductive types defined in the current namespace and/or
module; so in particular for now it will not induct on built-in
inductives such as `Nat` or `List`.

This is stacked on top of #11132, and there are overlapping changes.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds vanilla induction suggestions to `try?`, updates collection of
inductive candidates, and tests the new behavior on custom inductive
types.
> 
> - **Try tactic pipeline**:
> - Add vanilla induction generators (`mkIndStx`, `mkAllIndStx`) that
try `induction <var> <;> …`, with fallback via `expose_names` when
needed.
> - Integrate induction into `mkTryEvalSuggestStx`, alongside existing
atomic, suggestions, and function-induction options.
> - **Collector updates (`Try/Collect.lean`)**:
> - Enhance `checkInductive` to `whnf` the type and use `getAppFn` to
detect inductive heads, populating `indCandidates`.
> - **Tests**:
> - New `tests/lean/run/try_induction.lean` covering suggestions for
`induction` on custom inductives, interaction with `grind`, and
coexistence with `fun_induction`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b357990c97. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-11 09:29:59 +00:00
Kim Morrison
838be605ac feat: replace Int.pow with a @[csimp] lemma (#11138)
This PR adds a `csimp` lemma for faster runtime evaluation of `Int.pow`
in terms of `Nat.pow`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Replaces `Int.pow` evaluation with a `@[csimp]` lemma using `Nat.pow`
and adds supporting lemmas (`pow_mul`, `neg_pow`, nonneg results).
> 
> - **Performance/runtime**:
> - Introduce `powImp` and `@[csimp]` theorem `pow_eq_powImp` to
evaluate `Int.pow` via `Nat.pow` with sign handling.
> - **Math lemmas (supporting)**:
>   - `Int.pow_mul`: `a ^ (n * m) = (a ^ n) ^ m`.
>   - `Int.sq_nonnneg`: nonnegativity of `m ^ 2`.
>   - `Int.pow_nonneg_of_even`: nonnegativity for even exponents.
>   - `Int.neg_pow`: `(-m)^n = (-1)^(n % 2) * m^n`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
66ac236db7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2025-11-11 06:39:10 +00:00
Kim Morrison
02b141ca15 feat: add library suggestions support to try? tactic (#11132)
This PR adds support for `grind +suggestions` and `simp_all?
+suggestions` in `try?`. It outputs `grind only [X, Y, Z]` or `simp_all
only [X, Y, Z]` suggestions (rather than just `+suggestions`).

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-11 06:38:28 +00:00
Kim Morrison
fe8238c76c feat: grind cases on Sum (#11087)
This PR enables `grind` to case bash on `Sum` and `PSum`.
2025-11-11 04:50:34 +00:00
François G. Dorais
7f77bfef4c feat: add List.mem_finRange (#9515)
This PR adds a missing lemma for the `List` API.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add `[simp]` lemma `List.mem_finRange` proving any `x : Fin n` is in
`finRange n`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
631f7ca852. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-11-11 04:16:08 +00:00
Leonardo de Moura
e7e85e5e17 fix: stackoverflow during proof construction in grind (#11137)
This PR fixes a stackoverflow during proof construction in `grind`.

Closes #11134
2025-11-11 03:23:43 +00:00
Leonardo de Moura
1b5fb2fa50 fix: check exponent in grind lia and grind ring (#11135)
This PR ensures that `checkExp` is used in `grind lia` (formerly known
as `grind cutsat`) and `grind ring` to prevent stack overflows.

closes #11130
2025-11-11 02:28:55 +00:00
Leonardo de Moura
0e455f5347 fix: disequality ctor propagation in grind (#11133)
This PR fixes disequality propagation for constructor applications in
`grind`. The equivalence class representatives may be distinct
constructor applications, but we must ensure they have the same type.
Examples that were panic'ing before this PR:
```lean
example (a b : List Nat)
    : a ≍ ([] : List Int) → b ≍ ([1] : List Int) → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → a ≍ ([] : List Int) → b = [1] → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → a ≍ ([] : List Int) → b = [1] → b ≍ [(1 : Int)] → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → b = [1] → a = b ∨ p → p := by
  grind

example (a b : List Nat)
    : a = [] → a ≍ ([] : List Int) → b = [1] → a = b ∨ p → p := by
  grind
```

Closes #11124
2025-11-11 01:28:54 +00:00
Leonardo de Moura
f74e21e302 fix: grind injection should not fail at clear (#11126)
This PR ensures `grind` does not fail when applying `injection` to a
hypothesis that cannot be cleared because of forward dependencies.
2025-11-10 14:50:18 +00:00
Wojciech Różowski
c08fcf6c28 feat: add union on ExtDHashMap/ExtHashMap/ExtHashSet (#10946)
This PR adds union operation on ExtDHashMap/ExtHashMap/ExtHashSet nd
provides lemmas about union operations.
2025-11-10 13:48:36 +00:00
Benjamin Shi
ecae85e77b doc: fix typo in List.finIdxOf? (#11111)
This PR fixes a typo in the doc string of `List.finIdxOf?`. The first
line of the doc string previously says the function returns the size of
the list if no element equal to `a`, but both the examples in the doc
string and real run-time behavior indicate it returns `none` in this
case.

Closes #11110
2025-11-10 10:04:07 +00:00
ecyrbe
6008c0d523 feat: add min and max list operations (#11060)
This PR add list `min` and `max` operations to complement `min?` and
`max?` ones in the same vain as `head?` and `head`.

It was dicussed here in
[zulip](https://leanprover.zulipchat.com/#narrow/channel/217875-Is-there-code-for-X.3F/topic/maximum.20of.20a.20list.20known.20to.20be.20nonempty/with/548296389)

it also add small unit tests for `min` , `max`, `min?` and `max?`
2025-11-10 09:56:59 +00:00
Kim Morrison
d47b474e41 feat: suggestions don't included deprecated theorems (#11125)
This PR adds a filter for premise selectors to ensure deprecated
theorems are not returned.
2025-11-10 04:24:06 +00:00
Kim Morrison
c7652413db feat: link docstrings for diamond inheritance (#11122)
This PR fixes a problem for structures with diamond inheritance: rather
than copying doc-strings (which are not available unless `.server.olean`
is loaded), we link to them. Adds tests.
2025-11-10 01:05:01 +00:00
Kim Morrison
08d0ae1e8a feat: add foldl_flatMap and foldr_flatMap theorems (#11123)
This PR adds theorems about folds over flatMaps, for
`List`/`Array`/`Vector`.

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-09 23:00:29 +00:00
Kim Morrison
6fc48d14c0 feat: missing lemmas about List.findIdx (#11113)
This PR adds some small missing lemmas.
2025-11-09 21:16:11 +00:00
Mac Malone
80409a9ceb feat: lake: Job.sync & other touchups (#11118)
This PR adds `Job.sync` as a standard way of declaring a synchronous
job.

It makes some non-behavior changes to related Job APIs to improve
compilation.
2025-11-08 04:35:05 +00:00
Mac Malone
590ff23e71 fix: lake: moreLinkObjs|Libs on a lean_exe (#11117)
This PR fixes a bug where Lake ignored `moreLinkObjs` and `moreLinkLibs`
on a `lean_exe`.
2025-11-08 04:20:42 +00:00
Joachim Breitner
f843837bfa test: test missing cases error (#11107)
This PR tests the missing cases error.

I thought I broke this, but it seems I did not (or at least not this
way, maybe there is a way to trigger it).
2025-11-06 14:38:55 +00:00
Joachim Breitner
d41f39fb10 perf: sparse case splitting in match compilation (#10823)
This PR lets the match compilation procedure use sparse case analysis
when the patterns only match on some but not all constructors of an
inductive type. This way, less code is produce. Before, code handling
each of the other cases was then optimized and commoned-up by later
compilation pipeline, but that is wasteful to do.

In some cases this will prevent Lean from noticing that a match
statement is complete
because it performs less case-splitting for the unreachable case. In
this case, give explicit
patterns to perform the deeper split with `by contradiction` as the
right-hand side.

At least temporarily, there is also the option to disable this behaviour
with
```
set_option backwards.match.sparseCases false
```
2025-11-06 13:46:35 +00:00
Joachim Breitner
7459304e98 refactor: bv_decide: remove verifyEnum et. al (#11068)
This PR removes the `verifyEnum` functions from the bv_decide frontend.
These functions looked at the implementation of matchers to see if they
really do the matching that they claim to do. This breaks that
abstraction barrier, and should not be necessary, as only functions with
a `MatcherInfo` env entry are considered here, which should all play
nicely.
2025-11-06 09:22:36 +00:00
Kim Morrison
e6b1f1984c feat: suggestions tactic generates hovers (#11098)
This PR updates the `suggestions` tactic so the printed message includes
hoverable type information (and displays scores and flags when
relevant).
2025-11-06 06:31:04 +00:00
Kim Morrison
6d2af21aa0 feat: add Int.ediv_pow and related lemmas (#11100)
This PR adds `theorem Int.ediv_pow {a b : Int} {n : Nat} (hab : b ∣ a) :
(a / b) ^ n = a ^ n / b ^ n` and related lemmas.

---------

Co-authored-by: Bhavik Mehta <bhavikmehta8@gmail.com>
2025-11-06 06:16:18 +00:00
Kim Morrison
3a4e64fe94 feat: some missing Array grind annotations (#11102)
This PR adds some annotations missing in the Array bootstrapping files.
2025-11-06 05:22:40 +00:00
Leonardo de Moura
0d7ca700ad fix: Function.Injective initialization in grind (#11101)
This PR fixes an initialization issue for local `Function.Injective f`
hypotheses.

Closes #11088
2025-11-06 04:26:57 +00:00
Leonardo de Moura
f401f8b46e fix: universe meta-variable support in grind (#11099)
This PR improves the support for universe-metavariables in `grind`.

Closes #11086
2025-11-06 03:38:59 +00:00
Sebastian Ullrich
ae86c18ac1 chore: backward.privateInPublic should not break irrelevance of proofs for rebuilds (#11097) 2025-11-05 23:00:04 +00:00
Sebastian Ullrich
ea2b745e57 chore: new module system adjustments for the Mathlib port (#11093) 2025-11-05 22:17:53 +00:00
Kim Morrison
28a3cf9a6c chore: grind attributes for Prod (#11085) 2025-11-05 20:52:28 +00:00
Joachim Breitner
343887e480 perf: use hasIndepIndices (#11095)
This PR makes use of `hasIndepIndices`. That function was unused since
commit 54f6517ca3, but it seems it should
be used.
2025-11-05 18:41:23 +00:00
Joachim Breitner
d8a67095d6 chore: make workspaceSymbol benchmarks modules (#11094)
This PR makes workspaceSymbol benchmarks `module`s, so that they are
less sensitive to additions of private symbols in the standard library.
2025-11-05 18:40:39 +00:00
Joachim Breitner
0cb79868f4 feat: sparse casesOn constructions (#11072)
This PR adds “sparse casesOn” constructions. They are similar to
`.casesOn`, but have arms only for some constructors and a catch-all
(providing `t.ctorIdx ≠ 42` assumptions). The compiler has native
support for these constructors and now (because of the similarity) also
the per-constructor elimination principles.
2025-11-05 15:49:11 +00:00
Leonardo de Moura
ccecac5a56 chore: use abbrev in denote functions (#11092)
This PR ensures that `grind ac` denotation functions used in proof by
reflection are marked as `abbrev`.
2025-11-05 13:51:36 +00:00
Marc Huisinga
8b43fc54b2 doc: clarify server protocol violations around initialize (#11091) 2025-11-05 09:53:39 +00:00
Leonardo de Moura
e7f4f98071 fix: stackoverflow during proof construction in grind (#11084)
This PR fixes a stack overflow that occurs when constructing a proof
term in `grind`.

Closes #11081
2025-11-05 02:35:05 +00:00
Leonardo de Moura
52e37e0d55 refactor: denote functions in grind (#11071)
This PR ensures that the `denote` functions used to implement
proof-by-reflection terms in `grind` are abbreviations. This change
eliminates the need for the `withAbstractAtoms` gadget.
2025-11-04 23:34:17 +00:00
Leonardo de Moura
a4e073f565 fix: panic during equality propagation in grind ring (#11080)
This PR fixes a panic during equality propagation in the `grind ring`
module. If the maximum number of steps has been reached, the polynomials
may not be fully simplified.

Closes #11073
2025-11-04 23:20:38 +00:00
Sebastian Ullrich
18131de438 fix: evalConst meta check and auxiliary IR decls (#11079)
Uncovered in Mathlib through new boxed decls from `BaseIO` changes
2025-11-04 21:29:49 +00:00
Leonardo de Moura
e430626d8a fix: anchor values in grind? (#11077)
This PR fixes the anchor values produced by `grind?`
2025-11-04 13:03:18 +00:00
Kim Morrison
e76bbef79b feat: simp? +suggestions handles ambiguity (#11075)
This PR updates `simp? +suggestions` so that if a name is ambiguous
(because of namespaces) all alternatives are used, rather than erroring.
2025-11-04 05:26:51 +00:00
Kim Morrison
04d72fe346 chore: basic dev instructions for Claude (#11074)
This PR adds a `.claude/claude.md`, with basic development instructions
for Claude Code to operate in this repository.
2025-11-04 04:07:53 +00:00
Sebastian Ullrich
e4fb780f8a perf: remove unused argument to ExternEntry.opaque (#11066)
This used to create quite a few unique objects in public .olean
2025-11-03 17:26:32 +00:00
Wojciech Różowski
00e29075f3 feat: add union on DTreeMap/TreeMap/TreeSet (#10896)
This PR adds union operations on DTreeMap/TreeMap/TreeSet and their raw
variants and provides lemmas about union operations.

---------

Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2025-11-03 13:47:44 +00:00
Kim Morrison
ec775907e4 chore: update stage0 2025-11-03 23:26:40 +11:00
Kim Morrison
8d603d34dc feat: make set_library_suggestions persistent 2025-11-03 23:26:40 +11:00
Mac Malone
528c0dd2e4 feat: lake: require dependencies by semver range (#10959)
This PR enables Lake users to require Reservoir dependencies by a
semantic version range. On a `lake update`, Lake will fetch the
package's version information from Reservoir and select the newest
version of the package that satisfies the range.

### Using Version Ranges

Version ranges can be specified through the `version` field of a TOML
`require` or the `@` clause of a Lean `require`. They are only
meaningful on Reservoir dependencies.

**lakefile.lean**
```lean-4
require "Seasawher" / "mdgen" @ "2.*"
```

**lakefile.toml**
```toml
[[require]]
name = "mdgen"
scope = "Seasawher"
version = "2.*"
```

The syntax for these versions ranges is a mix of
[Rust's](https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html?highlight=caret#version-requirement-syntax)
and
[Node's](https://github.com/npm/node-semver/tree/v7.7.3?tab=readme-ov-file#ranges)
with some Lean-friendly deviations.

### Comparators

The basic unit of semantic version ranges are version comparators. They
take a base version and a comparison operator and match versions which
compare positively with their base. Lake supports the following
comparison operators.

* `<`, `<=` / `≤`, `>`, `>=` / `≥`, `=`, `!=` / `≠`

Unlike Rust and Node, Lake supports Unicode alternatives for the
operators. It also adds the not-equal operator (`!=` / `≠`) to make
excluding broken versions easier.

Comparators can be combined into clauses via conjunction or disjunction:

* **AND clauses**: Rust-style `≥1.2.3, <1.8.0` or Node-style `1.2.3
<1.8.0`
* **OR clauses**: Node-style `1.2.7 || >=1.2.9, <2.0.0`

When the base version of a comparator has a `-` suffix (e.g.,
`>1.2.3-alpha.3`) it will match versions of the same core (`1.2.3`) with
suffixes that lexicographically compare (e.g., `1.2.3-alpha.7` or
`1.2.3-beta.2`) but will not match suffixed versions of different cores
(e.g., `3.4.5-rc5`). An empty `-` suffix can be used to disable this
behavior. For example, `<2.0.0-` will match `1.2.3-beta.2` and
`2.0.0-alpha.1`.

### Range Macros

In addition to the basic comparators, Lake also supports standard
shorthand for specifying more complex ranges. Namely, it supports the
caret (`^`) and tilde (`~`) operator along with wildcard ranges.

**Caret Ranges**
* `^1` => `≥1.0.0, <2.0.0-`
* `^1.2` => `≥1.2.0, <2.0.0-`
* `^1.2.3` => `≥1.2.3, <2.0.0-`
* `^1.2.3-beta.2` => `≥1.2.3-beta.2, <2.0.0-`
* `^0.2` => `≥0.0.0, <0.3.0-`
* `^0.2.3` => `≥0.2.3, <0.3.0-`
* `^0.0.3` => `≥0.0.3, <0.0.4-`
* `^0` => `≥0.0.0, <1.0.0-`
* `^0.0` => `≥0.0.0, <0.1.0-`

**Tilde Ranges**
* `~1` => `≥1.0.0, <2.0.0-`
* `~1.2` => `≥1.2.0, <1.3.0-`
* `~1.2.3` => `≥1.2.3, <1.3.0-`
* `~1.2.3-beta.2` => `≥1.2.3-beta.2, <1.3.0-`
* `^0` => `≥0.0.0, <1.0.0-`
* `^0.2.3` => `≥0.2.3, <0.3.0-`
* `^0.0.3` => `≥0.0.3, <0.0.4-`
* `~0` => `≥0.0.0, <1.0.0-`
* `~0.0` => `≥0.0.0, <0.1.0-`
* `~0.0.0` => `≥0.0.0, <0.1.0-`

**Wildcard Ranges**
* `*` => `≥0.0.0`
* `1.x` => `≥1.0.0, <2.0.0-`
* `1.*.x` => `≥1.0.0, <2.0.0-`
* `1.2.*` => `≥1.2.0, <1.3.0-`

These ranges closely follow Rust's and Node's syntax. Like Node but
unlike Rust, wildcard ranges support `x` and `X` as alternative syntax
for wildcards.
2025-11-03 04:18:24 +00:00
Kim Morrison
b8bd91d92b feat: simp? +suggestions (#11032)
This PR implements `simp? +suggestions`, which uses the configured
library suggestion engine to add relevant theorems to the `simp` call.
`simp +suggestions` without the `?` prints a message requiring adding
the `?`.
2025-11-03 03:26:16 +00:00
Lean stage0 autoupdater
e05b0e097c chore: update stage0 2025-11-03 02:54:49 +00:00
Mac Malone
d23dcc88ea fix: lake: pin mathlib in new/init to toolchain (#11063)
This PR changes the `math` and `math-lax` templates for `lake new` and
`lake init` to use the version of Mathlib corresponding to the current
Lean toolchain. Thus, `lake +x.y.z new <pkg> math` will use the Mathlib
for Lean `x.y.z`. On the other hand, `lake update` on such packages will
no longer update Mathlib automatically. Users will need to change the
Mathlib revision in the configuration file before updating.

This change was inspired by a
[discussion](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Build.20error.20on.20.60lake.20.2Bleanprover.2Flean4.3Av4.2E22.2E0.20new.20foo.20math.60/near/546236449)
on the community Zulip.
2025-11-03 02:16:29 +00:00
Mac Malone
1aca181fe2 fix: lake: use -O0 for debug builds (#11062)
This PR changes Lake's debug build type to use `-O0` instead of `-Og`
when compiling C code. `-Og` was found to be insufficient for debugging
compiled Lean code -- relevant code was stilled optimized out.
2025-11-03 02:16:26 +00:00
Leonardo de Moura
2c45d55683 fix: deep recursion type checking grind proof (#11061)
This PR fixes a deep recursion issue in the kernel when type-checking a
proof term produced by `grind`.

Closes #11059
2025-11-02 19:43:48 +00:00
Rob23oba
f4c71f6ec8 fix: simplify Nat.ble (#11058)
This PR changes `Nat.ble` by joining the two `Nat.ble Nat.zero _` cases
into one, allowing `decide (0 <= x) = true` and `decide (0 < succ x) =
true` to be solvable by `rfl`.
2025-11-02 12:39:49 +00:00
Henrik Böving
823173a761 fix: make ST.Ref.ptrEq behave as stated in the docs (#11056)
This PR fixes `ST.Ref.ptrEq` to act as described in the docs. This fixes
two bugs:
1. The recent `IO.RealWorld` elimination PR overlooked this function
(afaik this is the only one),
   causing its return value to be generally wrong.
2. The implementation of `ptrEq` would previously always consider two
different cells with pointer
equivalent value to be pointer equal. However, the function is supposed
to check whether two
   `Ref` are the same cell, not whether the contained elements are.
2025-11-02 10:42:33 +00:00
Leonardo de Moura
3e86729ee0 feat: grind? using finish? infrastructure (#11057)
This PR implements `grind?` using the new `grind => finish?`
infrastructure.
2025-11-02 05:00:50 +00:00
Leonardo de Moura
2da124d469 chore: remove grind offset (#11051)
This PR removes the `grind offset` module because it is (now) subsumed
by `grind order`.
2025-11-01 19:08:18 +00:00
Leonardo de Moura
1d00dee392 fix: grind order equality propagation for Nat (#11050)
This PR fixes equality propagation for `Nat` in `grind order`.
2025-11-01 18:35:34 +00:00
Leonardo de Moura
e5a6901161 feat: Nat equality propagation in grind order (#11049)
This PR implements equality propagation for `Nat` in `grind order`.
`grind order` supports offset equalities for rings, but it has an
adapter for `Nat`. Example:
```lean
example (a b : Nat) (f : Nat → Int) : a ≤ b + 1 → b + 1 ≤ a → f (1 + a) = f (1 + b + 1) := by
  grind -offset -mbtc -lia -linarith (splits := 0)
```
2025-11-01 15:37:17 +00:00
Markus Himmel
cf871a892c chore: use String.ofList instead of String.mk in elaborator+kernel (#11048)
This PR is a follow-up to #11017, preparing for the eventual removal of
the `String.mk` function.
2025-11-01 14:44:16 +00:00
Markus Himmel
d24ece1396 feat: String.toList_map (#11021)
This PR adds more theory about `Splits` for strings and deduces the
first user-facing `String` lemma, `String.toList_map`.
2025-11-01 13:54:39 +00:00
Leonardo de Moura
faed852c2f feat: equality propagation in grind order (#11047)
This PR implements (nested term) equality propagation in `grind order`.
That is, it propagates implied equalities from `grind order` back to the
`grind` core. Examples:
```lean
open Lean Grind Std

example [LE α] [IsPartialOrder α] (a b : α) (f : α → Nat) : a ≤ b → b ≤ c → c ≤ a → f a = f b := by
  grind (splits := 0)

example [CommRing α] [LE α] [LT α] [LawfulOrderLT α] [IsPartialOrder α] [OrderedRing α]
    (a b : α) (f : α → Int) : a ≤ b + 1 → b ≤ a - 1 → f a = f (2 + b - 1) := by
  grind -mbtc -lia -linarith (splits := 0)

example (a b : Int) (f : Int → Int) : a ≤ b + 1 → b ≤ a - 1 → f a = f (2 + b - 1) := by
  grind -mbtc -lia -linarith (splits := 0)
```
2025-11-01 13:45:54 +00:00
Sebastian Ullrich
4939f447a3 test: avoid testing colliding private inductives (#11041)
`prelude-injectivity.lean` was testing inj thm generation for all
inductives in core, including private ones, which could lead to name
clashes that should not be able to occur in actual files. Put it under
the module system to not load private decls in the first place.
2025-11-01 11:47:52 +00:00
Henrik Böving
3d307925b7 refactor: make constant folding more robust for future bugs (#11044)
This PR enforces users of the constant folder API to provide proofs of
their algebraic properties,
thus hopefully avoiding bugs such as #11042 and #11043 in the future.
2025-11-01 11:07:20 +00:00
Rob23oba
1fa67d0d47 fix: overeager Nat.sub constant folding (#11043)
This PR fixes a case of overeager constant folding on Nat where the
compiler would mistakenly assume `0 - x = x` (see also #11042 for the
same bug on UInts).
2025-11-01 10:14:20 +00:00
Henrik Böving
51ef1dcc5e fix: overeager uint constant folding (#11042)
This PR fixes a case of overeager constant folding on UInts where the
compiler would mistakenly
assume `0 - x = x`.
2025-11-01 02:42:43 +00:00
Leonardo de Moura
282b583f1d fix: panic during the processing of generalized patterns in grind (#11040)
This PR fixes a panic that occurred during the processing of generalized
E-matching patterns in `grind`.

Closes #10983
2025-10-31 16:58:11 +00:00
Leonardo de Moura
4a111501eb fix: grind invalid universe level regression (#11039)
This PR fixes the `grind` invalid universe level regression reported in
#11036

Closes #11036
2025-10-31 15:44:34 +00:00
Leonardo de Moura
16d0005991 feat: finish? produces finish only suggestion (#11034)
This PR adds a new suggestion to `finish?`. It now generates the `grind`
tactic script as before, and a `finish only` tactic. Example:
```lean
/--
info: Try these:
  [apply] ⏎
    instantiate only [findIdx, insert, = mem_indices_of_mem]
    instantiate only [= getElem?_neg, = getElem?_pos]
    cases #1bba
    · instantiate only [findIdx]
    · instantiate only
      instantiate only [= HashMap.mem_insert, = HashMap.getElem_insert]
  [apply] finish only [findIdx, insert, = mem_indices_of_mem, = getElem?_neg, = getElem?_pos, = HashMap.mem_insert,
    = HashMap.getElem_insert, #1bba]
-/
example (m : IndexMap α β) (a : α) (b : β) :
    (m.insert a b).findIdx a = if h : a ∈ m then m.findIdx a else m.size := by
  grind => finish?
```
2025-10-31 14:47:24 +00:00
Markus Himmel
377f149862 refactor: use String.ofList and String.toList for String <-> List Char conversion (#11017)
This PR establishes `String.ofList` and `String.toList` as the preferred
method for converting between strings and lists of characters and
deprecates the alternatives `String.mk`, `List.asString` and
`String.data`.
2025-10-31 14:41:23 +00:00
Markus Himmel
c41cb64ca7 chore: adjust pr-title check to enforce capitalization (#11033)
This PR updates the `pr-title` CI check to enforce that the commit
message does not start with a capital letter followed by a non-capital
letter.

This should ensure that messages do not start with a capitalized word,
but allow messages that start with an acronym.
2025-10-31 07:23:25 +00:00
Kim Morrison
1ce05b2a17 feat: library suggestion engine for local theorems (#11030)
This PR adds a library suggestion engine for local theorems. To be
useful, I still need to write more combinators to re-rank and combine
suggestions from multiple engines.

This is stacked on top of #11029.
2025-10-31 04:48:53 +00:00
Kim Morrison
0db89d65b2 chore: use 'library suggestions' rather than 'premise selection' (#11029)
This PR changes the terminology used from "premise selection" to
"library suggestions". This will be more understandable to users (we
don't assume anyone is familiar with the premise selection literature),
and avoids a conflict with the existing use of "premise" in Lean
terminology (e.g. "major premise" in induction, as well as generally the
synonym for "hypothesis"/"argument").
2025-10-31 04:07:49 +00:00
Kim Morrison
00d41d64e5 feat: remove +premises from grind? output (#11028)
This PR ensures that `grind? +premises` removes `+premises` from the
"Try this" suggestion.
2025-10-31 03:00:33 +00:00
Leonardo de Moura
40e1e097c1 fix: grind order nontermination and propagation issues (#11026)
This PR fixes a nontermination and missing propagation bug in `grind
order`. It also register relevant case-splits for arithmetic.

Closes #11001
2025-10-30 23:12:51 +00:00
Joachim Breitner
d7e4f32d75 feat: Bool.ctorIdx (#11024)
This PR lets `Bool` have `.ctorIdx` like any other inductive.
2025-10-30 21:17:43 +00:00
Joachim Breitner
c7f57d6a0b fix: avoid unnecessary branching in match compilation (#10763)
This PR improves match compilation: Branch on variables in the order
suggested by the first remaining alternative, and do not branch when the
first remaining alternative does not require it. This fixes
https://github.com/leanprover/lean4/issues/10749. With `set_option
backwards.match.rowMajor false` the old behavior can be turned on.

(For now this is an experiment to get familiar with the code and the
whole
problem domain. It is likely overly naive.)
2025-10-30 20:05:13 +00:00
Wrenna Robson
275f9077b6 fix: Rename theorems that use sorted instead of pairwise (#10743)
This PR renames theorems that use `sorted` in their name to instead use
`pairwise`.


Closes #10742.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-10-30 16:07:35 +00:00
Henrik Böving
8b28467655 perf: better detection of repeated branching on same value (#11020)
This PR improves the detection of situations where we branch multiple
times on the same value in the
code generator. Previously this would only consider repeated branching
on function arguments, now on
arbitrary values.


Closes: #11018
2025-10-30 16:02:45 +00:00
Markus Himmel
c0ad969b14 fix: docstring of ByteArray.IsValidUTF8.intro (#11022)
This PR fixes an incomplete docstring.
2025-10-30 15:57:01 +00:00
AxelBoldt
e73e0901f8 doc: improve variable names and specify types in SMul doc (#10989)
When documenting smul : M → α → α, it is unintuitive and confusing to
use variables a:M and b:α. It is better to use m:M and a:α. I also
specified the types of all involved terms in the documentation text.
2025-10-30 15:56:42 +00:00
Henrik Böving
cc046e0c18 perf: improve join point finding (#10999)
This PR improves join point finding in the compiler through two means:
1. We now handle situations where a function `f` can only become a join
point when a function `g`
   becomes a join point as well correctly.
2. We introduce a second join point finding pass after specialisation
and before the following
simplification pass, as the specialiser might have introduced new join
point opportunities for
   the simplifier to exploit.

Notably in the code from #10995 we now correctly detect the missing join
point which required both
of these changes to be made.

Closes: #10995
2025-10-30 15:05:11 +00:00
Wrenna Robson
e11ef3ee4e fix: Remove redundant instance requirement (#10941)
This PR removes a redundant instance requirement from
`Std.instIrreflLtOfIsPreorderOfLawfulOrderLT`.
2025-10-30 13:21:26 +00:00
Kim Morrison
b2b385b456 chore: update stage0 2025-10-30 18:43:59 +11:00
Kim Morrison
7a8c2daf96 feat: sine qua non premise selection 2025-10-30 18:43:59 +11:00
Lean stage0 autoupdater
33e92677ba chore: update stage0 2025-10-30 07:41:23 +00:00
Markus Himmel
5af12df54b chore: add String.ofList redefine String.toList (#11016)
This PR ensures that `String.toList` and `String.ofList` exist and have
the right `extern` annotations.
2025-10-30 07:07:12 +00:00
Kim Morrison
705084d9ba chore: deprecate more duplications (#11004)
This PR deprecates various duplicated definitions, detected in
[#mathlib4 > duplicate declarations @
💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/duplicate.20declarations/near/547434277)
2025-10-30 05:58:29 +00:00
Joachim Breitner
e2f5938e74 refactor: some Meta.Match.Match refactorings (#11011)
This PR extracts some refactorings from #10763, including dropping dead
code and not failing in `inaccessibleAsCtor`, which leadas to (slightly)
better error messages, and also on the grounds that the failing
alternative may actually be unreachable.
2025-10-29 23:24:57 +00:00
Leonardo de Moura
d3c9056d2b feat: support grind parameters in finish and finish? (#11012)
This PR ensures the `grind` tactics `finish` and `finish?` can take
parameters.
2025-10-29 20:51:48 +00:00
Joachim Breitner
2f0f0a3018 chore: pr release: git commit --allow-empty (#11013)
This PR uses `--allow-empty` so that the PR release script does not fail
when it's being retried after an intermittent failure.
2025-10-29 21:48:11 +01:00
Henrik Böving
1587d02dfb fix: more stable eager lambda lifting heuristic (#11010)
This PR makes the eager lambda lifting heuristic more predictable by
blocking it from lifting from
any kind of inlineable function, not just `@[inline]`. It also adapts
the doc-string to describe
what is actually going on.
2025-10-29 13:58:23 +00:00
Henrik Böving
a51822ead2 perf: inline Decidable instances (#11008)
This PR inlines several Decidable instances for performance reasons.

Unlike the previous #10934 it does not attempt to also simplify the
Decidable instance system as
that has proven to have non trivial performance impact.

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-10-29 10:59:54 +00:00
Henrik Böving
2cfd980528 fix: revert the waitAny refactoring (#11000)
This PR fixes a memleak caused by the Lean based `IO.waitAny`
implementation by reverting it.

This the faulty Lean implementation:
```lean
def IO.waitAny (tasks : @& List (Task α)) (h : tasks.length > 0 := by exact Nat.zero_lt_succ _) :
    BaseIO α := do
  have : Nonempty α := ⟨tasks[0].get⟩
  let promise : IO.Promise α ← IO.Promise.new
  tasks.forM <| fun t => BaseIO.chainTask (sync := true) t promise.resolve
  return promise.result!.get
```
In a situation where we call this function repeatedly in a loop with a
pair of tasks `[t1, t2]`
where `t2` is a long lived task that we pass every time and `t1` is
fresh a short lived task, `t2` will
accumlate more and more children from `BaseIO.chainTask` that fill
memory over time. The old C++
implementation did not have this issue so we are reverting.
2025-10-29 08:27:16 +00:00
Henrik Böving
2497cf0b40 fix: Revert "perf: inline decidable instances" (#11007)
This PR reverts leanprover/lean4#10934
2025-10-29 08:06:51 +00:00
Markus Himmel
167429501b refactor: redefine String.replace (#10986)
This PR defines `String.Slice.replace` and redefines `String.replace` to
use the `Slice` version.

The new implementation is generic in the pattern, so it supports things
like `"education".replace isVowel "☃!" = "☃!d☃!c☃!t☃!☃!n"`. Since it
uses the `ForwardSearcher` infrastructure, `String` patterns are
searched using KMP, unlike the previous implementation which had
quadratic runtime. As a side effect, the behavior when replacing an
empty string now matches that of most other programming languages,
namely `"abc".replace "" "k" = "kakbkck"`.
2025-10-29 07:48:33 +00:00
Sebastian Graf
d427423917 chore: remove duplicate lemmas in Std.Do.SPred (#11006)
This PR removes the duplicate lemmas
`Std.Do.SPred.{and_pure,or_pure,imp_pure,entails_pure_intro}`.
2025-10-29 07:26:24 +00:00
Markus Himmel
106b0fa661 fix: KMP implementation (#10998)
This PR fixes the KMP implementation, which did incorrect bookkeeping of
the backtracking process, leading to incorrect starting ranges of
matches.

The new implementation does not require `partial` anywhere.
2025-10-29 06:04:45 +00:00
Kim Morrison
335e34df19 chore: add deprecations for duplicated theorems (#10967) 2025-10-29 05:26:16 +00:00
Leonardo de Moura
d436619c6d feat: add anchor support for restricting search space in grind only (#11003)
This PR adds support for specifying anchors to restrict the search space
in `grind` when using `grind only`. Anchors can limit which case splits
are performed and which local lemmas are instantiated.
2025-10-29 01:16:10 +00:00
Marc Huisinga
19533ab1d4 feat: revamp server logging (#10787)
This PR revamps the server logging mechanism to allow filtering the log
output by LSP method.
2025-10-28 16:26:59 +00:00
Leonardo de Moura
28310a77ad feat: config options at finish (#10997)
This PR adds support for configuration options at `finish` and
`finish?`.
2025-10-28 15:41:26 +00:00
Lean stage0 autoupdater
8ff5b8ec2c chore: update stage0 2025-10-28 13:47:28 +00:00
Leonardo de Moura
99ff606d58 chore: rename cutsat => lia (#10991)
This PR renames `cutsat` in configuration options and trace messages to
`lia`.
2025-10-28 12:25:48 +00:00
Kim Morrison
8862d23453 chore: cleanup in Dyadic API (#10994) 2025-10-28 06:41:46 +00:00
Kim Morrison
d8335bd661 feat: add @[grind ext] attributes for extensional maps (#10993)
This PR allows `grind` to work extensionally on extensional maps/sets.
2025-10-28 05:20:45 +00:00
Kim Morrison
1981c62604 feat: make grind +premises more robust to bad suggestions (#10992)
This PR ensures that `grind +premises` silently drops warnings and
errors about bad suggestions.
2025-10-28 03:22:42 +00:00
Leonardo de Moura
bb3dd13f72 feat: set_config for setting grind configuration options (#10990)
This PR adds the `set_config` tactic for setting `grind` configuration
options. It uses the same syntax used for setting configuration options
in the `grind` main tactic.
2025-10-28 02:25:01 +00:00
Joachim Breitner
808d123e6f doc: typo in grindDef docstring (#10988)
This PR fixes a typo in the `grindDef` docstring.
2025-10-27 15:59:06 +00:00
Joachim Breitner
14d76cc062 fix: decreasing_by: preserve variable names of match alts (#10980)
This PR tries to preserve names of pattern variables in match
alternatives in `decreasing_by`, by telescoping into the concrete
alternative rather than the type of the matcher's alt. Fixes #10976.
2025-10-27 14:00:36 +00:00
Markus Himmel
d2f76ade61 fix: search for empty string (#10985)
This PR ensures that searching for an empty string returns the expected
pattern of alternating size-zero matches and size-one rejects.

In particular, splitting by an empty string returns an array formed of
the empty string, all of the string's characters as singleton strings,
followed by another empty string. This matches the [Rust
behavior](https://doc.rust-lang.org/std/primitive.str.html#method.split),
for example.
2025-10-27 13:05:33 +00:00
Rob23oba
12750d25b5 perf: inline decidable instances (#10934)
This PR adds inline annotations to several `Decidable` instances.
Additionally, it removes the `Decidable` instance for `p → q` which is
made redundant by `forall_prop_decidable`.
2025-10-27 12:05:01 +00:00
Henrik Böving
334fa475b4 fix: use general allocator for closures (#10982)
This PR changes the closure allocator to use the general allocator
instead of the small object one.
This is because users may create closures with a gigantic amount of
closed variables which in turn
boost the size of the closure beyond the small object threshold.

This issue was uncovered by #10979. Detecting that the small object
threshold is at fault requires
building mimalloc in debug mode at which point it yields:
```
mimalloc: assertion failed: at "/home/henrik/lean4/build/debug/mimalloc/src/mimalloc/src/alloc.c":132, mi_heap_malloc_small_zero
  assertion: "size <= MI_SMALL_SIZE_MAX"
```
The generated code at fault here looks as follows:
```c
LEAN_EXPORT lean_object* l_initExec___at___00res_spec__0(lean_object* x_1) {
_start:
{
lean_object* x_2; lean_object* x_3; lean_object* x_4; lean_object* x_5; lean_object* x_6; lean_object* x_7; lean_object* x_8; lean_object* x_9; lean_object* x_10; lean_object* x_11; lean_object* x_12; lean_object* x_13; lean_object* x_14;
x_2 = lean_alloc_closure((void*)(l_initializer_ext___at___00initExec___at___00res_spec__0_spec__0___lam__0___boxed), 3, 0);
x_3 = l_initExec___redArg___closed__0;
x_4 = l_initExec___redArg___closed__1;
x_5 = l_instMonadLiftNonDetT___closed__0;
x_6 = l_initExec___redArg___closed__2;
x_7 = l_initExec___at___00res_spec__0___closed__0;
lean_inc_ref(x_2);
x_8 = lean_alloc_closure((void*)(l_initExec___at___00res_spec__0___lam__29___boxed), 213, 212);
lean_closure_set(x_8, 0, x_3);
lean_closure_set(x_8, 1, x_2);
lean_closure_set(x_8, 2, x_4);
lean_closure_set(x_8, 3, x_3);
lean_closure_set(x_8, 4, x_4);
lean_closure_set(x_8, 5, x_3);
lean_closure_set(x_8, 6, x_4);
lean_closure_set(x_8, 7, x_3);
lean_closure_set(x_8, 8, x_4);
lean_closure_set(x_8, 9, x_3);
lean_closure_set(x_8, 10, x_4);
lean_closure_set(x_8, 11, x_3);
lean_closure_set(x_8, 12, x_4);
lean_closure_set(x_8, 13, x_3);
lean_closure_set(x_8, 14, x_4);
lean_closure_set(x_8, 15, x_5);
lean_closure_set(x_8, 16, x_6);
lean_closure_set(x_8, 17, x_5);
lean_closure_set(x_8, 18, x_5);
lean_closure_set(x_8, 19, x_5);
lean_closure_set(x_8, 20, x_5);
lean_closure_set(x_8, 21, x_5);
lean_closure_set(x_8, 22, x_5);
...
```
With the crash happening in `lean_alloc_closure` where we
unconditionally invoke the small allocator
which cannot cope with closures this large. Hopefully changing this to
the general purpose allocator
doesn't have too much of an impact on performance.

Closes: #10979
2025-10-27 10:16:59 +00:00
Markus Himmel
8fe260de55 feat: termination arguments for String.ValidPos and String.Slice.Pos (#10933)
This PR adds the basic infrastructure to perform termination proofs
about `String.ValidPos` and `String.Slice.Pos`.

We choose approach where the intended way to do termination arguments is
to argue about the position itself rather than some projection of it
like `remainingBytes`.

The types `String.ValidPos` and `String.Slice.Pos` are equipped with a
`WellFoundedRelation` instance given by the greater-than relation. This
means that if a function takes a position `p` and performs a recursive
call on `q`, then the decreasing obligation will be `p < q`. This works
well in the common case where `q` is `p.next h`, in which case the goal
`p < p.next h` is solved by the simplifier.

For stepping through a string backwards, we introduce a type synonym
with a `WellFoundedRelation` instance given by the less-than relation.
This means that if a function takes a position `p` and performs a
recursive call on `q` and specifies `termination_by p.down`, then the
decreasing obligation will be `q < p`. This works well in the case where
`q` is `p.prev h`, in which case the goal `p.prev h < p` is solved by
the simplifier.

For termination arguments invoving multiple strings, the lower-level
primitive `p.remainingBytes` (landing in `Nat`) is also available.

In a future PR, we will additionally provide the necessary typeclasses
instances to register `String.ValidPos` and `String.Slice.Pos` with
`grind` to make complex termination arguments more convenient in user
code.
2025-10-27 10:05:44 +00:00
Henrik Böving
7e1be20317 perf: widen more in ElimDeadBranches (#10856)
This PR performs more widening in ElimDeadBranches in an attempt to
improve performance in situations with a lot of local precision.

While this is not enough to make the compilation instant it pushes
compilation time from 12s to 3s for the example in #10857 and barely
introduces regressions so it seems like a good first step in this
direction.

Closes: #10857
2025-10-27 09:12:16 +00:00
Leonardo de Moura
3a42ee0c30 feat: grind tactic mode improvements (#10978)
This PR implements the following `grind` improvements:
1. `set_option` can now be used to set `grind` configuration options in
the interactive mode.
2. Fixes a bug in the repeated theorem instantiation detection.
3. Adds the macro `use [...]` as a shorthand for `instantiate only
[...]`.
2025-10-27 04:47:02 +00:00
Kim Morrison
30ecacd625 feat: add LawfulOfScientific class (#10971)
This PR adds a `LawfulOfScientific` class, providing compatibility with
a `Lean.Grind.Field` structure.
2025-10-27 04:18:55 +00:00
Kim Morrison
a0e742be5e chore: >6 month old deprecations (#10969) 2025-10-26 22:48:41 +00:00
Leonardo de Moura
50e2fdaa74 feat: add cdot combinator in grind tactic mode (#10975)
This PR adds the combinator ` · t_1 ... t_n` to the `grind` interactive
mode. The `finish?` tactic now generates scripts using this combinator
to conform to Mathlib coding standards. The new format is also more
compact. Example:
```lean
/--
info: Try this:
  [apply] ⏎
    instantiate only [= mem_indices_of_mem, insert, = getElem_def]
    instantiate only [= getElem?_neg, = getElem?_pos]
    cases #f590
    · cases #ffdf
      · instantiate only
        instantiate only [= Array.getElem_set]
      · instantiate only
        instantiate only [size, = HashMap.mem_insert, = HashMap.getElem_insert, = Array.getElem_push]
    · instantiate only [= mem_indices_of_mem, = getElem_def]
      instantiate only [usr getElem_indices_lt]
      instantiate only [size]
      cases #ffdf
      · instantiate only [=_ WF]
        instantiate only [= getElem?_neg, = getElem?_pos, = Array.getElem_set]
        instantiate only [WF']
      · instantiate only
        instantiate only [= HashMap.mem_insert, = HashMap.getElem_insert, = Array.getElem_push]
-/
#guard_msgs in
example (m : IndexMap α β) (a a' : α) (b : β) (h : a' ∈ m.insert a b) :
    (m.insert a b)[a'] = if h' : a' == a then b else m[a'] := by
  grind => finish?
```
2025-10-26 21:27:00 +00:00
Sebastian Ullrich
77ddfd49e6 chore: further shake improvements (#10947) 2025-10-26 11:27:19 +00:00
Kim Morrison
4887eeb77c chore: remove >6 month old deprecations (#10968) 2025-10-26 10:01:30 +00:00
Kim Morrison
9cf2877945 fix: mis-stated lemmas about empty raw maps (#10966)
This PR fixes some mis-stated lemmas which should have been about the
`.Raw` variants of maps.
2025-10-26 07:51:39 +00:00
Leonardo de Moura
97d63db52c fix: mbtc for nonlinear terms in grind cutsat (#10965)
This PR ensures that model-based theory combination in `grind cutsat`
considers nonlinear terms. Nonlinear multiplications such as `x * y` are
treated as uninterpreted symbols in `cutsat`.

Closes #10885
2025-10-26 04:35:34 +00:00
Leonardo de Moura
f8b0beeba9 fix: propagator for a^(n+m) in grind (#10964)
This PR adds a propagator for `a^(n+m)` and removes its normalizer. This
change was motivated by issue #10661

Closes #10661
2025-10-26 03:52:28 +00:00
Leonardo de Moura
93c5bd0fdd chore: use realizeGlobalConstNoOverloadWithInfo (#10963)
closes #10427
closes #10426
2025-10-26 02:45:46 +00:00
Leonardo de Moura
feb864712f fix: spurious warning message in grind (#10962)
This PR fixes a spurious warning message in `grind`.

Closes #10670
2025-10-26 02:40:12 +00:00
Leonardo de Moura
2f3211028b feat: support for Rat scientific literals (#10961)
This PR adds support for scientific literals for `Rat` in `grind`.
`grind` does not yet add support for this kind of literal in arbitrary
fields.

closes #10489
2025-10-26 02:05:26 +00:00
Leonardo de Moura
cdaa827b2a fix: grind linarith counterexample (#10960)
This PR fixes a bug in the `grind linarith` model/counterexample
construction.

Closes #10500
2025-10-26 00:27:47 +00:00
Kim Morrison
a9d6bc60d0 chore: update stage0 2025-10-26 10:29:47 +11:00
Kim Morrison
9166c71e08 feat: don't count symbols in instances and proofs 2025-10-26 10:29:47 +11:00
Kim Morrison
43b2d41e81 chore: add test file 2025-10-26 10:29:47 +11:00
Kim Morrison
e8620255a0 feat: symbol frequency environment extension 2025-10-26 10:29:47 +11:00
Leonardo de Moura
48f0eb206c test: rationals in grind linarith (#10958) 2025-10-25 21:48:43 +00:00
Leonardo de Moura
962e7d5a30 test: for #10317 (#10957)
This PR adds tests for issue that has been fixed by previous commits.

closes #10317
2025-10-25 21:42:56 +00:00
Leonardo de Moura
aa59c01742 fix: equality propagation in grind order (#10956)
This PR fixes a bug in the equality propagation procedure in
`grind.order`. Specifically, it affects the procedure that asserts
equalities in the `grind` core state that are implied by (ring)
inequalities in the `grind.order` module.

closes #10622
2025-10-25 20:12:04 +00:00
Leonardo de Moura
cd60d9c14a fix: grind order regression (#10955)
This PR fixes a regression in the `grind order` module introduced by

Closes #10953
2025-10-25 16:12:28 +00:00
Eric Wieser
b3ef7c9f25 chore: add an assertion about mkValueTypeClosure (#10954)
This is a guard against #10705; if a kernel error is raised when the
return value of this function is eventually checked, it is often
silenced downstream, making it hard to spot the failure.
If we panic here via `assert!`, then the diagnostic cannot be missed.
2025-10-25 12:59:17 +00:00
Lean stage0 autoupdater
be2c2bcf9b chore: update stage0 2025-10-25 11:32:01 +00:00
Leonardo de Moura
d5ca0c7032 fix: bug in cutsat model construction (#10951)
This PR fixes a bug in the `cutsat` incremental model construction. The
model was not being reset when new (unsatisfied) equalities were
asserted.
2025-10-25 04:16:32 +00:00
Leonardo de Moura
3c2ab0fefa feat: model-based theory combination tactic and action (#10950)
This PR adds the `mbtc` tactic to the `grind` interactive mode. It
implements model-based theory combination. It also ensures `finish?` is
capable of generating it.
2025-10-25 03:35:19 +00:00
Leonardo de Moura
1643fd7532 fix: finish? checks whether solver propagation steps are needed (#10949)
This PR ensures that solver propagation steps are necessary in the
generated tactic script to close the goal.

It produces more compact proof scripts, but this is not just an
optimization, if we include an unnecessary step, we may fail to replay
the generated script when `cases` steps are pruned using
non-chronological backtracking (NCB). For example, when executing
`finish?`, we may have performed a `cases #<anchor>` step that enabled
`ring` to propagate a new fact. If this fact is not used in the final
proof, and the corresponding `cases #<anchor>` step is pruned by NCB,
the `ring` step will fail during replay.
2025-10-25 02:27:44 +00:00
Leonardo de Moura
53442d48f5 feat: finish? produces partial tactic scripts with sorry (#10948)
This PR ensures that `finish?` produces partial tactic scripts
containing `sorry`s.
We may add an option to disable this feature in the future.
It is enabled by default because it provides a useful way to debug
`grind` failures.
2025-10-24 23:47:30 +00:00
Joachim Breitner
96bace56fa fix: run enableRealizationsForConst on sizeOf decls (#10944)
This PR runs enableRealizationsForConst on sizeOf declarations. Fixes
#10573.
2025-10-24 16:15:38 +00:00
Sebastian Ullrich
b0127e01e3 chore: final module system fixes and refinements for initial Mathlib porting (#10869) 2025-10-24 15:53:49 +00:00
Joachim Breitner
e71d0abc7d test: test case for #10775 (#10943) 2025-10-24 14:54:36 +00:00
Joachim Breitner
a6d50a61b3 fix: Meta.Closure: topologically sort abstracted vars (#10926)
This PR topologically sorts abstracted vars in
`Meta.Closure.mkValueTypeClosure` if MVars are being abstracted.
Fixes #10705

---------

Co-authored-by: Eric Wieser <efw@google.com>
2025-10-24 12:07:16 +00:00
Sebastian Ullrich
dec007693a chore: fix C++ warning (#10922) 2025-10-24 11:09:08 +00:00
Henrik Böving
1145498521 fix: regression from ST redefinition (#10940)
This PR fixes a regression introduced by the `ST` redefinition by making
the definition of `ST` as
reducible as previously. The key issue here is that in the previous
state it would reduce to a
function at which point the monad lifting mechanisms don't kick in in
the same fashion anymore.
2025-10-24 08:50:56 +00:00
Leonardo de Moura
2f07b70870 fix: default parameter value in constructor footgun at cases tactic (#10939)
This PR fixes another instance of the “default parameter value in
constructor” footgun, which was affecting the `cases` tactic in the
`grind` interactive mode.
2025-10-24 00:56:15 +00:00
Leonardo de Moura
09b36c332a fix: missing processNewFacts at solver tactics (#10938)
This PR ensures solver `grind` tactics (e.g., `ac`, `ring`, `lia`, etc)
process pending facts after making progress.
2025-10-24 00:08:06 +00:00
Leonardo de Moura
955fff52c5 fix: missing reset ematch.num at cases (#10937)
This PR fixes a missing counter reset at the `cases` tactic in `grind`
interactive mode.
2025-10-23 23:58:36 +00:00
Leonardo de Moura
6d665f3e91 fix: bugs at grind => finish? (#10936)
This PR fixes issues in `grind => finish?` that were preventing
generated `grind` tactic scripts from being successfully replayed.
2025-10-23 22:35:20 +00:00
Joachim Breitner
74fd46894f fix: deprecation warning location with field notation (#10826)
This PR fixes the location of the “deprecated constant” and similar
error messages on field notation (`e.f`, `(e).f`, `e |>. f`). Fixes
#10821.
2025-10-23 20:55:25 +00:00
Joachim Breitner
ffaadcc990 doc: warning for wf_preprocess (#10897)
This PR adds a warning to `wf_preproces` that these lemmas can be used
to introduce hidden partiality.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-10-23 20:54:40 +00:00
Joachim Breitner
54175f3b99 fix: decreasing_by: remove mdata (#10931)
This PR strips the `Expr.mdata` that `WF.Fix` uses to associate goal
with recursive calls from the goal presented to the tactics.
Fixes #10895.
2025-10-23 20:54:32 +00:00
Markus Himmel
59573646c2 chore: more minor String improvements (#10930)
This PR moves some more material out of `Init.Data.String.Basic` and
fixes the incorrect name
`String.Pos.Raw.IsValidForSlice.le_utf8ByteSize`.
2025-10-23 13:57:23 +00:00
Markus Himmel
ba7798b389 chore: more reorganization of strings (#10928)
This PR splits more material out of `Init.Data.String.Basic`.
2025-10-23 11:56:11 +00:00
Henrik Böving
f1203f3d0d perf: used hashmaps for symbol lookup in the interpreter (#10927)
This PR uses hashmaps for the symbol lookups in the IR interpreter
instead of the existing rbmaps.
Thus reducing the constant overhead per function call.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-10-23 11:45:20 +00:00
Henrik Böving
881a131ad3 chore: re-enable tests (#10923) 2025-10-23 08:38:57 +00:00
Lean stage0 autoupdater
fb3e2c15fd chore: update stage0 2025-10-23 08:03:18 +00:00
Rob23oba
fad0e69cc7 fix: make name mangling unambiguous (#10727)
This PR fixes name mangling to be unambiguous / injective by adding `00`
for disambiguation where necessary. Additionally, the inverse function,
`Lean.Name.unmangle` has been added which can be used to unmangle a
mangled identifier. This unmangler has been added to demonstrate the
injectivity but also to allow unmangling identifiers e.g. for debugging
purposes.

Closes #10724
2025-10-23 07:18:07 +00:00
Kim Morrison
cf22c367a1 feat: grind +premises (#10920)
This PR adds support for `grind +premises`, calling the currently
configured premise selection algorithm and including the results as
parameters to `grind`. (Recall that there is not currently a default
premise selector provided by Lean4: you need a downstream premise
selector to make use of this.)
2025-10-23 06:42:48 +00:00
Mac Malone
291d238ec4 test: symbol clash on private import of same def (#10915)
This PR adds a test for depending on two packages which privately import
modules that define the same Lean definition. It verifies the current
behavior of a symbol clash. This behavior will be fixed later this
quarter.
2025-10-23 03:42:52 +00:00
Kim Morrison
69e1eae480 feat: grind +lax ignores bad parameters (#10890)
This PR adds a `+lax` configuration option for `grind`, causing it to
ignore parameters referring to non-existent theorems, or to theorems for
which we can't generate a pattern. This allows throwing large sets of
theorems (e.g. from a premise selection enginre) into `grind` to see
what happens.
2025-10-23 03:19:31 +00:00
Leonardo de Moura
b1d4c9b9d5 feat: grind "silent" have (#10919)
This PR implements the `have <ident>? : <prop>` tactic for the `grind`
interactive mode. The proposition is proved using the default `grind`
search strategy. This tactic is also useful for inspecting or querying
the current `grind` state.
2025-10-23 02:36:49 +00:00
Leonardo de Moura
d748b0c8c9 feat: instantiate tactic parameter optimizer (#10916)
This PR implements parameter optimization for the generated
`instantiate` tactics produced by `finish?`.
We use a simple parameter optimizer that takes two sets as input: the
lower and upper bounds.
The lower bound consists of the theorems actually used in the proof
term, while the upper bound includes all the theorems instantiated in a
particular theorem instantiation step.
The lower bound is often sufficient to replay the proof, but in some
cases, additional theorems must be included because a theorem
instantiation may contribute to the proof by providing terms and many
not be present in the final proof term.
2025-10-23 01:22:33 +00:00
Kim Morrison
90af66d64b chore: minor changes to MePo (#10917)
This PR makes minor changes to the MePo premise selection algorithm.

I'm increasingly believing that MePo will not work well in Lean; I've
tried a few things without success. Alistair Geesing's thesis from 2023
had similar conclusions.

My intention is to reach the point we can properly benchmark premise
selection algorithms before doing any more work here.
2025-10-23 01:15:38 +00:00
Markus Himmel
3ce7d4ef5c chore: minor optimizations on the critical path (#10900)
This PR optimizes two `String` proofs and makes sure that
`MkIffOfInductiveProp` does not import `Lean.Elab.Tactic`, which
previously pushed it to the very end of the import graph.
2025-10-22 19:32:26 +00:00
Leonardo de Moura
63c06725bb feat: preserve instantiation order at finish? (#10899)
This PR ensures the generated `instantiate` tactic instantiates the
theorems using the same order used by `finish?`
2025-10-22 17:44:26 +00:00
Markus Himmel
b5dc11e8d3 chore: move some material out of Init.Data.String.Basic (#10893)
This PR splits some low-hanging fruit out of `Init.Data.String.Basic`:
basic material about `String.Pos.Raw`, `String.Substrig`, and
`String.Iterator`.

More splitting required and the remaining material is quite unorganized,
but it's a start.
2025-10-22 16:31:08 +00:00
Eric Wieser
08bc333705 perf: mark move constructors and assignment operators as noexcept (#10784)
Detected by
https://clang.llvm.org/extra/clang-tidy/checks/performance/noexcept-move-constructor.html.
This ensures constructions like `std::vector<object_ref>` call these
operators instead of the copy ones, and do not do extra refcounting.

Note that `optional` and `atomic` need something more complex using
`noexcept()`, as they are templated.
2025-10-22 14:21:51 +00:00
Henrik Böving
52b1b342ab feat: zero cost BaseIO (#10625)
This PR implements zero cost `BaseIO` by erasing the `IO.RealWorld`
parameter from argument lists and structures. This is a **major breaking
change for FFI**.

Concretely:
- `BaseIO` is defined in terms of `ST IO.RealWorld`
- `EIO` (and thus `IO`) is defined in terms of `EST IO.RealWorld`
- The opaque `Void` type is introduced and the trivial structure
optimization updated to account for it. Furthermore, arguments of type
`Void s` are removed from the argument lists of the C functions.
- `ST` is redefined as `Void s -> ST.Out s a` where `ST.Out` is a pair
of `Void s` and `a`

This together has the following major effects on our generated code:
- Functions that return `BaseIO`/`ST`/`EIO`/`IO`/`EST` now do not take
the dummy world parameter anymore. To account for this FFI code needs to
delete the dummy world parameter from the argument lists.
- Functions that return `BaseIO`/`ST` now return their wrapped value
directly. In particular `BaseIO UInt32` now returns a `uint32_t` instead
of a `lean_object*`. To account for this FFI code might have to change
the return type and does not need to call `lean_io_result_mk_ok` anymore
but can instead just `return` values right away (same with extracting
values from `BaseIO` computations.
- Functions that return `EIO`/`IO`/`EST` now only return the equivalent
of an `Except` node which reduces the allocation size. The
`lean_io_result_mk_ok`/`lean_io_result_mk_error` functions were updated
to account for this already so no change is required.

Besides improving performance by dropping allocation (sizes) we can now
also do fun new things such as:
```lean
@[extern "malloc"]
opaque malloc (size : USize) : BaseIO USize
```
2025-10-22 10:55:12 +02:00
Hikaru Hasegawa
6ee8511ae3 doc: fix example parenthesis in natAbs docstring (#10870)
This PR fixes an incorrect parenthesis in the `natAbs` docstring in
`src/Init/Data/Int/Basic.lean`.
2025-10-22 08:23:09 +00:00
Markus Himmel
6a1cc7d6b8 chore: minor String improvements (#10891)
This PR renames the cast functions on `String.ValidPos` for `set` and
`modify` to adhere to the established naming convention.

It also fixes two typos and very slighly tweaks the import graph,
shortening the critical path by a negligible amount.
2025-10-22 06:35:51 +00:00
Kim Morrison
2fb4d4bac2 chore: updates to release automation (#10888) 2025-10-22 03:17:28 +00:00
Leonardo de Moura
9b794befe0 feat: use new TermInfo.isDisplayableTerm when hovering grind anchors (#10887)
This PR uses the new `TermInfo.isDisplayableTerm` when hovering over
`cases` tactic anchors in the `grind` interactive mode.
2025-10-22 02:52:18 +00:00
Kim Morrison
38240612bc chore: add Rat.max_def lemma (#10886)
Closes #10842.
2025-10-22 00:38:52 +00:00
Mac Malone
3768c07654 fix: lake: cache revision path (#10883)
This PR fixes a bug with Lake's cache where revisions were stored at the
incorrect path. Revisions were stored at `<rev>/<pkg>.jsonl` rather than
the correct `<pkg>/<rev>.jsonl`.
2025-10-21 23:59:44 +00:00
Kim Morrison
466b0f824c chore: begin development cycle for v4.26 (#10884) 2025-10-21 23:22:58 +00:00
Kim Morrison
11eabdb000 chore: release_steps runs lake exe cache get when needed (#10882) 2025-10-21 22:49:38 +00:00
Leonardo de Moura
bd05f87d01 fix: grind proof instability (#10881)
This PR fixes a proof instability source in `grind`.

We say a proof is *unstable* if minor changes in the `.lean` file
containing the proof **affect** it.
2025-10-21 22:08:15 +00:00
Leonardo de Moura
56f3ca6fc7 fix: propagation in grind order (#10877)
This PR fixes theory propagation issue in `grind order`.
2025-10-21 16:38:39 +00:00
Sofia Rodrigues
94cb32bc46 fix: ipv4 address encoding from libuv to lean (#10854)
This PR fixes the IPv4 address encoding from libuv to lean
2025-10-21 14:17:22 +00:00
Leonardo de Moura
2f90b1dadd fix: elaborate grind state filter (#10874)
This PR uses the correct context for elaborating the `grind` state
filter.
2025-10-21 12:31:21 +00:00
Sebastian Graf
2991c66c81 chore: add missing spec lemmas for OptionT (#10867) 2025-10-21 12:01:35 +00:00
Eric Wieser
3f82b307aa fix: rule of 3 for xtimit (#10818)
This PR adds a missing move assignment operator, and deletes the copy
assignment operator.

C++ types should not implement move constructors without also
implementing move assignment. This also ensures that `m_fn` is correctly
emptied after a move, which is not guaranteed by the standard.

This change is also needed to allow `lean::optional` to be eventually
replaced by `std::optional`.
2025-10-21 12:01:23 +00:00
Sebastian Graf
bce47c6e0d fix: improve performance of mvcgen by optimizing try (mpure_intro; trivial) (#10872)
This PR improves the performance of `mvcgen` by an optimized
implementation for `try (mpure_intro; trivial)`. This tactic sequence is
used to eagerly discharge VCs and in the process instantiates schematic
variables.
2025-10-21 11:32:24 +00:00
Markus Himmel
b28daa6d60 chore: rename String.endPos -> String.rawEndPos (#10853)
This PR renames `String.endPos` to `String.rawEndPos`, as in a future
release the name `String.endPos` will be taken by the function that is
currently called `String.endValidPos`.
2025-10-21 11:25:30 +00:00
Markus Himmel
196d50156a fix: logic error in String.Slice.takeWhile (#10868)
This PR fixes a bug in `String.Slice.takeWhile` which caused it to get
its bookkeeping wrong and panic. The new version only uses safe
operations on `String.Slice.Pos`.
2025-10-21 09:52:11 +00:00
Sebastian Graf
2eb44b3cdc chore: fix a typo in the docstring for SeqRight and add documentation to getLevel (#10866) 2025-10-21 09:07:24 +00:00
Sebastian Graf
916125ae1c fix: make Std.Do.Spec.forIn'_list and friends more universe polymorphic (#10865)
This PR makes the spec `Std.Do.Spec.forIn'_list` and friends more
universe polymorphic.

The regression test came from a dicussion on the public Zulip.
2025-10-21 09:04:52 +00:00
Henrik Böving
bd0b91de07 perf: reduce amount of symbols in DLLs (#10864)
This PR reduces the amount of symbols in our DLLs by cutting open a
linking cycle of the shape:

`Environment -> Compiler -> Meta -> Environment`

This is achieved by introducing a dynamic call to the compiler hidden
behind a `Ref` as previously
done in the pretty printer.
2025-10-21 09:00:56 +00:00
Sebastian Ullrich
37b78bd53d chore: more module system fixes and refinements for finishing batteries port (#10819) 2025-10-21 08:19:50 +00:00
Marc Huisinga
2e3d947e07 feat: flag on TermInfo to force rendering of term in hover (#10805)
This PR adds a field `isDisplayableTerm` to `TermInfo` and all utility
functions which create `TermInfo` that can be set to force the language
server to render the term in hover popups.
2025-10-21 08:19:20 +00:00
Markus Himmel
a5a8f2779c chore: rename String.Range to Lean.Syntax.Range (#10852)
This PR renames `String.Range` to `Lean.Syntax.Range`, to reflect that
it is not part of the standard library.
2025-10-21 07:32:25 +00:00
Mac Malone
efbbb0b230 fix: lake: recurse directories in input_dir (#10861)
This PR fixes `input_dir` tracking to also recurse through
subdirectories. The `filter` of an `input_dir` will be applied to each
file in the directory tree (the path names of directories will not be
checked).

Closes #10827.
2025-10-21 04:19:10 +00:00
Leonardo de Moura
a366cbcd20 feat: show_term for grind interactive mode (#10862)
This PR implements the `show_term` combinator in `grind` interactive
mode.
2025-10-21 02:30:16 +00:00
Kim Morrison
756fee3e96 feat: improvements to release automation for v4.25.0-rc1 (#10860)
This PR improves the release automation. We link to CI output for
building the release tag, don't give instructions for bumping downstream
repositories until the release it ready, and improve documentation and
prompts.
2025-10-21 00:59:44 +00:00
Leonardo de Moura
2ead798d87 fix: set_option auto-completion in grind mode (#10859)
This PR fixes auto-completion for `set_option` in `grind` interactive
mode.
2025-10-21 00:53:35 +00:00
Leonardo de Moura
baacf86e7f feat: improve done tactic in grind interactive mode (#10858)
This PR improves the `done` tactic in `grind` interactive mode. It now
displays the `grind` state diagnostics for all unsolved subgoals.
2025-10-21 00:25:40 +00:00
Joachim Breitner
e3a5369bd7 perf: match compilation to use exfalso early (#10851)
This PR lets match compilation use exfalso as soon as no alternatives
are left. This way, the compiler does not have to look at subsequent
case splits.
2025-10-20 12:23:23 +00:00
Markus Himmel
c981ebc546 feat: split and splitInclusive iterators are finite (#10820)
This PR shows that the iterators returned by `String.Slice.split` and
`String.Slice.splitInclusive` are finite as long as the forward matcher
iterator for the pattern is finite (which we already know for all of our
patterns).

At actually also completely redefines the iterators to avoid the inner
loop in `Internal.nextMatch` which generates inefficient code. Instead,
when encountering a mismach from the matcher, we `skip` the split
iterator.
2025-10-20 10:21:21 +00:00
Paul Reichert
71f1a6c164 feat: Iterator find? consumer and variants (#10769)
This PR adds a `find?` consumer in analogy to `List.find?` and variants
thereof.
2025-10-20 09:12:53 +00:00
Sebastian Ullrich
0d5869bb71 fix: stuck "Missing alternative name" with incremental processing (#10848)
This PR fixes an issue where adding a missing case name after the pipe
in `induction` would not remove the now-obsolete error message.

Fixes #10847
2025-10-20 08:00:48 +00:00
Leonardo de Moura
135e7e7bd3 fix: instance tactic generation at finish? (#10846)
This PR fixes a few issues on `instance only [...]` tactic generation at
`finish?`.
2025-10-20 03:10:38 +00:00
Kim Morrison
58a884ef06 chore: update actions/checkout action in lake new template (#10845)
This PR update the `lake new` template to use the current version of the
`actions/checkout` Github workflow.
2025-10-20 02:32:52 +00:00
Kim Morrison
77e72afe0a chore: tweak error message about weak options (#10844)
This PR tweaks the error message about options defined in libraries.
This was relevant for an option defined in Mathlib, but set in FLT.
2025-10-20 02:28:20 +00:00
Leonardo de Moura
823671f744 feat: set_option tactic in grind interactive mode (#10843)
This PR implements the `set_option` tactic in `grind` interactive mode.
2025-10-20 00:44:59 +00:00
Leonardo de Moura
681724a8cf feat: generate instantiate only [...] at finish? (#10841)
This PR improves the `grind` tactic generated by the `instantiate`
action in tracing mode. It also updates the syntax for the `instantiate`
tactic, making it similar to `simp`. For example:

* `instantiate only [thm1, thm2]` instantiates only theorems `thm1` and
`thm2`.
* `instantiate [thm1, thm2]` instantiates theorems marked with the
`@[grind]` attribute **and** theorems `thm1` and `thm2`.

The action produces `instantiate only [...]` tactics. Example:

```lean
/--
info: Try this:
  [apply] ⏎
    instantiate only [= Array.getElem_set]
    instantiate only [= Array.getElem_set]
-/
#guard_msgs in
example (as bs cs : Array α) (v₁ v₂ : α)
        (i₁ i₂ j : Nat)
        (h₁ : i₁ < as.size)
        (h₂ : bs = as.set i₁ v₁)
        (h₃ : i₂ < bs.size)
        (h₄ : cs = bs.set i₂ v₂)
        (h₅ : i₁ ≠ j ∧ i₂ ≠ j)
        (h₆ : j < cs.size)
        (h₇ : j < as.size) :
    cs[j] = as[j] := by
  grind => finish?
```

Recall that `finish?` replays generated tactics before suggesting them.

The `instantiate` action inspects the generated proof term to decide
which theorems to include as parameters in the `instantiate only [...]`
tactic. However, in some cases, a theorem contributes only by adding a
term to the state. In such cases, the generated tactic cannot be fully
replayed, and the action uses
`instantiate approx [<thms instantiated>]` to indicate which parts of
the tactic script are approximate. The `approx` is just a hint for
users.
2025-10-19 23:35:27 +00:00
Lean stage0 autoupdater
28dd72d514 chore: update stage0 2025-10-19 23:45:51 +00:00
Leonardo de Moura
61ee3b2711 feat: expose optionValue parser (#10839)
This PR exposes the `optionValue` parser used to implement the
`set_option` notation.
2025-10-19 22:57:47 +00:00
Leonardo de Moura
206eb73cd9 feat: finish? tactic for grind interactive mode (#10837)
This PR implements the `finish?` tactic for the `grind` interactive
mode. When it successfully closes the goal, it produces a code action
that allows the user to close the goal using explicit grind tactic
steps, i.e., without any search. It also makes explicit which solvers
have been used.

This is just the first version, we will add many "bells and whistles"
later. For example, `instantiate` steps will clearly show which theorems
have been instantiated.

Example:

```lean
/--
info: Try this:
  [apply] ⏎
    cases #b0f4
    next => cases #50fc
    next => cases #50fc <;> lia
-/
#guard_msgs in
example (p : Nat → Prop) (x y z w : Int) :
    (x = 1 ∨ x = 2) →
    (w = 1 ∨ w = 4) →
    (y = 1 ∨ (∃ x : Nat, y = 3 - x ∧ p x)) →
    (z = 1 ∨ z = 0) → x + y ≤ 6 := by
  grind => finish?
```

The anchors in the generated script are based on stable hash codes.
Moreover, users can hover over them to see the exact term used in the
case split. `grind?` will also be implemented using the new framework.
2025-10-19 03:52:32 +00:00
Leonardo de Moura
09f22203f8 feat: add SolverExtension.action and Solvers.mkAction (#10836)
This PR implements support for `Action` in the `grind` solver extensions
(`SolverExtension`). It also provides the `Solvers.mkAction` function
that constructs an `Action` using all registered solvers. The generated
action is "fair," that is, a solver cannot prevent other solvers from
making progress.
2025-10-19 00:53:45 +00:00
Leonardo de Moura
ef23782608 feat: ring action (#10834)
This PR implements the `ring` action for `grind`.
2025-10-18 22:01:51 +00:00
Leonardo de Moura
e2b5747f4b feat: evalTactic in GrindM (#10833)
This PR implements infrastructure for evaluating `grind` tactics in the
`GrindM` monad. We are going to use it to check whether auto-generated
tactics can effectively close the original goal.
2025-10-18 17:02:36 +00:00
Markus Himmel
dad541265c refactor: move operations on String.Pos.Raw to the String.Pos.Raw namespace (#10735)
This PR moves many operations involving `String.Pos.Raw` to a the
`String.Pos.Raw` namespace with the eventual aim of freeing up the
`String` namespace to contain operations using `String.ValidPos` (to be
renamed to `String.Pos`) instead.

This PR adds the `String.ValidPos.set` and `String.ValidPos.modify`
functions.

After this PR, `String.pos_lt_eq` is no longer a `simp` lemma. Add
`String.Pos.Raw.lt_iff` as a `simp` lemma if your proofs break.
2025-10-18 12:12:55 +00:00
Markus Himmel
ca7a8e18b7 refactor: rename String.split to String.splitToList (#10822)
This PR renames `String.split` to `String.splitToList`, because soon the
name `String.split` will be used by a new implementation which is
superior because it is polymorphic over the pattern kind and it returns
an iterator of slices instead of a list of strings.
2025-10-18 12:12:54 +00:00
Sebastian Ullrich
721ffe5713 chore: CI: disable tree-less clone on nightly release 2025-10-18 13:32:04 +02:00
Leonardo de Moura
c76411d6c5 feat: compact notation for inspecting grind state (#10828)
This PR implements a compact notation for inspecting the `grind` state
in interactive mode. Within a `grind` tactic block, each tactic may
optionally have a suffix of the form `| filter?`.

Examples:

```lean
instantiate | gen > 0  -- Displays terms in the `grind` state after executing `instantiate` with generation greater than zero
```

```lean
instantiate |  -- Displays the `grind` state after executing `instantiate`
```

Remark: If the user places the cursor one space before `|`, the state
*before* executing `instantiate` is displayed.
This PR removes the code that was silently displaying the `grind` state
after each tactic step, as it was too noisy.
It also updates the notation for the `first` combinator in the `grind`
tactic mode to avoid conflicts with the new syntax.
2025-10-17 19:54:23 +00:00
Joachim Breitner
c22100036c fix: more pedantic checking of inaccessible patterns (#10796)
This PR changes match compilation to reject some pattern matches that
were previously accepted due to inaccessible patterns sometimes treated
like accessible ones. Fixes #10794.
2025-10-17 17:02:54 +00:00
Sebastian Ullrich
5800ce17b3 chore: CI: upgrade all git checkouts to tree-less clones (#10814) 2025-10-17 16:23:42 +00:00
Leonardo de Moura
78ab60d045 feat: cases? tactic for grind interactive mode (#10824)
This PR implements the `cases?` tactic for the `grind` interactive mode.
It provides a convenient way to select anchors. Users can filter the
candidates using the filter language. Examples:

<img width="1454" height="399" alt="image"
src="https://github.com/user-attachments/assets/fc370c2e-97f9-4d68-93a6-f0ebf33499f8"
/>

<img width="1447" height="166" alt="image"
src="https://github.com/user-attachments/assets/6c9c3707-79f7-4c63-8007-8d0aaedecc45"
/>
2025-10-17 15:44:19 +00:00
Sofia Rodrigues
f9adafe54d feat: adds acceptSelector and modified selectors (#10667)
This PR adds more selectors for TCP and Signals.

It also fixes a problem with `Selectors` that they cannot be closures
over a promise, otherwise it causes the waiter promise to never be
dropped.
2025-10-17 14:53:46 +00:00
Sebastian Ullrich
69d8d63d58 feat: hint about inaccessible private declaration on dot notation failure (#10803)
This PR improves the error message of generalized field notation if the
issue is that the resolved declaration is not visible in the current
context.
2025-10-17 09:31:56 +00:00
Sebastian Ullrich
dc7c184ee2 chore: CI: introduce fast-ci label 2025-10-17 08:45:41 +02:00
Sebastian Ullrich
e43ff50e76 chore: CI: revert macOS tests accidentally run on PRs 2025-10-17 08:45:41 +02:00
Leonardo de Moura
4ce7ad19ce feat: lia, linarith, and ac actions (#10812)
This PR implements `lia`, `linarith`, and `ac` actions for `grind`
interactive mode.
2025-10-17 03:56:21 +00:00
Leonardo de Moura
2a70da50c1 feat: proper case-split anchor generation in splitNext for grind? and finish? (#10811)
This PR implements proper case-split anchor generation in the
`splitNext` action, which will be used to implement `grind?` and
`finish?`.
2025-10-17 03:07:13 +00:00
Kim Morrison
effde06296 chore: add public modifiers in Lean.Elab.Tactic.Induction (#10810) 2025-10-16 21:52:02 +00:00
Kim Morrison
127fe785a3 chore: add public modifiers in Lean.Elab.Tactic.Ext (#10809)
This PR restores further definitions to `public`, after #10699.
2025-10-16 21:48:41 +00:00
Sebastian Ullrich
663df8f7e8 feat: backward.privateInPublic option (#10807)
This PR introduces the `backward.privateInPublic` option to aid in
porting projects to the module system by temporarily allowing access to
private declarations from the public scope, even across modules. A
warning will be generated by such accesses unless
`backward.privateInPublic.warn` is disabled.
2025-10-16 20:51:45 +00:00
Sebastian Ullrich
428355cf02 chore: remove redundant imports in core (#10750) 2025-10-16 20:27:46 +00:00
Sebastian Ullrich
83126883d9 chore: CI: overhaul check level logic (#10806)
The logic was *still* wrong after two PRs so let's get rid of
`check-level` as a matrix entry and trust in simple bools.
2025-10-16 20:27:02 +00:00
Sebastian Ullrich
5c7b003191 chore: lean.code-workspace: fix terminal cwd (#10802) 2025-10-16 20:19:12 +00:00
Leonardo de Moura
8a1b6e0f71 feat: compress generated grind tactic sequences using <;> (#10808)
This PR implements support for compressing auto-generated `grind` tactic
sequences.
2025-10-16 18:14:33 +00:00
Leonardo de Moura
7087c4a039 feat: add splitNext grind action (#10801)
This PR implements the `splitNext` action for `grind`.
2025-10-16 17:28:14 +00:00
Rob23oba
b7ea66d8d3 fix: consider underscores in getHexNumSize (#10719)
This PR fixes `getHexNumSize` to consider underscores. Previously, only
the amount of bytes was counted, making it output 9 for `1234_abcd`
instead of the actual number of digits, which is 8.
2025-10-16 13:57:58 +00:00
Joachim Breitner
10d6232594 chore: remove test for #10766 (#10804)
the tested situation (kernel runs into deep recursion but elaborator is
happy) is not very stable and depends on, for example, stack size. This
test is not worth that hassle.
2025-10-16 11:11:29 +00:00
Wojciech Różowski
5b35d6192c feat: redefine HashSet.union and add lemmas (#10611)
This PR adds adds union operation on `DHashMap`/`HashMap`/`HashSet` and
their raw variants and provides lemmas about union operations.

---------

Co-authored-by: Paul-Lez <paul.lezeau@gmail.com>
Co-authored-by: Markus Himmel <markus@lean-fro.org>
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-10-16 08:43:01 +00:00
Joachim Breitner
8748031853 fix: only run processInaccessibleAsCtor if there is at least one constructor around (#10793)
This PR fixes #10792.
2025-10-16 08:20:55 +00:00
Marc Huisinga
ac499323af chore: add .vscode/settings.json to .gitignore (#10795)
This PR adds `.vscode/settings.json` to our `.gitignore`, which allows
Lean 4 developers to set local workspace settings. We already use the
the workspace file for settings in core, so this shouldn't cause any
problems.
2025-10-16 07:08:41 +00:00
Kim Morrison
def3c97dbf chore: make extCore and customEliminators public for Batteries (#10799)
This PR restores two declarations to `public`, that were made non-public
in #10699, apparently breaking Batteries.
2025-10-16 05:01:23 +00:00
Kim Morrison
8db3969f87 chore: remove bad grind _=_ annotation on List.contains_iff_mem (#10800) 2025-10-16 04:00:42 +00:00
Leonardo de Moura
2f93363752 feat: intro and assertAll as actions (#10798)
This PR implements the `grind` actions `intro`, `intros`, `assertNext`,
`assertAll`.
2025-10-15 19:47:48 +00:00
Marc Huisinga
4329eae8d4 fix: unknown identifier minimization (#10797)
This PR fixes a bug in the unknown identifier code actions where the
identifiers wouldn't be correctly minimized in nested namespaces. It
also fixes a bug where identifiers would sometimes be minimized to
`[anonymous]`.

The first bug was introduced in #10619.
2025-10-15 19:25:27 +00:00
Leonardo de Moura
114f7e42f1 feat: lazy message with grind state (#10791)
This PR adds a silent info message with the `grind` state in its
interactive mode. The message is shown only when there is exactly one
goal in the grind interactive mode. The condition is a workaround for
current limitations of our `InfoTree`.
2025-10-15 15:03:07 +00:00
Sebastian Ullrich
419982bd42 chore: even more module system fixes and refinements from Mathlib porting (#10726) 2025-10-15 14:59:09 +00:00
Joachim Breitner
8431088c93 fix: preserve error locations when expanding match arms (#10783)
This PR ensures that error messages such as “redundant alternative” have
the right error location even if the arms share their RHS. Fixes #10781.
2025-10-15 13:31:42 +00:00
Sebastian Ullrich
803ec8ff9d chore: CI: re-enable mistakenly deactivated tests for Linux Lake (#10788) 2025-10-15 13:20:26 +00:00
Sebastian Ullrich
c4747752fe fix: detect private references in inferred type of public def (#10762)
This PR fixes an inconsistency in the module system around defs with
elided types.
2025-10-15 12:51:54 +00:00
Joachim Breitner
ed4d453346 refactor: processLeaf: Only look at first alt (#10774)
This PR lets match compilation look only at the first remaining
alternative in `processLeaf`. At this point we have no further variables
we can split on, so if the first one isn’t applicable, match compilation
should fail.
2025-10-15 10:10:52 +00:00
David Thrane Christiansen
45df6fcd37 fix: hovers and docstrings for (co)inductive types (#10738)
This PR fixes a regression introduced by #10307, where hovering the name
of an inductive type or constructor in its own declaration didn't show
the docstring. In the process, a bug in docstring handling for
coinductive types was discovered and also fixed. Tests are added to
prevent the regression from repeating in the future.
2025-10-15 09:32:11 +00:00
Sebastian Graf
4077bf2c05 feat: implement mvcgen?, expanding to mvcgen invariants? (#10782)
This PR implements a hint tactic `mvcgen?`, expanding to `mvcgen
invariants?`

Example:
```
/--
info: Try this:
  [apply] mvcgen invariants?
---
info: Try this:
  [apply] mvcgen [mySum] invariants?
---
info: Try this:
  [apply] mvcgen +elimLets invariants?
---
info: Try this:
  [apply] mvcgen +elimLets [mySum] invariants?
-/
#guard_msgs (info) in
theorem mySum_suggest_invariant_short (l : List Nat) : mySum l = l.sum := by
  generalize h : mySum l = r
  apply Id.of_wp_run_eq h
  mvcgen?
  mvcgen? [mySum]
  mvcgen? +elimLets
  mvcgen? +elimLets [mySum]
  all_goals admit
```
2025-10-15 08:22:09 +00:00
Joachim Breitner
54a3fbf88f fix: improve error message when decide +kernel fails (#10780)
This PR improves the error message when `decide +kernel` fails in the
kernel, but not the elaborator. Fixes #10766.
2025-10-15 07:11:27 +00:00
Leonardo de Moura
746206c5e6 feat: hover information for grind anchors (#10779)
This PR implements hover information for `grind` anchors. Anchors are
stable hash codes for referencing terms in the grind state. The anchors
will be used when auto generating tactic scripts. The hover display the
following information:

1- In the `instantiate` tactic, it displays the type of the theorem
being instantiated.
<img width="952" height="125" alt="image"
src="https://github.com/user-attachments/assets/be949b87-cf9b-4f75-abe0-17751295de93"
/>

2- In the `cases` tactic, the hover information depends on the kind of
case-split.
  a) Proposition
<img width="1019" height="125" alt="image"
src="https://github.com/user-attachments/assets/253e2927-f18e-49ab-a8fc-2144657406d8"
/>

b) A hypotheses. In this case, you can opt to replace the anchor with
the hypothesis' name if it is accessible.
<img width="1019" height="178" alt="image"
src="https://github.com/user-attachments/assets/858b3751-4ef9-492d-a42f-c0743753a7de"
/>

c) A term. The hover displays just the type, by `grind` logs a silent
information with additional information
  
<img width="1376" height="148" alt="image"
src="https://github.com/user-attachments/assets/30078ca4-a886-49d9-912e-866f3567b0da"
/>
2025-10-15 02:43:11 +00:00
Leonardo de Moura
88141a0a49 feat: hygiene for grind interactive mode (#10778)
This PR ensures that `grind` interactive mode is hygienic. It also adds
tactics for renaming inaccessible names: `rename_i h_1 ... h_n` and
`next h_1 ... h_n => ..`, and `expose_names` for automatically generated
tactic scripts. The PR also adds helper functions for implementing
case-split actions.
2025-10-15 01:27:51 +00:00
Kim Morrison
b17afe0f06 feat: improvements to release automation (#10777)
This PR improves the scripts assisting with cutting Lean releases (by
reporting CI status of open PRs, and adding documentation), and adds a
`.claude/commands/release.md` prompt file so Claude can assist.
2025-10-15 00:28:26 +00:00
Paul Reichert
7632cefa87 feat: hash map iterators (#10761)
This PR provides iterators on hash maps.
2025-10-14 15:10:01 +00:00
Paul Reichert
7a47bfa208 feat: flatMap iterator combinator (#10728)
This PR introduces the `flatMap` iterator combinator. It also adds
lemmas relating `flatMap` to `toList` and `toArray`.
2025-10-14 12:50:54 +00:00
Sebastian Ullrich
ae6335f115 chore: demote Intel macOS to Tier 2 platform (#10770) 2025-10-14 12:10:06 +00:00
Paul Reichert
f58999a7a6 refactor: use Shrink stub in the iterator framework (#10725)
This PR introduces a no-op version of `Shrink`, a type that should allow
shrinking small types into smaller universes given a proof that the type
is small enough, and uses it in the iterator library. Because this type
would require special compiler support, the current version is just a
wrapper around the inner type so that the wrapper is equivalent, but not
definitionally equivalent.

While `Shrink` is unable to shrink universes right now, but introducing
it now will allow us to generalize the universes in the iterator library
with fewer breaking changes as soon as an actual `Shrink` is possible.
2025-10-14 10:22:14 +00:00
Lean stage0 autoupdater
888b59bf95 chore: update stage0 2025-10-14 08:04:41 +00:00
Markus Himmel
1dae353575 chore: duplicate some String functions ahead of deprecation (#10768)
This PR is split off from #10735 for boring bootstrapping reasons.
2025-10-14 07:36:05 +00:00
Leonardo de Moura
a4b788c332 feat: add Grind/Action.lean (#10767)
This PR implements the new control interface for implementing `grind`
search strategies. It will replace the `SearchM` framework.
2025-10-14 03:21:51 +00:00
Sebastian Ullrich
5865c41a76 chore: lean.code-workspace: always open terminal in root folder (#10745) 2025-10-13 14:12:35 +00:00
Marc Huisinga
4b0e8d88ce fix: don't display CSS color picker in Lean files in VS Code (#10757)
This PR fixes a bug in combination with VS Code where Lean code that
looks like CSS color codes would display a color picker decoration.

VS Code displays this decoration by default for all languages, not just
CSS. Due to https://github.com/microsoft/vscode/issues/91533, this
setting cannot be disabled in the client on a per-language basis.
However, we can override the default behavior by providing a color
provider of our own. This PR implements an empty color provider to
override the VS Code one.
2025-10-13 13:39:16 +00:00
Marc Huisinga
9d427fdfcf feat: "try this" messages with support for interactivity (#10524)
This PR adds support for interactivity to the combined "try this"
messages that were introduced in #9966. In doing so, it moves the link
to apply a suggestion to a separate `[apply]` button in front of the
suggestion. Hints with diffs remain unchanged, as they did not
previously support interacting with terms in the diff, either.

<img width="379" height="256" alt="Suggestion with interactive message"
src="https://github.com/user-attachments/assets/7838ebf6-0613-46e7-bc88-468a05acbf51"
/>
2025-10-13 13:39:03 +00:00
Kim Morrison
fe1e7d56f4 chore: restore #8656 (#10758)
This PR restores the change in #8656, which removed `autoImplicit =
false` from the default lake template (per previous discussions linked
there). This was accidentally reverted in #8866.
2025-10-13 10:34:01 +00:00
Markus Himmel
fbe98d76b2 fix: turn meta import into import in Init.Data.ToString (#10754)
This PR makes sure that we always properly import
`Init.Data.ToString.Name` when importing `Init`.
2025-10-13 09:20:48 +00:00
Joachim Breitner
9a5e425990 refactor: no public section in Elab.Induction (#10699)
This PR removes `public section` in `Elab.Induction`.
2025-10-13 09:02:36 +00:00
Leonardo de Moura
14ff08db6f feat: repeat tactical for grind interactive mode (#10748)
This PR implements the `repeat` tactical for the `grind` interactive
mode.
2025-10-12 22:05:58 +00:00
Sebastian Ullrich
316859e871 perf: reset InfoState.lazyAssignment before each command (#10744)
This PR fixes a performance regression introduced in #10518. More
generally, it ensures both message log and info state are per-command,
which has been the case in practice ever since the asynchronous language
driver was introduced.
2025-10-12 09:27:14 +00:00
Leonardo de Moura
47dbcd4b93 feat: finish? and grind? infrastructure (#10747)
This PR implements infrastructure for `finish?` and `grind?` tactics.
2025-10-12 02:48:16 +00:00
Leonardo de Moura
4f7d3bb692 feat: instantiate tactic parameters (#10746)
This PR implements parameters for the `instantiate` tactic in the
`grind` interactive mode. Users can now select both global and local
theorems. Local theorems are selected using anchors. It also adds the
`show_thms` tactic for displaying local theorems. Example:

```lean
example (as bs cs : Array α) (v₁ v₂ : α)
        (i₁ i₂ j : Nat)
        (h₁ : i₁ < as.size)
        (h₂ : bs = as.set i₁ v₁)
        (h₃ : i₂ < bs.size)
        (h₃ : cs = bs.set i₂ v₂)
        (h₄ : i₁ ≠ j ∧ i₂ ≠ j)
        (h₅ : j < cs.size)
        (h₆ : j < as.size)
        : cs[j] = as[j] := by
  grind =>
    instantiate = Array.getElem_set
    instantiate Array.getElem_set
```
2025-10-11 21:35:21 +00:00
Lean stage0 autoupdater
0dc862e3ed chore: update stage0 2025-10-11 05:57:21 +00:00
Mac Malone
d9ee24bf36 fix: lake: local cache w/ --old (#10741)
This PR fixes a bug where partially up-to-date files built with `--old`
could be stored in the cache as fully up-to-date. Such files are no
longer cached. In addition, builds without traces now only perform an
modification time check with `--old`. Otherwise, they are considered
out-of-date.
2025-10-11 02:20:31 +00:00
Mac Malone
0639d49a4c feat: scope output cache by platform & toolchain (#10730)
This PR changes the Lake's remote cache interface to scope cache outputs
by toolchain and/or platform were useful.

Packages that set `platformIndependent = true` will not be scoped by
platform and the core build (i.e., `bootstrap = true`) will not be
scoped by toolchain. Lake's detected platform and toolchain can be
overridden with the new `--platform` and `--toolchain` options to `cache
get` and `cache put`.

Lake no longer accepts the `--scope` option when using `cache get` with
Reservoir.. The `--repo` option must be used instead.
2025-10-11 02:17:39 +00:00
Lean stage0 autoupdater
3a26eb7281 chore: update stage0 2025-10-10 22:22:55 +00:00
Joachim Breitner
830be29422 feat: generate equational theorems uniformly (#10734)
This PR follows upon #10606 and creates equational theorems uniformly
from the unfold theorem, there is only one handler registered in
`registerGetEqnsFn`.

For now we keep `registerGetEqnsFn`, because it’s used by mathlib’s
`irreducible_def`, but I’d like to get rid of it in the long term,
relying only on `registerGetUnfoldEqnFn` for constructions that should
unfold differently.
2025-10-10 21:35:09 +00:00
Leonardo de Moura
2a8c03109a feat: improve ac, linarith, lia, and ring in grind interactive mode (#10740)
This PR improves the tactics `ac`, `linarith`, `lia`, `ring` tactics in
`grind` interactive mode. They now fail if no progress has been made.
They also generate an info message with counterexample/basis if the goal
was not closed.
2025-10-10 21:04:26 +00:00
Leonardo de Moura
07f8ab533c feat: add tactics to grind interactive mode (#10737)
This PR adds the tactics `linarith`, `ac`, `fail`, `first`, `try`,
`fail_if_success`, and `admit` to `grind` interactive mode.
2025-10-10 20:24:07 +00:00
Paul Reichert
a73ebe8a77 feat: any/all predicates for iterators (#10686)
This PR introduces `any`, `anyM`, `all` and `allM` for pure and monadic
iterators. It also provides lemmas about them.
2025-10-10 19:24:10 +00:00
Paul Reichert
3931a72573 feat: SInt ranges (#10633)
This PR provides range support for the signed finite number types
`Int{8,16,32,64}` and `ISize`. The proof obligations are handled by
reducing all of them to proofs about an internal `UpwardEnumerable`
instance for `BitVec` interpreted as signed numbers.
2025-10-10 17:07:20 +00:00
Wojciech Różowski
bf809b5298 chore: change the location of error message for coinductive predicates (#10722)
This PR changes where errors are displayed when trying to use
`coinductive` keyword when targeting things that do not live in `Prop`.
Instead of displaying the error above the first element of the mutual
block, it is displayed above the erroneous definition.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2025-10-10 16:06:18 +00:00
Joachim Breitner
4b6f07060d feat: remove support for reducible well-founded recursion (#10714)
This PR removes support for reducible well-founded recursion, a Breaking
Change. Using `@[semireducible]` on a definition by well-founded
recursion prints a warning that this is no longer effective.

With the upcoming module system, proofs are often not available. With
this change, we remove a fringe use case hat may require proofs, and
that would not be supported under the module system anyways.

At least for now, direct use of `WellFounded.fix` is not affected.

This fixes: #5192
2025-10-10 15:48:28 +00:00
David Thrane Christiansen
09092549d0 fix: Verso docstring semantic highlighting fixes (#10662)
This PR re-enables semantic tokens for Verso docstrings, after a prior
change accidentally disabled them. It also adds a test to prevent this
from happening again.

In the process, it became clear that there was a bug. The highlighting
strategy led to overlapping but not identical tokens, but the code had
previously assumed that this couldn't happen at the delta-encoding step.
So this PR additionally replaces the removal of duplicate tokens with
priority-based handling of overlapping tokens.

---------

Co-authored-by: Marc Huisinga <mhuisi@protonmail.com>
2025-10-10 11:57:02 +00:00
Joachim Breitner
1b4360c32a fix: unfold more auxillary theorems in termination checking (#10733)
This PR unfolds auxillary theorems more aggressively during termination
checking. This fixes #10721.
2025-10-10 11:09:28 +00:00
Cameron Zwarich
705dac4f77 chore: make @hargoniX code owner of the compiler (#10732) 2025-10-10 04:43:38 +00:00
Leonardo de Moura
3bab621364 feat: add grind interactive mode tactics (#10731)
This PR adds the following tactics to the `grind` interactive mode:
- `focus <grind_tac_seq>`
- `next => <grind_tac_seq>`
- `any_goals <grind_tac_seq>`
- `all_goals <grind_tac_seq>`
- `grind_tac <;> grind_tac`
- `cases <anchor>`
- `tactic => <tac_seq>`

Example:
```lean
def g (as : List Nat) :=
  match as with
  | []      => 1
  | [_]     => 2
  | _::_::_ => 3

example : g bs = 1 → g as ≠ 0 := by
  grind [g.eq_def] =>
    instantiate
    cases #ec88
    next => instantiate
    next => finish
    tactic =>
      rw [h_2] at h_1
      simp [g] at h_1
```
2025-10-10 01:17:37 +00:00
Sebastian Ullrich
526ab9caff feat: Verso and Shake (#10657)
This PR ensures Shake does not remove any imports required by Verso
docstrings
2025-10-09 16:40:29 +00:00
Rob23oba
71ddf227d2 doc: add a recommended spelling for HEq (#10717)
This PR adds a recommended spelling for heterogenous equality (`HEq`,
`≍`).
2025-10-09 10:10:23 +00:00
Markus Himmel
dca8d6d188 refactor: discipline around arithmetic of String.Pos.Raw (#10713)
This PR enforces rules around arithmetic of `String.Pos.Raw`.

Specifically, it adopts the following conventions:

- Byte indices ("ordinals") in strings should be represented using
`String.Pos.Raw`
- Amounts of bytes ("cardinals") in strings should be represented using
`Nat`.

For example, `String.Slice.utf8ByteSize` now returns `Nat` instead of
`String.Pos.Raw`, and there is a new function `String.Slice.rawEndPos`.

Finally, the `HAdd` and `HSub` instances for `String.Pos.Raw` are
reorganized. This is a **breaking change**.

The `HAdd/HSub String.Pos.Raw String.Pos.Raw String.Pos.Raw` instances
have been removed. For the use case of tracking positions relative to
some other position, we instead provide `offsetBy` and `unoffsetBy`
functions. For the use case of advancing/unadvancing a position by an
arbitrary number of bytes, we instead provide `increaseBy` and
`decreaseBy` functions. For
offsetting/unoffsetting/advancing/unadvancing a position `p` by the size
of a string `s` (resp. character `c`), use `s + p`/`p - s`/`p + s`/`p -
s` (resp. `c + p`/`p - c`/`p + c`/`p - c`).
2025-10-09 07:47:45 +00:00
Rob23oba
6f1e932542 fix: make IO.sleep opaque (#10718)
This PR makes the function `IO.sleep` opaque. Previously, the definition
of `IO.sleep` made it definitionally equivalent to `pure ()`.
2025-10-09 07:37:11 +00:00
Sebastian Graf
c32a57e580 feat: revert "feat: disable "experimental" warning for mvcgen (#10638)" (#10720)
This PR re-enables the "experimental" warning for `mvcgen` by changing
its default. The official release has been postponed to justify small
breaking changes in the semantic foundations in the near future.
2025-10-09 06:31:18 +00:00
Lean stage0 autoupdater
aa86d95c08 chore: update stage0 2025-10-08 22:00:53 +00:00
Leonardo de Moura
f9e140838e feat: hexnum parser (#10716)
This PR adds a new helper parser for implementing parsers that contain
hexadecimal numbers. We are going to use it to implement anchors in the
`grind` interactive mode.
2025-10-08 21:12:03 +00:00
Leonardo de Moura
98a6fa1ac7 feat: improve grind anchors computation (#10715)
This PR improves anchor stability (aka stable hash codes) used to
reference terms in a `grind` goal.
2025-10-08 17:44:55 +00:00
Sebastian Ullrich
11be7e8f4a chore: use lld if available for building core (#10694) 2025-10-08 16:47:30 +00:00
Lean stage0 autoupdater
a89463bf9e chore: update stage0 2025-10-08 16:51:08 +00:00
Sofia Rodrigues
7600d41c90 fix: add cancel function to the Timer API to make it behave correctly with finalizers and selectables (#10630)
This PR aims to fix the Timer API selector to make it finish as soon as
possible when unregistered. This change makes the `Selectable.one`
function drop the `selectables` array as soon as possible, so when
combined with finalizers that have some effects like the TCP socket
finalizer, it runs it as soon as possible.
2025-10-08 16:14:39 +00:00
Marc Huisinga
80b8e44072 test: fix test flakiness (#10680)
This PR fixes several causes of test flakiness and re-enables the tests
that were disabled in #10665, #10669 and #10673.

Specifically, it fixes:
- A race condition in the file worker that caused it to report an
incomplete snapshot prefix in the inlay hint request (confirmed to be
the cause of #10665)
- A bug in the test runner where it didn't correctly account for
non-deterministic message ordering inducing different RPC pointer
numbering (confirmed to be the cause of #10673)
- A race condition in the watchdog that would sometimes cause the module
hierarchy to be empty (likely the cause of #10669, but not confirmed as
this issue only reproduced again once in tens of thousands of test runs
on various machines, including CI)
- An unrelated bug in the module hierarchy implementation that would
cause it to report an empty module hierarchy when the file was changed

It also replaces some calls to `Task.get` in the language server with
`IO.wait` to protect the code against unfortunate compiler re-ordering.
2025-10-08 13:33:56 +00:00
Sebastian Ullrich
1d989523d4 fix: simp should not pick up inaccessible definitional equations (#10696)
Fixes #10671
2025-10-08 12:48:35 +00:00
Sebastian Ullrich
3b061a0996 chore: more module system fixes and improvements from Mathlib porting (#10655) 2025-10-08 11:30:09 +00:00
Marc Huisinga
1b1c802362 feat: auto-completion for end names (#10660)
This PR adds auto-completion for identifiers after `end`. It also fixes
a bug where completion in the whitespace after `set_option` would not
yield the full option list.

Closes #3885.

### Breaking changes

The `«end»` syntax is adjusted to take an `identWithPartialTrailingDot`
instead of an `ident`.
2025-10-08 11:12:05 +00:00
Joachim Breitner
50c19f704b fix: Let MVarId.cleanup chase local declarations (#10712)
This PR lets `MVarId.cleanup` chase local declarations (a bit as if they
were equalities). Fixes #10710.
2025-10-08 10:49:14 +00:00
Mac Malone
bbc194b733 feat: USE_LAKE_CACHE CMake option (#10708)
This PR adds the `USE_LAKE_CACHE` option to the core CMake build
(defaults to `OFF`). When enabled, the Lake artifact cache will be
enabled (via `enableArtifactCache`) for stage 1 builds (which includes
interactive use).
2025-10-08 08:56:53 +00:00
Leonardo de Moura
4e7a2b2371 feat: anchors for referencing terms in the grind state (#10709)
This PR implements *anchors* (also known as stable hash codes) for
referencing terms occurring in a `grind` goal. It also introduces the
commands `show_splits` and `show_state`. The former displays the anchors
for candidate case splits in the current `grind` goal.
2025-10-08 02:51:21 +00:00
Mac Malone
215bc30296 feat: lake: allowImportAll configuration option (#9855)
This PR adds a new `allowImportAll` configuration option for packages
and libraries. When enabled by an upstream package or library,
downstream packages will be able to `import all` modules of that package
or library. This enables package authors to selectively choose which
`private` elements, if any, downstream packages may have access to.
2025-10-08 02:47:35 +00:00
Leonardo de Moura
b00d1f933f feat: make finish fail when the goal is not closed (#10707)
This PR ensures the `finish` tactic in `grind` interactive mode fails
and reports diagnostics when goal is not closed.
2025-10-07 20:34:19 +00:00
Leonardo de Moura
5ba0f8b885 feat: have tactic for grind interactive mode (#10706)
This PR adds the `have` tactic for the `grind` interactive mode.
Example:
```lean
example {a b c d e : Nat}
    : a > 0 → b > 0 → 2*c + e <= 2 → e = d + 1 → a*b + 2 > 2*c + d := by
  grind =>
    have : a*b > 0 := Nat.mul_pos h h_1
    lia
```
2025-10-07 20:06:16 +00:00
François G. Dorais
43da17aa7f feat: add forall_fin_zero and exists_fin_zero (#10627)
This PR adds lemmas `forall_fin_zero` and `exists_fin_zero`. It also
marks lemmas `forall_fin_zero`, `forall_fin_one`, `forall_fin_two`,
`exists_fin_zero`, `exists_fin_one`, `exists_fin_two` with `simp`
attribute.

Closes #10629
2025-10-07 18:50:23 +00:00
Wojciech Różowski
0195fdf9aa feat: add coinductive command to specify coinductive predicates (#10333)
This PR introduces a `coinductive` keyword, that can be used to define
coinductive predicates via a syntax identical to the one for `inductive`
keyword. The machinery relies on the implementation of elaboration of
inductive types and extracts an endomap on the appropriate space of the
predicates from the definition that is then fed to the
`PartialFixpoint`. Upon elaborating definitions, all the constructors
are declared through automatically generated lemmas.

For example, infinite sequence of transitions in a relation, can be
given by the following:
```lean4
section
variable (α : Type)
coinductive infSeq (r : α → α → Prop) : α → Prop where
  | step : r a b → infSeq r b → infSeq r a
  
/--
info: infSeq.coinduct (α : Type) (r : α → α → Prop) (pred : α → Prop) (hyp : ∀ (x : α), pred x → ∃ b, r x b ∧ pred b)
  (x✝ : α) : pred x✝ → infSeq α r x✝
-/
#guard_msgs in
#check infSeq.coinduct

/--
info: infSeq.step (α : Type) (r : α → α → Prop) {a b : α} : r a b → infSeq α r b → infSeq α r a
-/
#guard_msgs in
#check infSeq.step
end
```
The machinery also supports `mutual` blocks, as well as mixing inductive
and coinductive predicate definitions:
```lean4
mutual
  coinductive tick : Prop where
  | mk : ¬tock → tick

  inductive tock : Prop where
  | mk : ¬tick → tock
end

/--
info: tick.mutual_induct (pred_1 pred_2 : Prop) (hyp_1 : pred_1 → pred_2 → False) (hyp_2 : (pred_1 → False) → pred_2) :
  (pred_1 → tick) ∧ (tock → pred_2)
-/
#guard_msgs in
#check tick.mutual_induct
```

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-10-07 18:04:51 +00:00
Joachim Breitner
5a751d4688 fix: induction: do not allow generalizing variables occurring in the using clause (#10697)
This PR lets `induction` print a warning if a variable occurring in the
`using` clause is generalized. Fixes #10683.
2025-10-07 15:38:34 +00:00
Lean stage0 autoupdater
486d93c5fd chore: update stage0 2025-10-07 13:47:20 +00:00
François G. Dorais
8cebe691a2 fix: Nat.and_distrib_right -> Nat.and_or_distrib_right (#10649)
This PR renames `Nat.and_distrib_right` to `Nat.and_or_distrib_right`.
This is to make the name consistent with other theorems in the same file
(e.g. `Nat.and_or_distrib_left`).
2025-10-07 12:57:46 +00:00
Joachim Breitner
8655f7706f refactor: structural recursion: prove .eq_def directly (#10606)
This PR changes how Lean proves the equational theorems for structural
recursion. The core idea is to let-bind the `f` argument to `brecOn` and
rewriting `.brecOn` with an unfolding theorem. This means no extra case
split for the `.rec` in `.brecOn` is needed, and `simp` doesn't change
the `f` argument which can break the definitional equality with the
defined function. With this, we can prove the unfolding theorem first,
and derive the equational theorems from that, like for all other ways of
defining recursive functions.

Backs out the changes from #10415, the old strategy works well with the
new goals.

Fixes #5667
Fixes #10431
Fixes #10195
Fixes #2962
2025-10-07 12:53:09 +00:00
Yuri de Wit
5c92ffc64d doc: fix url to profile.ts source (#10628)
This PR fixes a broken link to the firefox profile definitions in one of
the comments.

The `profile.js` file was renamed to `profile.ts` while the rest of the
url remained the same.
2025-10-07 12:41:04 +00:00
Sebastian Ullrich
ca7e7c4279 fix: do not discard mutual members on macro use (#10695)
This PR fixes an issue where non-`macro` members of a `mutual` block
were discarded if there was at least one macro present.

Fixes #10687
2025-10-07 12:04:04 +00:00
dependabot[bot]
13c38f64a5 chore: CI: bump softprops/action-gh-release from 2.3.2 to 2.3.3 (#10646)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-07 11:42:02 +00:00
dependabot[bot]
b59959ddab chore: CI: bump actions/stale from 9 to 10 (#10647)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-07 11:41:31 +00:00
dependabot[bot]
8f9c27cc06 chore: CI: bump actions/github-script from 7 to 8 (#10648)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-07 11:41:04 +00:00
Sebastian Ullrich
715c53d92e chore: Modulize: put section below first module doc (#10693) 2025-10-07 09:10:42 +00:00
Sebastian Graf
7a9d769444 chore: fix the docstring of PredTrans.conjunctive (#10691) 2025-10-07 08:56:13 +00:00
Sebastian Ullrich
15636a347f fix: induction incrementality on removal of extraneous case (#10679)
This PR fixes an issue where "Invalid alternative name" errors from
`induction` stick around after removing the offending alternative.
2025-10-07 08:24:41 +00:00
Sebastian Ullrich
1ecdf8ddfa chore: simplify and extend Modulize.lean (#10692)
Take explicit list of files instead of asking Lake, take `--meta` flag
instead of guessing based on module name.
2025-10-07 08:22:52 +00:00
Chris Henson
54c6efea95 doc: typo in docstring of Std.Time.DateTime.now (#10668)
This PR fixes a duplicated docstring for `Std.Time.DateTime.now`.
2025-10-07 04:55:31 +00:00
Leonardo de Moura
b13f7e25ec feat: add show_* and instantiate grind tactics (#10690)
This PR adds the `instantiate`, `show_true`, `show_false`,
`show_asserted`, and `show_eqcs` tactics for the `grind` interactive
mode. The `show` tactic take an optional "filter" and are used to probe
the `grind` state. Example:
```lean
example (as bs cs : Array α) (v₁ v₂ : α)
        (i₁ i₂ j : Nat)
        (h₁ : i₁ < as.size)
        (h₂ : bs = as.set i₁ v₁)
        (h₃ : i₂ < bs.size)
        (h₃ : cs = bs.set i₂ v₂)
        (h₄ : i₁ ≠ j ∧ i₂ ≠ j)
        (h₅ : j < cs.size)
        (h₆ : j < as.size)
        : cs[j] = as[j] := by
  grind =>
    instantiate
    -- Display asserted facts with `generation > 0`
    show_asserted gen > 0
    -- Display propositions known to be `True`, containing `j`, and `generation > 0`
    show_true j && gen > 0
    -- Display equivalence classes with terms that contain `as` or `bs`
    show_eqcs as || bs
    instantiate
```

This PR also fixes a bug in the `grind` interactive mode initialization
procedure.
2025-10-07 03:36:22 +00:00
Sofia Rodrigues
6964a15b5d feat: add Std.CancellationToken type (#10510)
This PR adds a `Std.CancellationToken` type
2025-10-07 03:21:45 +00:00
Sofia Rodrigues
ad701b577b feat: add StreamMap (#10400)
This PR adds the StreamMap type that enables multiplexing in
asynchronous streams.

This PR depends on: #10366, #10367 and #10370.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-10-06 23:39:44 +00:00
Henrik Böving
1f7374a5d6 fix: RC dec insertion for unused variables (#10689)
This PR fixes an oversight in the RC insertion phase in the code
generator.

If the code generator encounters a `let` that is unused (which is
perfectly reasonable as at this
phase we are in an impure IR and as such allow for side effects to
happen so we cannot remove all
unused `let`) it didn't insert a `dec` instruction for this variable.
This has previously gone
unnoticed because at this point in the compiler basically all unused
lets are removed already
anyways. However with the `IO`/`ST` token erasure coming up they will be
very frequent.
2025-10-06 22:05:17 +00:00
Mac Malone
aa3d409eb6 refactor: lake: mv tests/examples to top-level tests dir (#10688)
This PR moves Lake's test infrastructure from `src/lake` to
`tests/lake`.
2025-10-06 21:47:57 +00:00
Paul Reichert
7771b8079c refactor: improve naming in the range API (#10537)
This PR renames some declarations in the range API for better
consistency and readability. For example,
`UpwardEnumerable.succMany?_succ?` is now called `succMany?_add_one`, in
order to (a) correct the erroneous use of `succ?` instead of `succ`
(=`Nat.succ`) and (b) distinguish the successor of natural numbers
(`add_one`) from the successor of the upward-enumerable type (`succ?` or
`succ`).
2025-10-06 20:51:09 +00:00
Mac Malone
43d4c8fe9f feat: IO.FS.hardLink (#10676)
This PR adds the `IO.FS.hardLink` function, which can be used to create
hard links.

This is implemented via libuv's `uv_fs_link` function.

Lake hopes to make use of this function to decrease the storage cost of
restoring artifacts.

This PR also fixes some C implementation issues found in nearby similar
functions.
2025-10-06 18:22:07 +00:00
Sofia Rodrigues
4898f28c12 feat: add Std.Broadcast type (#10369)
This PR adds a multi-consumer, multi-producer channel to Std.Sync.

This PR depends on: #10366, #10367 and #10370.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-10-06 17:47:18 +00:00
Paul Reichert
16400e2aa3 feat: add lemmas about iterator fold and map interactions (#10653)
This PR adds equational lemmas about (filter-)mapping and then folding
iterators.
2025-10-06 16:12:13 +00:00
Markus Himmel
d228cd3edd feat: LT and LE instances on new position types (#10685)
This PR introduces `LT` and `LE` instances on `String.ValidPos` and
`String.Slice.Pos`.
2025-10-06 16:06:16 +00:00
Joachim Breitner
232a0495b0 chore: remove public section from end of files (#10684)
This PR removes `public section` lines from end of files; they look a
bit silly there.
2025-10-06 13:30:48 +00:00
Joachim Breitner
30f41fe542 fix: instance name for deriving ToExpr (#10682)
This PR changes the instance name for `deriving ToExpr` to be consistent
with other derived instance since #10271. Fixes #10678.
2025-10-06 11:46:46 +00:00
Leonardo de Moura
fbfb0757ca feat: grind interactive mode basic tactics (#10677)
This PR implements the basic tactics for the new `grind` interactive
mode. While many additional `grind` tactics will be added later, the
foundational framework is already operational. The following `grind`
tactics are currently implemented: `skip`, `done`, `finish`, `lia`, and
`ring`.
This PR also removes the notion of `grind` fallback procedure since it
is subsumed by the new framework. Examples:
```lean
example (x y : Nat) : x ≥ y + 1 → x > 0 := by
  grind => skip; lia; done

open Lean Grind

example [CommRing α] (a b c : α)
  : a + b + c = 3 →
    a^2 + b^2 + c^2 = 5 →
    a^3 + b^3 + c^3 = 7 →
    a^4 + b^4 + c^4 = 9 := by
  grind => ring
```
2025-10-06 01:08:26 +00:00
Sebastian Ullrich
ffb6142ee7 chore: CI: update macOS images (#10666) 2025-10-05 16:06:03 +00:00
Marc Huisinga
7b3c22cebb test: disable flaky interactive diag tests (#10673) 2025-10-05 09:38:41 +00:00
Lean stage0 autoupdater
1ac81c6a7a chore: update stage0 2025-10-05 02:59:23 +00:00
Mac Malone
662dc10447 fix: lake: outdated traces w/ cache (#10672)
This PR fixes an issue with the Lake artifact cache where trace files
were not correctly updated when switching between different cached
builds.
2025-10-05 00:44:43 +00:00
Marc Huisinga
7688919765 test: temporarily disable all new tests that use waitForILeans (#10669)
Due to the flaky test failure at
https://github.com/leanprover/lean4/actions/runs/18241144163/job/51943212141
2025-10-04 09:25:43 +00:00
Rob23oba
5d3df7b5f4 fix: some ExtraModUses (#10620)
This PR records extra mod uses that previously caused wrong unnecessary
import reports from shake.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-10-03 15:50:40 +00:00
Marc Huisinga
643da1ea1b test: disable flaky tests (#10665) 2025-10-03 14:31:39 +00:00
Marc Huisinga
3d75c2ce2b fix: eliminate potential source of inlay hint flakiness (#10664)
This PR fixes one potential source of inlay hint flakiness.

In the old `IO.waitAny` implementation, we could rely on the fact that
if all tasks in the list were finished, `IO.waitAny` would pick the
first finished one. In the new implementation (#9732), this isn't the
case anymore for fairness reasons, but this also means that in
`IO.AsyncList.getFinishedPrefixWithTimeout`, it can happen that we don't
scan the full finished command snapshot prefix because we pick the
timeout task before the finished snapshot task. This is likely the cause
of a flaky test failure
[here](https://github.com/leanprover/lean4/actions/runs/18215430028/job/51863870111),
where the inlay hint test yielded no result (the timeout task has an
edit delay of 0ms in the first inlay hint request that is emitted,
finishes immediately and can thus immediately cause the finished prefix
to be skipped with the new `waitAny` implementation).

This PR fixes this issue by adding a `hasFinished` check before the
`waitAny` to ensure that we always scan the finished prefix and don't
need to rely on a brittle invariant that doesn't hold anymore. It also
converts some `Task.get`s to `IO.wait` for safety so that the compiler
can't re-order them.
2025-10-03 10:54:36 +00:00
David Thrane Christiansen
b979fa012b fix: verso docstring {name} role suggestion overload (#10663)
This PR disables `{name}` suggestions for `.anonymous` and adds syntax
suggestions.

When the provided name can't be resolved, the `{name}` role suggests
fully-qualified variants. But if the name is a syntax error, it
attempted to suggest names that had `.anonymous` as a suffix; the
resulting list of suggestions of all names in Lean's environment
overloaded the language server.
2025-10-03 09:33:53 +00:00
Sebastian Ullrich
288b7d2023 chore: further cleanup from shaking Init (#10658) 2025-10-02 17:29:00 +00:00
Markus Himmel
5c707d936c chore: rename Stream to Std.Stream (#10645)
This PR renames `Stream` to `Std.Stream` so that the name becomes
available to mathlib after a deprecation cycle.
2025-10-02 15:25:56 +00:00
Joachim Breitner
5a2e46b021 fix: equational theorem generation: avoid reducing at transparency all (#10654)
This PR avoid reducing at transparency all in equational theorem
generation. Fixes #10651.
2025-10-02 13:55:32 +00:00
Sebastian Graf
24c86fc05d fix: improve error message for mstart when goal is not a Prop (#10650)
This PR improves the error message for `mstart` when the goal is not a
`Prop`.
2025-10-02 08:46:29 +00:00
Sebastian Ullrich
d17160518c chore: module system fixes and refinements from Mathlib porting (#10643) 2025-10-02 08:28:08 +00:00
Paul Reichert
89686fcd02 refactor: replace PRange shape α with Rcc α and eight other types (#10319)
This PR "monomorphizes" the structure `Std.PRange shape α`, replacing it
with nine distinct structures `Std.Rcc`, `Std.Rco`, `Std.Rci` etc., one
for each possible shape of a range's bounds. This change was necessary
because the shape polymorphism is detrimental to attempts of automation.

**BREAKING CHANGE:** While range/slice notation itself is unchanged,
this essentially breaks the entire remaining (polymorphic) range and
slice API except for the dot-notation(`toList`, `iter`, ...). It is not
possible to deprecate old declarations that were formulated in a
shape-polymorphic way that is not available anymore.
2025-10-02 06:45:11 +00:00
David Thrane Christiansen
0b2193c771 chore: docstring review for ByteArray (#10632)
This PR adds missing docstrings for ByteArray and makes existing ones
consistent with our style.
2025-10-02 04:20:18 +00:00
David Thrane Christiansen
2c6576b269 chore: missing docstring + style updates for String docs (#10640)
This PR adds a missing docstring and applies our style guide to parts of
the String API.
2025-10-02 04:19:55 +00:00
Markus Himmel
2cca32ccc3 chore: use UTF8 instead of Utf8 in identifiers (#10636)
This PR renames `String.getUtf8Byte` to `String.getUTF8Byte` in order to
adhere to the standard library naming convention.
2025-10-01 17:57:32 +00:00
Sebastian Graf
784a063092 fix: try synthesizing synthetic MVars in mspec (#10644)
This PR explicitly tries to synthesize synthetic MVars in `mspec`. Doing
so resolves a bug triggered by use of the loop invariant lemma for
`Std.PRange`.
2025-10-01 16:29:12 +00:00
Sebastian Graf
ba52e9393c feat: LawfulMonad and WPMonad instances for Option and OptionT (#9932)
This PR adds `LawfulMonad` and `WPMonad` instances for `Option` and
`OptionT`.
2025-10-01 16:16:07 +00:00
Paul Reichert
1efefc25a5 fix: expose Int* definitions for simprocs and decide (fixes #10546) (#10631)
This PR exposes the definitions about `Int*`. The main reason is that
the `SInt` simprocs require many of them to be exposed. Furthermore,
`decide` now works with `Int*` operations. This fixes #10631.
2025-10-01 15:53:02 +00:00
Sebastian Graf
c920326f0b feat: introduce List.Cursor.pos as an abbreviation for prefix.length (#10642)
This PR introduces `List.Cursor.pos` as an abbreviation for
`prefix.length`.
2025-10-01 15:28:30 +00:00
Sebastian Graf
63354ce594 fix: spurious invariant instantiation in mspec by rfl (#10641)
This PR ensures that the `mspec` and `mvcgen` tactics no longer
spuriously instantiate loop invariants by `rfl`.
2025-10-01 15:03:09 +00:00
Sebastian Graf
3095c9d4df fix: hygiene for goals generated by mvcgen (#10639)
This PR fixes hygiene of the local context for *all* goals generated by
`mvcgen`, not just those that get a fresh MVar as in #9781.
2025-10-01 14:13:15 +00:00
Sebastian Graf
689b3aa8d7 feat: disable "experimental" warning for mvcgen (#10638)
This PR disables the "experimental" warning for `mvcgen` by changing its
default.
2025-10-01 14:10:40 +00:00
Lean stage0 autoupdater
d9058225a9 chore: update stage0 2025-10-01 14:32:36 +00:00
Markus Himmel
29c2b86ef4 chore: String.getUTF8Byte (#10637)
This PR adds the function `String.getUTF8Byte` ahead of a more
comprehensive PR to use `UTF8` instead of `Utf8` in identifiers.
2025-10-01 13:59:42 +00:00
Lean stage0 autoupdater
ee8f0cca33 chore: update stage0 2025-10-01 12:32:50 +00:00
Markus Himmel
5bfbe2a875 refactor: incorporate UTF8 material from String.Extra into String.Basic (#10634)
This PR defines `ByteArray.validateUTF8`, uses it to show that
`ByteArray.IsValidUtf8` is decidable and redefines `String.fromUTF8` and
friends to use it.

The functions `String.validateUTF8` and `String.utf8DecodeChar?` are
deprecated in favor of the identically named functions in the
`ByteArray` namespace.
2025-10-01 11:33:29 +00:00
Markus Himmel
9dc1faf327 chore: add an internal String function (#10635)
This PR adds an internal `String` function ahead of an upcoming PR.
2025-10-01 11:12:35 +00:00
Lean stage0 autoupdater
663d4d2c79 chore: update stage0 2025-10-01 08:21:46 +00:00
Markus Himmel
81ea922025 chore: rename String.Pos to String.Pos.Raw (#10624)
This PR renames `String.Pos` to `String.Pos.Raw`.

After an abbreviated deprecation cycle, we will then rename
`String.ValidPos` to `String.Pos`.
2025-10-01 07:45:24 +00:00
Henrik Böving
d88e417cda refactor: tame down dead let eliminator in lambda RC (#10626)
This PR reduces the aggressiveness of the dead let eliminator from
lambda RC.

The motivation for this is that all other passes in lambda RC respect
impurity but the dead let eliminator still operates under the assumption
of purity. There is a couple of motivations for the elim dead let
elaborator:
- unused projections introduced by the ToIR translation
- the elim dead branch pass introducing new opportunities
- closed term extraction introducing new opportunities
2025-09-30 19:51:16 +00:00
Marc Huisinga
dfd3d18530 test: improve language server test coverage (#10574)
This PR significantly improves the test coverage of the language server,
providing at least a single basic test for every request that is used by
the client. It also implements infrastructure for testing all of these
requests, e.g. the ability to run interactive tests in a project context
and refactors the interactive test runner to be more maintainable.
Finally, it also fixes a small bug with the recently implemented unknown
identifier code actions for auto-implicits (#10442) that was discovered
in testing, where the "import all unambiguous unknown identifiers" code
action didn't work correctly on auto-implicit identifiers.
2025-09-30 11:15:03 +00:00
Lean stage0 autoupdater
7d55c033e1 chore: update stage0 2025-09-30 01:46:26 +00:00
Mac Malone
5d8498888b feat: lake: use system cache for bootstrap (#10621)
This PR alters the Lake directory detection so that the core build
(i.e., `bootstrap = true`) is stored in the user cache directory (if
available) and never in a toolchain-specific directory.

It is also fixes some issues with cache environment configuration
discovered along the way.
2025-09-30 00:57:45 +00:00
Mac Malone
5ede2bfcf2 chore: use libPrefixOnWindows in core build (#10617)
This PR switches the core build Lake configuration file to use
`libPrefixOnWindows` rather than a CMake hack.

It also removes some dead TOML variables from the CMake configuration.
2025-09-29 20:07:02 +00:00
Markus Himmel
c039e29a3f perf: shorten critical build path around String.Basic (#10614)
This PR cuts some edges from the import graph.

Specifically:
- `TreeMap` and `HashMap` no longer depend on `String`, so now the
expensive things are all in parallel instead of partially in sequence
- `Omega` no longer relies on `List` lemmas
- The section of the import graph between `Init.Omega` and
`Init.Data.Bitvec.Lemmas` is cleaned up a bit
2025-09-29 19:45:21 +00:00
Kyle Miller
356d1f64bf fix: instantiate mvars in types of mvars in abstractMVars (#10612)
This PR fixes an issue reported [on
Zulip](https://leanprover.zulipchat.com/#narrow/channel/239415-metaprogramming-.2F-tactics/topic/.60abstractMVars.60.20not.20instantiating.20level.20mvars/near/541918246)
where `abstractMVars` (which is used in typeclass inference and `simp`
argument elaboration) was not instantiating metavariables in the types
of metavariables, causing it to abstract already-assigned metavariables.

This also eliminates an unnecessary `instantiateMVars` and documents the
invariant that the argument to `abstractExprMVars` must have its
metavariables already instantiated.
2025-09-29 16:33:10 +00:00
Marc Huisinga
9f2ce635ae fix: unknown identifier code actions with nested open (#10619)
This PR fixes a bug in the unknown identifier code actions where it
would yield non-sensical suggestions for nested `open` declarations like
`open Foo.Bar`.
2025-09-29 15:44:56 +00:00
Sebastian Graf
76403367ba fix: remove superfluous Monad instances from some spec lemmas (#10564) (#10618)
This PR removes superfluous `Monad` instances from the spec lemmas of
the `MonadExceptOf` lifting framework.

It also adds a bit of documentation and more tracing to `mvcgen`.

Fixes #10564.
2025-09-29 15:02:43 +00:00
Marc Huisinga
c016bb9434 fix: non-LSP-compliant FileSystemWatcher (#10609)
This PR fixes an LSP-non-compliance in the `FileSystemWatcher` that was
introduced in #925.

Closes #10597.
2025-09-29 14:16:09 +00:00
Lean stage0 autoupdater
239c348239 chore: update stage0 2025-09-29 14:24:13 +00:00
Henrik Böving
b82303e9b3 feat: consistent type ABI regardless of transparency (#10610)
This PR ensures that even if a type is marked as `irreducible` the
compiler can see through it in
order to discover functions hidden behind type aliases.
2025-09-29 13:31:41 +00:00
Mac Malone
6f3fef9373 fix: lake: add lake help cache (#10616)
This PR fixes an oversight where `lake cache help` existed but `lake
help cache` (and by extension `lake cache --help`) did not.
2025-09-29 12:41:28 +00:00
David Thrane Christiansen
4338a8be32 fix: better error message on missing declaration name for docstring (#10608)
This PR fixes a bad error message due to elaborating partial syntax with
Verso docstrings.

When elaborating partial syntax, the elaborator sometimes attempts to
add a docstring for a declaration that it didn't parse a name for. The
name defaults to anonymous, but inserting the docs for the anonymous
name throws a panic about being on the wrong async branch.

With this change, the reported error is the expected parser error
instead, which is much friendlier.
2025-09-29 06:26:08 +00:00
Lean stage0 autoupdater
19f6c168ef chore: update stage0 2025-09-29 00:32:08 +00:00
Leonardo de Moura
eba8bf3347 feat: infrastructure for grind interactive mode (#10607)
This PR adds infrastructure for the upcoming `grind` tactic mode, which
will be similar to the `conv` mode. The goal is to extend `grind` from a
terminal tactic into an interactive mode: `grind => …`.

It will serve as the foundation for `ungrind`, the process of converting
an expensive (and potentially fragile) `grind` proof into a robust
script. This mode will include tactics for expensive reasoning steps
such as cutsat model-based search, Gröbner basis computation,
E-matching, case splits, and more.

It will also provide robust, succinct references to facts and terms:
labels, structural matches, and anchors (e.g., `#abcd`).
2025-09-28 23:46:49 +00:00
David Thrane Christiansen
8c69b1eaec feat: suggest qualified names while editing Verso docstrings (#10584)
This PR causes Verso docstrings to search for a name in the environment
that is at least as long as the current name, providing it as a
suggestion.
2025-09-28 22:02:26 +00:00
Sebastian Ullrich
fd3f51012f feat: shake import minimizer aware of the module system and arbitrary elaboration dependencies (#10575)
This PR adds the necessary infrastructure for recording elaboration
dependencies that may not be apparent from the resulting environment
such as notations and other metaprograms. An adapted version of `shake`
from Mathlib is added to `script/` but may be moved to another location
or repo in the future.
2025-09-28 16:00:00 +00:00
Sebastian Ullrich
8b2fea1ec7 perf: avoid blocking wait on kernel env on some interpreter entries (#10591) 2025-09-28 12:52:24 +00:00
Lean stage0 autoupdater
9b1109c55d chore: update stage0 2025-09-28 05:44:06 +00:00
Leonardo de Moura
55b35c6e38 chore: grind examples (#10605)
Examples for `grind` demo.
2025-09-28 05:19:04 +00:00
Leonardo de Moura
3ce5097c3c feat: process grind core equalities in grind order (#10604)
This PR implements the method `processNewEq` in `grind order`. It is
responsible for processing equalities propagated by the `grind` E-graph.
2025-09-28 04:19:35 +00:00
Mac Malone
b6bfc9733c fix: lake: module artifact restoration for ir/bc (#10602)
This PR corrects the file path where Lake copies module `.ir` / `.bc`
artifacts.
2025-09-28 04:14:17 +00:00
Leonardo de Moura
8637bd296e fix: isPartialOrder in grind order (#10601)
This PR fixes a panic in `grind order` when order is not a partial
order.
2025-09-28 02:19:29 +00:00
Leonardo de Moura
6881177e38 feat: grind order negative constraints (#10600)
This PR implements support for negative constraints in `grind order`.
Examples:

```lean
open Lean Grind
example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsLinearPreorder α]
    (a b c d : α) : a ≤ b → ¬ (c ≤ b) → ¬ (d ≤ c) → d < a → False := by
  grind -linarith (splits := 0)

example [LE α] [Std.IsLinearPreorder α]
    (a b c d : α) : a ≤ b → ¬ (c ≤ b) → ¬ (d ≤ c) → ¬ (a ≤ d) → False := by
  grind -linarith (splits := 0)

example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsLinearPreorder α] [CommRing α] [OrderedRing α]
    (a b c d : α) : a - b ≤ 5 → ¬ (c ≤ b) → ¬ (d ≤ c + 2) → d ≤ a - 8 → False := by
  grind -linarith (splits := 0)
```
2025-09-28 01:50:27 +00:00
Leonardo de Moura
409daac2cb fix: Nat adapter in grind order (#10599)
This PR fixes the support for `Nat` in `grind order`. This module uses
the `Nat.ToInt` adapter.
2025-09-28 00:26:37 +00:00
Leonardo de Moura
62fa92ec4a feat: grind order positive constraints (#10598)
This PR implements support for positive constraints in `grind order`.
The new module can already solve problems such as:

```lean
example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsPreorder α]
    (a b c : α) : a ≤ b → b ≤ c → c < a → False := by
  grind

example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsPreorder α]
    (a b c d : α) : a ≤ b → b ≤ c → c < d → d ≤ a → False := by
  grind

example [LE α] [Std.IsPreorder α]
    (a b c : α) : a ≤ b → b ≤ c → a ≤ c := by
  grind

example [LE α] [Std.IsPreorder α]
    (a b c d : α) : a ≤ b → b ≤ c → c ≤ d → a ≤ d := by
  grind
```

It also generalizes support for offset constraints in `grind` to rings.
The new module implements theory propagation and reduces the number of
case splits required to solve problems:

```lean
example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsPreorder α] [Ring α] [OrderedRing α]
    (a b : α) : a ≤ 5 → b ≤ 8 → a > 6 ∨ b > 10 → False := by
  grind -linarith (splits := 0)

example [LE α] [LT α] [Std.LawfulOrderLT α] [Std.IsPreorder α] [CommRing α] [OrderedRing α]
    (a b c : α) : a + b*c + 2*c ≤ 5 → a + c > 5 - c - c*b → False := by
  grind -linarith (splits := 0)

example (a b : Int) (h : a + b > 5) : (if a + b ≤ 0 then b else a) = a := by
  grind -linarith -cutsat (splits := 0)
```

We still need to implement support for negated constraints.
2025-09-27 23:22:09 +00:00
Leonardo de Moura
0504e32bb7 feat: add addEdge to grind order (#10596)
This PR implements the function for adding new edges to the graph used
by `grind order`. The graph maintains the transitive closure of all
asserted constraints.
2025-09-27 18:18:41 +00:00
Mac Malone
fbfc7694a0 fix: only pass known CMake build types to Lake (#10595)
This PR ensures that Lake only receives recognized CMake build types
from CMake. This fixes an issue with #10581 which broke the
`RelWithAssert` build.
2025-09-27 17:47:15 +00:00
Leonardo de Moura
69b8b0098c feat: proofs for theory propagation in grind order (#10594)
This PR implements proof construction for theory propagation in `grind
order`.
2025-09-27 16:36:21 +00:00
Leonardo de Moura
69c8f13bf2 feat: proof construction for grind order (#10590)
This PR implements proof term construction for `grind order`.
2025-09-27 05:30:32 +00:00
Leonardo de Moura
39beb25f16 feat: helper theorems for grind order (#10589)
This PR adds helper theorems for implementing  `grind order`
2025-09-27 04:04:44 +00:00
Mac Malone
6d5efd79b9 chore: lake: restoreAllArtifacts / CMake build types in TOML schema (#10588)
This PR adds `restoreAllArtifacts` and the CMake build types to the Lake
TOML schema.

I forgot to do this in #10576 and #10578.
2025-09-27 03:38:22 +00:00
Mac Malone
b37d2ce2b9 chore: use CMake build type in Lake core build (#10581)
This PR alters the core build Lake configuration file to use the
`CMAKE_BUILD_TYPE` for Lake's `buildType`.
2025-09-27 03:38:12 +00:00
Mac Malone
18832eb600 fix: lake: ill-formed build output handling (#10586)
This PR makes Lake no longer error if build outputs found in a trace
file (or in the artifact cache) are ill-formed.

This is caused a problem with the CI cache and is just generally too
strict.
2025-09-27 03:35:57 +00:00
Mac Malone
05300f7b51 chore: restoreAllArtifacts = true for core (#10582)
This PR sets `restoreAllArtifacts = true` in the core build Lake
configuration file.
2025-09-27 03:30:21 +00:00
Leonardo de Moura
0bf7741a3e feat: multiple grind propagators per declaration (#10583)
This PR allows users to declare additional `grind` constraint
propagators for declarations that already include propagators in core.
2025-09-27 02:04:03 +00:00
Mac Malone
f80d6e7d38 refactor: lake: libPrefixOnWindows on libName (#10579)
This PR alters `libPrefixOnWindows` behavior to add the `lib` prefix to
the library's `libName` rather than just the file path. This means that
Lake's `-l` will now have the prefix on Windows. While this should not
matter to a MSYS2 build (which accepts both `lib`-prefixed and
unprefixed variants), it should ensure compatibility with MSVC (if that
is ever an issue).
2025-09-27 01:54:23 +00:00
Mac Malone
5b8d4d7210 chore: invalidate Lake CI cache (#10587)
This PR invalidates the CI cache for the Linux Lake build job by bumping
the version of the CI cache key.

The CI cache is broken due to a change in the output format in build
traces. This will be fixed in #10586, but this should prevent further
breakages of PRs in the meantime.
2025-09-27 01:11:23 +00:00
Lean stage0 autoupdater
db8c77a8fa chore: update stage0 2025-09-26 22:39:54 +00:00
Mac Malone
7ee3079afb feat: lake: CMake build types (#10578)
This PR adds support for the CMake spelling of a build type (i.e.,
capitalized) to Lake's `buildType` configuration option.
2025-09-26 21:06:12 +00:00
Mac Malone
c3d9d0d931 feat: lake: restoreAllArtifacts (#10576)
This PR adds a new package configuration option: `restoreAllArtifacts`.
When set to `true` and the Lake local artifact cache is enabled, Lake
will copy all cached artifacts into the build directory. This ensures
they are available for external consumers who expect build results to be
in the build directory.
2025-09-26 20:58:32 +00:00
Mac Malone
e98d7dd603 feat: lake: Reservoir-versioned dependencies (#10551)
This PR enables Reservoir packages to be required as dependencies at a
specific package version (i.e., the `version` specified in the package's
configuration file).
2025-09-26 20:52:54 +00:00
Mac Malone
6102f00322 chore: rm src/lake/lakefile.toml (#10580)
This file is essentially just for me and can cause problems with the
language server, so I have removed it from the committed code (and left
an ignored version on my own setup).
2025-09-26 20:51:02 +00:00
Sebastian Ullrich
646f2fabbf fix: allow meta decls in #eval (#10545) 2025-09-26 15:10:33 +00:00
Sebastian Ullrich
f4a0259344 chore: cleanups uncovered by Shake (#10572)
* Wrap proof subterms in `by exact` so dependencies can be demoted to
private `import`s
* Remove trivial instance re-definitions that may cause name collisions
on import changes
* Remove unused `open`s that may fail on import removals
2025-09-26 14:38:30 +00:00
Sebastian Graf
3f816156cc fix: immediately replace main goal in SPred proof mode tactics (#10571)
This PR ensures that `SPred` proof mode tactics such as `mspec`,
`mintro`, etc. immediately replace the main goal when entering the proof
mode. This prevents `No goals to be solved` errors.
2025-09-26 13:41:38 +00:00
Sebastian Ullrich
c92ec361cd chore: CommandElabM.liftCoreM should not reset InfoState.lazyAssignment (#10518)
Fixes #10408
2025-09-26 13:37:40 +00:00
Sebastian Ullrich
49cff79712 fix: privacy checks and import all (#10550)
This PR ensures private declarations are accessible from the private
scope iff they are local or imported through an `import all` chain,
including for anonymous notation and structure instance notation.
2025-09-26 13:26:10 +00:00
Sebastian Ullrich
2677ca8fb4 fix: import-merging theorems under the module system (#10556) 2025-09-26 13:02:51 +00:00
Sebastian Graf
78b09d5dcc feat: support case label like syntax in mvcgen invariants (#10570)
This PR adds support for case label like syntax in `mvcgen invariants`
in order to refer to inaccessible names. Example:

```lean
def copy (l : List Nat) : Id (Array Nat) := do
  let mut acc := #[]
  for x in l do
    acc := acc.push x
  return acc

theorem copy_labelled_invariants (l : List Nat) : ⦃⌜True⌝⦄ copy l ⦃⇓ r => ⌜r = l.toArray⌝⦄ := by
  mvcgen [copy] invariants
  | inv1 acc => ⇓ ⟨xs, letMuts⟩ => ⌜acc = l.toArray⌝
  with admit
```
2025-09-26 12:57:49 +00:00
Sebastian Ullrich
a164ae5073 chore: overhaul meta error messages (#10569) 2025-09-26 12:56:46 +00:00
Sebastian Ullrich
2c54386555 fix: Prop instances should be elaborated in the private scope (#10568) 2025-09-26 12:16:09 +00:00
Sebastian Graf
62fd973b28 fix: make getArg!' compute the correct arg index to access (#10567)
This PR fixes argument index calculation in `Lean.Expr.getArg!'`.
2025-09-26 11:54:49 +00:00
Sebastian Graf
71e09ca883 feat: concrete invariant? suggestions based on start and end (#10566)
This PR improves `mvcgen invariants?` to suggest concrete invariants
based on how invariants are used in VCs.
These suggestions are intentionally simplistic and boil down to "this
holds at the start of the loop and this must hold at the end of the
loop":

```lean
def mySum (l : List Nat) : Nat := Id.run do
  let mut acc := 0
  for x in l do
    acc := acc + x
  return acc

/--
info: Try this:
  invariants
    · ⇓⟨xs, letMuts⟩ => ⌜xs.prefix = [] ∧ letMuts = 0 ∨ xs.suffix = [] ∧ letMuts = l.sum⌝
-/
#guard_msgs (info) in
theorem mySum_suggest_invariant (l : List Nat) : mySum l = l.sum := by
  generalize h : mySum l = r
  apply Id.of_wp_run_eq h
  mvcgen invariants?
  all_goals admit
```

It still is the user's job to weaken this invariant such that it
interpolates over all loop iterations, but it *is* a good starting point
for iterating. It is also useful because the user does not need to
remember the exact syntax.
2025-09-26 11:37:14 +00:00
Kim Morrison
e6dd41255b feat: upstream ReduceEval instances from quote4 (#10563)
This PR moves some `ReduceEval` instances about basic types up from the
`quote4` library.
2025-09-26 04:02:55 +00:00
Leonardo de Moura
cfc46ac17f feat: internalization for grind order (#10562)
This PR simplifies the `grind order` module, and internalizes the order
constraints. It removes the `Offset` type class because it introduced
too much complexity. We now cover the same use cases with a simpler
approach:
- Any type that implements at least `Std.IsPreorder`
- Arbitrary ordered rings.
- `Nat` by the `Nat.ToInt` adapter.
2025-09-26 03:49:06 +00:00
Mac Malone
7c0868d562 refactor: lake: introduce LogConfig (#10468)
This PR refactors the Lake log monads to take a `LogConfig` structure
when run (rather than multiple arguments). This breaking change should
help minimize future breakages due to changes in configurations options.

In addition, the CLI logging monad stack has been polished up and
`LogIO` now supports the `failLv` configuration option.
2025-09-26 02:44:51 +00:00
Mac Malone
28fb4bb1b2 feat: lake cache (& remote cache support) (#10188)
This PR adds support for remote artifact caches (e.g., Reservoir) to
Lake. As part of this support, a new suite of `lake cache` CLI commands
has been introduced to help manage Lake's cache. Also, the existing
local cache support has been overhauled for better interplay with the
new remote support.

**Cache CLI**

Artifacts are uploaded to a remote cache via `lake cache put`. This
command takes a JSON Lines input-to-outputs file which describes the
output artifacts for a build (indexed by its input hash). This file can
be produced by a run of `lake build` with the new `-o` option. Lake will
write the input-to-outputs mappings of thee root package artifacts
traversed by the build to the file specified via `-o`. This file can
then be passed to `lake cache put` to upload both it and the built
artifacts from the local cache to the remote cache.

The remote cache service can be customized using the following
environment variables:

* `LAKE_CACHE_KEY`: This is the authorization key for the remote cache.
Lake uploads artifacts via `curl` using the AWS Signature Version 4
protocol, so this should be the S3 `<key>:<secret>` pair expected by
`curl`.

* `LAKE_CACHE_ARTIFACT_ENDPOINT`: This is the base URL to upload (or
download) artifacts to a given remote cache. Artifacts will be stored at
`<endpoint>/<scope/<content-hash>.art`.

* `LAKE_CACHE_REVISION_ENDPOINT`: This is the base URL to upload (or
download) input-to-output mappings to a given remote cache. Mappings are
indexed by the Git revision of the package, and are stored at
`<endpoint>/<scope/<rev>.jsonl`.

The `<scope>` is provided through the `--scope` option to `lake cache
put`. This option is used to prevent one package from overwriting the
artifacts/mappings of another. Lake artifact hashes and Git revisions
hashes are not cryptographically secure, so it is not safe for a service
to store untrusted files across packages in a single flat store.

Once artifacts are available in a remote cache, the `lake cache get`
command can be used to retrieve them. By default, it will fetch
artifacts for the root package's dependencies from Reservoir using its
API. But, like `cache put`, it can be configured to use a custom
endpoint with the above environment variables and an explicit `--scope`.
When so configured, `cache get` will instead download artifacts for the
root package. Lake only downloads artifacts for a single package in this
case, because it cannot deduce the necessary package scopes without
Reservoir.

**Significant local cache changes**

* Lake now always has a cache directory. If Lake cannot find a good
candidate directory on the system for the cache, it will instead store
the cache at `.lake/cache` within the workspace.

* If the local cache is disabled, Lake will not save built artifacts to
the cache. However, Lake will, nonetheless, always attempt to lookup
build artifacts in the cache. If found, the cached artifact will be
copied to the the build location ("restored").

* Input-to-outputs mappings in the local cache are no longer stored in a
single file for a package, but rather in individual files per input (in
the `outputs` subdirectory of the cache).

* Outputs in a trace file, outputs file, or mappings file are now an
`ArtifactDescr`, which is currently composed of both the content hash
and the file extension.

* Trace files now contain a date-based `schemaVersion` to help make
version to version migration easier. Hashes in JSON and in artifacts
names now use a 16-digit hexadecimal encoding (instead of a variable
decimal encoding).

* `buildArtifactUnlessUpToDate` now returns an `Artifact` instead of a
`FilePath`.

**NOTE:** The Lake local cache is still disabled by default. This means
that built artifacts, by default, will not be placed in the cache
directory, and thus will not be available for `lake cache put` to
upload. Users must first explicitly enable the cache by either setting
the `LAKE_ARTIFACT_CACHE` environment variable to a truthy value or by
setting the `enableArtifactCache` package configuration option to
`true`.
2025-09-26 01:13:43 +00:00
Robert J. Simmons
2231d9b488 feat: improve error messages for ambiguous 3.toDecmial syntax (#10488)
This PR changes the way that scientific numerals are parsed in order to
give better error messages for (invalid) syntax like `32.succ`.

Example:

```lean4
#check 32.succ
```

Before, the error message is:

```
unexpected identifier; expected command
```

This is because `32.` parses as a complete float, and `#check 32.`
parses as a complete command, so `succ` is being read as the start of a
new command.

With this change, the error message will move from the `succ` token to
the `32` token (which isn't totally ideal from my perspective) but gives
a less misleading error message and corresponding suggestion:

```
unexpected identifier after decimal point; consider parenthesizing the number
```
2025-09-26 01:12:10 +00:00
David Thrane Christiansen
e72bf59385 feat: more metadata for Verso docstrings (#10560)
This PR adds highlighted Lean code to Verso docstrings and fixes smaller
quality-of-life issues.
2025-09-25 23:51:51 +00:00
Mac Malone
343328b7df feat: lake: rename dependencies (#10452)
This PR refactors Lake's package naming procedure to allow packages to
be renamed by the consumer. With this, users can now require a package
using a different name than the one it was defined with.

This is support will be used in the future to enable seamlessly
including the same package at multiple different versions within the
same workspace.

In a Lake package configuration file written in Lean, the current
package's assigned name is now accessed through `__name__` instead of
the previous `_package.name`. A deprecation warning has been added to
`_package.name` to assist in migration.
2025-09-25 22:10:39 +00:00
Leonardo de Moura
5b9befcdbf feat: infrastructure for grind order (#10553)
This PR implements infrastructure for the new `grind order` module.
2025-09-25 17:53:43 +00:00
Alex Keizer
188ef680da chore: ensure pass refers to SpecResult.pass in GuardMsgs (#10539)
This PR adds a `.` in front of `pass` in the `#guard_msgs`
implementation.

Previously, the match arm read `| pass => ...`. Presumably, `pass` was
intended to mean `SpecResult.pass`, but, this isn't in scope, so instead
`pass` here is a catch-all variable. By adding a dot, we ensure we
actually refer to the constant. Note that this was the last case in the
pattern-match, and since all other constructors were correctly
referenced, the only case that went to the fallback was
`SpecResult.pass`, so the code did the right thing. Still, by fixing
this, we prevent a surprise in the event that a new `SpecResult`
constructor is added.
2025-09-25 13:50:46 +00:00
Henrik Böving
5fd8c1b94d feat: new String.Slice API (#10514)
This PR defines the new `String.Slice` API.

Many of the core design principles of the API are taken over from Rust's
[string
library](https://doc.rust-lang.org/stable/std/string/struct.String.html).
2025-09-25 12:18:52 +00:00
Sebastian Ullrich
5ef7b45afa doc: meta modifier (#10554) 2025-09-25 11:45:54 +00:00
Mario Carneiro
9f41f3324a fix: make Substring.beq reflexive (#10552)
This PR ensures that `Substring.beq` is reflexive, and in particular
satisfies the equivalence `ss1 == ss2 <-> ss1.toString = ss2.toString`.

Closes #10511.

Note: I also fixed a strange line in the `String.extract` documentation
which looks like it may have been a copypasta, and added another example
to show how invalid UTF8 positions work, but the doc also makes a point
of saying that it is unspecified so maybe it would be better not to have
the example? 🤷
2025-09-25 05:08:41 +00:00
Henrik Böving
055060990c fix: use _Exit in the language server (#10538)
This PR fixes deadlocking `exit` calls in the language server.

We have previously observed deadlocking calls to `exit` inside of the
language server and deemed them irrelevant. However, child processes of
these deadlocking exiting processes can continue to consume a large
amount of CPU as they try to compile a library etc. Hence, this PR
switches to the MT safe `_Exit` inside of the language server,
in order to ensure the server finishes when it is told to.
2025-09-24 14:44:16 +00:00
Sebastian Graf
4c44f4ef7c chore: add fixed test case for #9363 (#10547) 2025-09-24 14:32:08 +00:00
Markus Himmel
d6cd738ab4 feat: redefine String, part two (#10457)
This PR introduces safe alternatives to `String.Pos` and `Substring`
that can only represent valid positions/slices.

Specifically, the PR

- introduces the predicate `String.Pos.IsValid`;
- proves several nontrivial equivalent conditions for
`String.Pos.IsValid`;
- introduces `String.ValidPos`, which is a `String.Pos` with an
`IsValid` proof;
- introduces `String.Slice`, which is like `Substring` but made from
`String.ValidPos` instead of `Pos`;
- introduces `String.Pos.IsValidForSlice`, which is like
`String.Pos.IsValid` but for slices;
- introduces `String.Slice.Pos`, which is like `String.ValidPos` but for
slices;
- introduces various functions for converting between the two types of
positions.

The API added in this PR is not complete. It will be expanded in future
PRs with addional operations and verification.
2025-09-24 13:36:55 +00:00
Markus Himmel
68409ef6fd chore: turn some crashes into errors (#8402)
This PR prevents some nonsensical code from crashing the server.

Specifically, the kernel is changed to
- properly check that passed expressions do not contain loose bvars,
which could lead to a segmentation fault on a well-crafted input
(discovered through fuzzing), and
- check that constants generated when creating a new inductive type do
not overwrite each other, which could lead to the kernel taking
something out of the environment and then casting it to something it
isn't.

Partially addresses #8258, but let's keep that one open until the error
message is a little better.

Fixes #10492.
2025-09-24 13:04:18 +00:00
Joachim Breitner
ca1101dddd feat: #print T.rec to show more information (#10543)
This PR lets `#print T.rec` show more information about a recursor, in
particular it's reduction rules.
2025-09-24 12:22:00 +00:00
Sebastian Graf
ce7a4f50be chore: add spec lemmas for MonadControl (#10544) 2025-09-24 12:16:06 +00:00
Sebastian Graf
eb9dd9a9e3 chore: add some missing spec lemmas (#10540) 2025-09-24 12:08:12 +00:00
Markus Himmel
b6198434f2 fix: String regressions (#10523)
This PR fixes some regressions introduced by #10304.
2025-09-24 12:01:50 +00:00
Joachim Breitner
1374445081 chore: update bench/riskv-ast.lean (#10505)
This PR disables `trace.profiler` in `bench/riskv-ast.lean`. We don't
want to optimize the trace profiler, but normal code.

While at it, I removed the `#exit` to cover more of the file.

While at it, also import the latest from from upstream.
2025-09-24 11:46:26 +00:00
Joachim Breitner
9df345e322 fix: .congr_simp for non-defs (#10508)
This PR allows `.congr_simp` theorems to be created not just for
definitoins, but any constant. This is important to make the machinery
work across module boundaries.

It also moves the `enableRealizationsForConst` for constructors to a
more sensible
place, and enables it for axioms.
2025-09-24 11:45:49 +00:00
Kim Morrison
3b2705d0df feat: helper functions for premise selection API (#10512)
This PR adds some helper functions for the premise selection API, to
assist implementers.

---------

Co-authored-by: Thomas Zhu <thomas.zhu.sh@hotmail.com>
2025-09-24 11:45:40 +00:00
Sebastian Ullrich
44a2b085c4 feat: scripts/Modulize.lean (#10460)
This PR introduces a simple script that adjusts module headers in a
package for use of the module system, without further minimizing import
or annotation use.

---------

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2025-09-24 11:40:17 +00:00
Joachim Breitner
7f18c734eb fix: simpHaveTelescope: calculate used fvars transitiviely (#10536)
This PR fixes `simp` in `-zeta -zetaUnused` mode from producing
incorrect proofs if in a `have` telescope a variable occurrs in the
type of the body only transitively. Fixes #10353.
2025-09-24 11:30:09 +00:00
Sebastian Ullrich
ac6ae51bce chore: minor module system fixes from batteries port (#10496) 2025-09-24 08:59:23 +00:00
Lean stage0 autoupdater
fd4a8c5407 chore: update stage0 2025-09-24 08:23:57 +00:00
Henrik Böving
2e5bbf4596 fix: #guard should work with the module system (#10535)
This PR ensures that `#guard` can be called under the module system
without issues.
2025-09-24 07:38:10 +00:00
David Thrane Christiansen
00b74e02cd feat: docstring role for module names, plus improved suggestions (#10533)
This PR adds a docstring role for module names, called `module`. It also
improves the suggestions provided for code elements, making them more
relevant and proposing `lit`.
2025-09-24 07:32:27 +00:00
Marc Huisinga
90db9ef006 feat: unknown identifier code actions for auto-implicits (#10442)
This PR ensures that unknown identifier code actions are provided on
auto-implicits.

Closes #8837.
2025-09-24 07:28:06 +00:00
Kim Morrison
3ddda9ae4d chore: adjust List.countP grind annotations (#10532) 2025-09-24 07:07:11 +00:00
Kim Morrison
ac0b82933f chore: add variant of Rat.ofScientific_def for grind (#10534) 2025-09-24 06:37:46 +00:00
Kim Morrison
d8219a37ef feat: grind linarith synthesis issues explain changes in behaviour (#10448)
This PR modifies the "issues" grind diagnostics prints. Previously we
would just describe synthesis failures. These messages were confusing to
users, as in fact the linarith module continues to work, but less
capably. For most of the issues, we now explain the resulting change in
behaviour. There is a still a TODO to explain the change when
`IsOrderedRing` is not available.
2025-09-24 04:02:35 +00:00
thorimur
7ea7acc687 chore: lower monad of addSuggestion(s) to CoreM (#10526) 2025-09-24 03:35:34 +00:00
Sofia Rodrigues
161a1c06a2 feat: add Std.Notify type (#10368)
This PR adds `Notify` that is a structure that is similar to `CondVar`
but it's used for concurrency. The main difference between
`Std.Sync.Notify` and `Std.Condvar` is that depends on a `Std.Mutex` and
blocks the entire thread that the `Task` is using while waiting. If I
try to use it with async and a lot of `Task`s like this:

```lean
def condvar : Async Unit := do
  let condvar ← Std.Condvar.new
  let mutex ← Std.Mutex.new false

  for i in [0:threads] do
    background do
      IO.println s!"start {i + 1}"
      await =<< (show IO (ETask _ _) from IO.asTask (mutex.atomically (condvar.wait mutex)))
      IO.println s!"end {i + 1}"

  IO.sleep 2000
  condvar.notifyAll
```

It causes some weird behavior because some tasks start running and get
notified, while others don’t, because `condvar.wait` blocks the `Task`
entire task and right now afaik it blocks an entire thread and cannot be
paused while doing blocking operations like that.

`Notify` uses `Promise`s so it’s better suited for concurrency. The
`Task` is not blocked while waiting for a notification which makes it
simpler for use cases that just involve notifying:

```lean
def notify : Async Unit := do
  let notify ← Std.Notify.new

  for i in [0:threads] do
    background do
      IO.println s!"start {i}"
      notify.wait
      IO.println s!"end {i}"

  IO.sleep 2000
  notify.notify
```

This PR depends on: #10366, #10367 and #10370.
2025-09-24 03:35:08 +00:00
Kim Morrison
781e3c6add chore: remove unhelpful grind annotations (#10435)
This PR removes some `grind` annotations for `Array.attach` and related
functions. These lemmas introduce lambda on the right hand side which
`grind` can't do much with. I've added a test file that verifies that
the theorems with removed annotations can actually be proved already by
grind. Removing the annotations will help with excessive instantiation.
2025-09-24 03:02:46 +00:00
Leonardo de Moura
b73b8a7edf feat: helper ordered ring theorems (#10529)
This PR adds some helper theorems for the upcoming `grind order` solver.
2025-09-24 03:01:19 +00:00
Sofia Rodrigues
94e5b66dfe feat: add AsyncStream, AsyncWrite and AsyncRead type classes (#10370)
This PR adds async type classes for streams.
2025-09-23 23:30:33 +00:00
Joachim Breitner
8443600762 chore: assert hasLooseBVar before shifting (#10528)
This assumptions seems to be violated in #10353, so maybe worth
asserting it here to more quickly stumble over it.
2025-09-23 20:49:04 +00:00
Garmelon
8b64425033 chore: set temci tags for the radar bench script (#10527)
The radar bench scripts at
https://github.com/leanprover/radar-bench-lean4/ split up the benchmarks
between the two runners based on the tags: One runner filters by the tag
`stdlib` while the other filters by the tag `other`. Only benchmarks
using one of these tags will be run, and any benchmark tagged with both
will waste electricity.

As far as I know, the tags are unused otherwise, so I just replaced all
the old tags.
2025-09-23 19:51:10 +00:00
David Thrane Christiansen
d96fd949ff fix: invalid docstring suggestions for attributes (#10522)
This also exposed an issue with `#guard_msgs` in Verso mode where the
docstring would log parse errors as if it contained Verso, even though
it actually worked. This has been fixed, and error messages improved as
well.
2025-09-23 16:18:21 +00:00
Sebastian Ullrich
d33aece210 feat: list definitions in defeq problems that could not be unfolded for lack of @[expose] (#10158)
This PR adds information about definitions blocked from unfolding via
the module system to type defeq errors.
2025-09-23 16:13:39 +00:00
Sebastian Graf
9a7bab5f90 chore: add documentation for mvcgen related definitions (#10525) 2025-09-23 15:59:58 +00:00
Kim Morrison
e2f87ed215 chore: lemma for unfolding eraseIdxIfInBounds (#10520) 2025-09-23 13:08:41 +00:00
Tom Levy
e42892cfb6 doc: fix comment about String.fromUTF8 replacing invalid chars (#10240)
Hi, the doc of `String.fromUTF8` previously said invalid characters are
replaced with 'A'. But the parameter `h : validateUTF8 a` guarantees
there are no invalid characters, so that explanation doesn't make sense
to me. This PR deletes that explanation (and fixes some unrelated
typos).

I also have a patch that uses `h` to prove each of the characters is
valid, eliminating the need for a default character
([pr/chore-String-fromUTF8-prove-valid](27f1ff36b2)),
would you be interested in merging that?

<details>
<summary>Notes on invalid characters from unchecked C++</summary>
I don't know if this function may be called from unchecked C++ with
invalid characters. If it may, I'm not sure what would happen with my
patched function... I'm not familiar with Lean's safety model, but it
seems like a bad idea to have a Lean function that takes a proof of a
proposition but is expected to operate in a certain way even if the
proposition is false. I think the safe approach is to have two functions
-- one that takes a proof and is only called from Lean, and another that
doesn't take a proof and replaces invalid chars (for use from C++, not
sure whether it's useful from Lean); I'd prefer to go even further and
report an error instead of silently replacing invalid characters (I'm
not sure if there is any easy way to report errors/panic in Lean code
called from C++).
</details>
2025-09-23 10:19:20 +00:00
Sebastian Ullrich
cc5c070328 fix: inline/specialize may only refer to publicly imported decls for now (#10494)
This PR resolves a potential bad interaction between the compiler and
the module system where references to declarations not imported are
brought into scope by inlining or specializing. We now proactively check
that declarations to be inlined/specialized only reference public
imports. The intention is to later resolve this limitation by moving out
compilation into a separate build step with its own import/incremental
system.
2025-09-23 09:58:14 +00:00
David Thrane Christiansen
f122454ef6 chore: cleanup and better docs for #10479 (#10504)
This PR cleans up a half-reverted refactor and adds documentation to
#10479.
2025-09-23 09:02:07 +00:00
Sebastian Graf
02f482129a fix: Use @[tactic_alt] for bv_decide, mvcgen and similar tactics (#10506)
This PR annotates the shadowing main definitions of `bv_decide`,
`mvcgen` and similar tactics in `Std` with the semantically richer
`tactic_alt` attribute so that `verso` will not warn about overloads.

This fixes leanprover/verso#535.
2025-09-23 07:40:02 +00:00
Kim Morrison
0807f73171 feat: basic premise selection algorithm based on MePo (#7844)
This PR adds a simple implementation of MePo, from "Lightweight
relevance filtering for machine-generated resolution problems" by Meng
and Paulson.

This needs tuning, but is already useful as a baseline or test case.

---------

Co-authored-by: Thomas Zhu <thomas.zhu.sh@hotmail.com>
2025-09-23 06:40:22 +00:00
Lean stage0 autoupdater
27fa5b0bb5 chore: update stage0 2025-09-23 06:22:49 +00:00
Alex Meiburg
8f9966ba74 doc: fix to new name for "Associative" in ac_rfl / ac_nf docstring (#10458)
This PR fixes the docstring for ac_rfl and ac_nf to correctly refer to
`Std.Associative` instead of the old name `Associative`; ditto
`Commutative`.
2025-09-23 05:52:05 +00:00
Sofia Rodrigues
eabd7309b7 feat: add vectored write and fix rc issue in tcp and udp cancel function (#10487)
This PR adds vectored write and fix rc issues in tcp and udp cancel
functions.
2025-09-22 17:02:57 +00:00
Sebastian Graf
795d13ddce feat: account for tactic_alt in missing docs linter (#10507)
This PR makes the missing docs linter aware of `tactic_alt`.
2025-09-22 16:23:24 +00:00
Kim Morrison
2b23afdfab chore: remove >6 month old deprecations (#10446) 2025-09-22 12:47:11 +00:00
Kim Morrison
20b0bd0a20 chore: upstream rangeOfStx? from Batteries (#10490)
This PR upstreams a helper function that is used in ProofWidgets.

---------

Co-authored-by: Marc Huisinga <mhuisi@protonmail.com>
2025-09-22 12:21:14 +00:00
Kim Morrison
979c2b4af0 chore: add grind annotations for List.not_mem_nil (#10493) 2025-09-22 12:18:03 +00:00
Kim Morrison
b3cd5999e7 chore: normalize empty ByteArrays to .empty (#10501) 2025-09-22 12:06:29 +00:00
Kim Morrison
a4dcb25f69 chore: add limited public API for builtinRpcProcedures (#10499)
This is not a complete public API, just enough to avoid an `open
private` in ProofWidgets.
2025-09-22 11:20:25 +00:00
Henrik Böving
85ce814689 fix: constant folding for UIntX (#10495)
This PR fixes constant folding for UIntX in the code generator. This
optimization was previously simply dead code due to the way that uint
literals are encoded.
2025-09-22 10:06:24 +00:00
Leonardo de Moura
9fc18b8ab4 doc: extra grind docstrings (#10486)
This PR adds and expands `grind` related docstrings.
2025-09-22 03:27:48 +00:00
Leonardo de Moura
852a3db447 chore: improve grind imports (#10491) 2025-09-22 01:25:41 +00:00
Lean stage0 autoupdater
d0d5d4ca39 chore: update stage0 2025-09-21 11:13:12 +00:00
Sebastian Ullrich
b32f3e8930 chore: revert "feat: add vectored write and fix rc issue in tcp and udp cancel functions" (#10485)
Reverts leanprover/lean4#10367 due to Windows build failure
2025-09-21 10:43:46 +00:00
David Thrane Christiansen
34f5fba54d chore: remove bootstrapping workaround (#10484)
This PR removes temporary bootstrapping workarounds introduced in PR
#10479.
2025-09-21 07:36:49 +00:00
Leonardo de Moura
4c9601e60f feat: support for injective functions in grind (#10483)
This PR completes support for injective functions in grind. See
examples:
```lean

/-! Add some injectivity theorems. -/

def double (x : Nat) := 2*x

@[grind inj] theorem double_inj : Function.Injective double := by
  grind [Function.Injective, double]

structure InjFn (α : Type) (β : Type) where
  f : α → β
  h : Function.Injective f

instance : CoeFun (InjFn α β) (fun _ => α → β) where
  coe s := s.f

@[grind inj] theorem fn_inj (F : InjFn α β) : Function.Injective (F : α → β) := by
  grind [Function.Injective, cases InjFn]

def toList (a : α) : List α := [a]

@[grind inj] theorem toList_inj : Function.Injective (toList : α → List α) := by
  grind [Function.Injective, toList]

/-! Examples -/

example (x y : Nat) : toList (double x) = toList (double y) → x = y := by
  grind

example (f : InjFn (List Nat) α) (x y z : Nat)
    : f (toList (double x)) = f (toList y) →
      y = double z →
      x = z := by
  grind
```
2025-09-21 06:31:46 +00:00
Leonardo de Moura
42be7bb5c7 fix: [grind inj] attribute (#10482)
This PR fixes symbol collection for the `@[grind inj]` attribute.
2025-09-21 04:14:17 +00:00
Leonardo de Moura
5f68c1662d refactor: generalize theorem activation in grind (#10481)
This PR generalizes the theorem activation function used in `grind`. 
The goal is to reuse it to implement the injective function module.
2025-09-21 02:50:55 +00:00
Leonardo de Moura
2d14d51935 fix: equality resolution in grind (#10480)
This PR fixes a bug in the equality resolution frontend used in `grind`.
2025-09-21 02:40:38 +00:00
Lean stage0 autoupdater
7cbeb14e46 chore: update stage0 2025-09-20 22:47:35 +00:00
David Thrane Christiansen
cee2886154 feat: improvements to Verso docstrings (#10479)
This PR implements module docstrings in Verso syntax, as well as adding
a number of improvements and fixes to Verso docstrings in general. In
particular, they now have language server support and are parsed at
parse time rather than elaboration time, so the snapshot's syntax tree
includes the parsed documentation.
2025-09-20 22:05:57 +00:00
Leonardo de Moura
35764213fc fix: grind sort internalization (#10477)
This PR ensures sorts are internalized by `grind`.
2025-09-20 18:31:20 +00:00
Sofia Rodrigues
6b92cbdfa4 feat: add vectored write and fix rc issue in tcp and udp cancel functions (#10367)
This PR adds vectored write for TCP and UDP (that helps a lot with not
copying the arrays over and over) and fix a RC issue in TCP and UDP
cancel functions with the line `lean_dec((lean_object*)udp_socket);` and
a similar one that tries to decrement the object inside of the `socket`.
2025-09-20 17:01:20 +00:00
Leonardo de Moura
72bb7cf364 fix: infer_let in the kernel (#10476)
This PR fixes the dead `let` elimination code in the kernel's
`infer_let` function.

Closes #10475
2025-09-20 16:26:46 +00:00
Sofia Rodrigues
4881c3042e refactor: replace Task with Async and minor changes to some basic Async functions (#10366)
This PR refactors the Async module to use the `Async` type in all of the
`Async` files.
2025-09-20 16:23:06 +00:00
Leonardo de Moura
ec7add0b48 doc: ! modifier in grind parameters (#10474)
This PR adds a doc string for the `!` parameter modifier in `grind`.
2025-09-20 08:06:05 +00:00
Leonardo de Moura
9b842b7554 fix: message context in grind code actions (#10473)
This PR ensures the code action messages produced by `grind` include the
full context
2025-09-20 08:02:12 +00:00
Leonardo de Moura
fc718eac88 feat: code action for grind parameters (#10472)
This PR adds a code action for `grind` parameters. We need to use
`set_option grind.param.codeAction true` to enable the option. The PR
also adds a modifier to instruct `grind` to use the "default" pattern
inference strategy.
2025-09-20 07:30:39 +00:00
Michał Dobranowski
8b3c82cce2 fix: lake: GH action template condition (#10459)
This PR fixes a conditional check in a GitHub Action template generated
by Lake.

Closes #10420.
2025-09-20 06:33:42 +00:00
Mac Malone
0d1b7e6c88 chore: lake: fix tests/lean (#10470)
The ordering of the `--setup` JSON object changed at some point,
breaking this test. This PR fixes it by avoiding the potential for such
breakages.
2025-09-20 02:11:50 +00:00
Leonardo de Moura
d898c9ed17 fix: grind canonicalizer (#10469)
This PR fixes an incorrect optimization in the `grind` canonicalizer.
See the new test for an example that exposes the problem.
2025-09-20 01:24:54 +00:00
Leonardo de Moura
c6abc3c036 feat: improve grind diagnostics (#10466)
This PR reduces noise in the 'Equivalence classes' section of the
`grind` diagnostics. It now uses a notion of *support expressions*.
Right now, it is hard-coded, but we will probably make it extensible in
the future. The current definition is

- `match`, `ite` and `dite`-applications. They have builtin support in
`grind`.
- Cast-like applications used by `grind`: `toQ`, `toInt`, `Nat.cast`,
`Int.cast`, and `cast`
- `grind` gadget applications (e.g., `Grind.nestedDecidable`)
- Projections of constructors (e.g., `{ x := 1, y := 2}.x`)
- Auxiliary arithmetic terms constructed by solvers such as `cutsat` and
`ring`.

If an equivalence class contains at most one non-support term, it goes
into the “others” bucket. Otherwise, we display the non-support elements
and place the support terms in a child node.

**BEFORE**:
<img width="1397" height="1558" alt="image"
src="https://github.com/user-attachments/assets/4fd4de31-7300-4158-908b-247024381243"
/>

**AFTER**:
<img width="840" height="340" alt="image"
src="https://github.com/user-attachments/assets/05020f34-4ade-49bf-8ccc-9eb0ba53c861"
/>

**Remark**: No information is lost; it is just grouped differently."
2025-09-19 23:44:30 +00:00
Leonardo de Moura
d07862db2a chore: skip cast-like operations in grind mbtc (#10465)
This PR skips cast-like helper `grind` functions during `grind mbtc`
2025-09-19 21:12:25 +00:00
Leonardo de Moura
8a79ef3633 chore: missing grind normalization (#10463)
This PR adds `Nat.sub_zero` as a `grind` normalization rule.
2025-09-19 18:50:39 +00:00
Leonardo de Moura
b1c82f776b chore: mbtc in grind cutsat (#10462)
Minor improvement to `grind mbtc` in `cutsat`.
2025-09-19 18:47:10 +00:00
Leonardo de Moura
f278f31469 fix: unnecessary case-splits in grind mbtc (#10461)
This PR fixes unnecessary case splits generated by the `grind mbtc`
module. Here, `mbtc` stands for model-based theory combination.
2025-09-19 17:24:57 +00:00
Joachim Breitner
38b4062edb feat: linear-size Ord instance (#10270)
This PR adds an alternative implementation of `Deriving Ord` based on
comparing `.ctorIdx` and using a dedicated matcher for comparing same
constructors (added in #10152). The new option
`deriving.ord.linear_construction_threshold` sets the constructor count
threshold (10 by default) for using the new construction.

It also (unconditionally) changes the implementation for enumeration
types to simply compare the `ctorIdx`.
2025-09-19 14:13:57 +00:00
Sebastian Graf
ae8dc414c3 feat: mvcgen invariants? to scaffold initial invariants (#10456)
This PR implements `mvcgen invariants?` for providing initial invariant
skeletons for the user to flesh out. When the loop body has an early
return, it will helpfully suggest `Invariant.withEarlyReturn ...` as a
skeleton.

```lean
def mySum (l : List Nat) : Nat := Id.run do
  let mut acc := 0
  for x in l do
    acc := acc + x
  return acc

/--
info: Try this:
  invariants
    · ⇓⟨xs, acc⟩ => _
-/
#guard_msgs (info) in
theorem mySum_suggest_invariant (l : List Nat) : mySum l = l.sum := by
  generalize h : mySum l = r
  apply Id.of_wp_run_eq h
  mvcgen invariants?
  all_goals admit

def nodup (l : List Int) : Bool := Id.run do
  let mut seen : HashSet Int := ∅
  for x in l do
    if x ∈ seen then
      return false
    seen := seen.insert x
  return true

/--
info: Try this:
  invariants
    · Invariant.withEarlyReturn (onReturn := fun r acc => _) (onContinue := fun xs acc => _)
-/
#guard_msgs (info) in
theorem nodup_suggest_invariant (l : List Int) : nodup l ↔ l.Nodup := by
  generalize h : nodup l = r
  apply Id.of_wp_run_eq h
  mvcgen invariants?
  all_goals admit
```
2025-09-19 14:05:24 +00:00
Sebastian Ullrich
7822ee4500 fix: check that compiler does not infer inconsistent types between modules (#10418)
This PR fixes a potential miscompilation when using non-exposed type
definitions using the module system by turning it into a static error. A
future revision may lift the restriction by making the compiler metadata
independent of the current module.
2025-09-19 12:36:47 +00:00
Joachim Breitner
8f22c56420 refactor: less public section in Eqns.lean files (#10454) 2025-09-19 11:52:56 +00:00
Joachim Breitner
0e122870be perf: mkNoConfusionCtors: cheaper inferType (#10455)
This PR changes `mkNoConfusionCtors` so that its use of `inferType` does
not have to reduce `noConfusionType`, to make #10315 really effective.
2025-09-19 10:51:17 +00:00
Sebastian Graf
13c23877d4 fix: reduce through lets in mvcgen main loop (#10453)
This PR makes `mvcgen` reduce through `let`s, so that it progresses over
`(have t := 42; fun _ => foo t) 23` by reduction to `have t := 42; foo
t` and then introducing `t`.
2025-09-19 08:21:04 +00:00
Kim Morrison
7fba12f8f7 chore: add test for website primes example (#10451)
(This test is currently failing. We either need to change the failing
line to `grind [!factorial_pos]`, or again change the behaviour of
grind.)
2025-09-19 04:56:28 +00:00
Kim Morrison
abb487a0c0 chore: include lean-lang.org in release checklist (#10450) 2025-09-19 03:46:32 +00:00
Leonardo de Moura
1091053824 chore: reportIssue! at grind ematch (#10449)
This PR ensures that issues reported by the E-matching module are
displayed only when `set_option grind.debug true` is enabled. Users
reported that these messages are too distracting and not very useful.
They are more valuable for library developers when annotating their
libraries.
2025-09-19 02:43:03 +00:00
Leonardo de Moura
545bd8a96c feat: add [grind inj] attribute (#10447)
This PR adds the `[grind inj]` attribute for marking injectivity
theorems for `grind`.
2025-09-19 00:49:05 +00:00
Sebastian Ullrich
2f9618f76b chore: make def used by proofwidgets public again (#10437) 2025-09-18 21:30:23 +00:00
Joachim Breitner
fa36fcd448 feat: linear-size BEq instance (#10268)
This PR adds an alternative implementation of `DerivingBEq` based on
comparing `.ctorIdx` and using a dedicated matcher for comparing same
constructors (added in #10152), to avoid the quadratic overhead of the
default match implementation. The new option
`deriving.beq.linear_construction_threshold` sets the constructor count
threshold (10 by default) for using the new construction. Such instances
also allow `deriving ReflBEq, LawfulBeq`, although these proofs for
these properties are still quadratic.
2025-09-18 21:27:25 +00:00
Henrik Böving
9a3b4b2716 fix: overeager inc insertion for large uint constants (#10444)
This PR fixes an overeager insertion of `inc` operations for large uint
constants.


Closes: #10443
2025-09-18 20:54:19 +00:00
Leonardo de Moura
9fb5ab8450 feat: helper definitions for injective function support in grind (#10445)
This PR adds helper definitions in preparation for the upcoming
injective function support in `grind`.
2025-09-18 19:42:15 +00:00
Leonardo de Moura
a62c0bce77 chore: missing grind modifier (#10441) 2025-09-18 14:44:57 +00:00
Lean stage0 autoupdater
3ce554abd7 chore: update stage0 2025-09-18 13:48:00 +00:00
Joachim Breitner
257c347f9f feat: reduceCtorIdx simproc (#10440)
This PR adds the `reduceCtorIdx` simproc which recognizes and reduces
`ctorIdx` applications. This is not on by default yet because it does
not use the discrimination tree (yet).
2025-09-18 13:05:14 +00:00
Sebastian Ullrich
ca1315e3ba fix: backtracking kernel errors under Elab.async (#10438)
This PR fixes an issue where notations and other overloadings would
signal kernel errors even though there exists a successful
interpretation.
2025-09-18 12:33:57 +00:00
Markus Himmel
197bc6cb66 feat: redefine String, part one (#10304)
This PR redefines `String` to be the type of byte arrays `b` for which
`b.IsValidUtf8`.

This moves the data model of strings much closer to the actual data
representation at runtime.

In the near future, we will

- provide variants of `String.Pos` and `Substring` that only allow for
valid positions
- redefine all `String` functions to be much closer to their C++
implementations

In the near-to-medium future we will then provide comprehensive
verification of `String` based on these refactors.
2025-09-18 11:36:52 +00:00
Luisa Cicolini
02ca710872 feat: add BitVec.ctz to bv_decide (#9298)
This PR adds support the Count Trailing Zeros operation `BitVec.ctz` to
the bitvector library and to `bv_decide`, relying on the existing `clz`
circuit. We also build some theory around `BitVec.ctz` (analogous to the
theory existing for `BitVec.clz`) and introduce lemmas
`BitVec.[ctz_eq_reverse_clz, clz_eq_reverse_ctz, ctz_lt_iff_ne_zero,
getLsbD_false_of_lt_ctz, getLsbD_true_ctz_of_ne_zero,
two_pow_ctz_le_toNat_of_ne_zero, reverse_reverse_eq,
reverse_eq_zero_iff]`.

`ctz` operation 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: Siddharth <siddu.druid@gmail.com>
2025-09-18 08:38:07 +00:00
Kim Morrison
3fbf080d72 chore: update script/release_notes.py for changelog-tactics (#10436) 2025-09-18 07:22:53 +00:00
5503 changed files with 109492 additions and 29658 deletions

14
.claude/CLAUDE.md Normal file
View File

@@ -0,0 +1,14 @@
When asked to implement new features:
* begin by reviewing existing relevant code and tests
* write comprehensive tests first (expecting that these will initially fail)
* and then iterate on the implementation until the tests pass.
To build Lean you should use `make -j$(nproc) -C build/release`.
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass. You have to keep working until you have verified both of these.
All new tests should go in `tests/lean/run/`. Note that these tests don't have expected output, and just run on a success or failure basis. So you should use `#guard_msgs` to check for specific messages.
If you are not following best practices specific to this repository and the user expresses frustration, stop and ask them to help update this `.claude/CLAUDE.md` file with the missing guidance.

View File

@@ -0,0 +1,69 @@
# Release Management Command
Execute the release process for a given version by running the release checklist and following its instructions.
## Before Starting
**IMPORTANT**: Before beginning the release process, read the in-file documentation:
- Read `script/release_checklist.py` for what the checklist script does
- Read `script/release_steps.py` for what the release steps script does
These comments explain the scripts' behavior, which repositories get special handling, and how errors are handled.
## Arguments
- `version`: The version to release (e.g., v4.24.0)
## Process
1. Run `script/release_checklist.py {version}` to check the current status
2. **CRITICAL: If preliminary lean4 checks fail, STOP immediately and alert the user**
- Check for: release branch exists, CMake version correct, tag exists, release page exists, release notes exist
- **IMPORTANT**: The release page is created AUTOMATICALLY by CI after pushing the tag - DO NOT create it manually
- Do NOT create any PRs or proceed with repository updates if these checks fail
3. Create a todo list tracking all repositories that need updates
4. **CRITICAL RULE: You can ONLY run `release_steps.py` for a repository if `release_checklist.py` explicitly says to do so**
- The checklist output will say "Run `script/release_steps.py {version} {repo_name}` to create it"
- If a repository shows "🟡 Dependencies not ready", you CANNOT create a PR for it yet
- You MUST rerun `release_checklist.py` before attempting to create PRs for any new repositories
5. For each repository that the checklist says needs updating:
- Run `script/release_steps.py {version} {repo_name}` to create the PR
- Mark it complete when the PR is created
6. After creating PRs, notify the user which PRs need review and merging
7. **MANDATORY: Rerun `release_checklist.py` to check current status**
- Do this after creating each batch of PRs
- Do this after the user reports PRs have been merged
- NEVER assume a repository is ready without checking the checklist output
8. As PRs are merged and tagged, dependent repositories will become ready
9. Continue the cycle: run checklist → create PRs for ready repos → wait for merges → repeat
10. Continue until all repositories are updated and the release is complete
## Important Notes
- The `release_steps.py` script is idempotent - it's safe to rerun
- The `release_checklist.py` script is idempotent - it's safe to rerun
- Some repositories depend on others (e.g., mathlib4 depends on batteries, aesop, etc.)
- Wait for user to merge PRs before dependent repos can be updated
- Alert user if anything unusual or scary happens
- Use appropriate timeouts for long-running builds (verso can take 10+ minutes)
- ProofWidgets4 uses semantic versioning (v0.0.X) - it's okay to create and push the next sequential tag yourself when needed for a release
## PR Status Reporting
Every time you run `release_checklist.py`, you MUST:
1. Parse the output to identify ALL open PRs mentioned (lines with "✅ PR with title ... exists")
2. Provide a summary to the user listing ALL open PRs that need review
3. Group them by status:
- PRs for repositories that are blocked by dependencies (show these but note they're blocked)
- PRs for repositories that are ready to merge (highlight these)
4. Format the summary clearly with PR numbers and URLs
This summary should be provided EVERY time you run the checklist, not just after creating new PRs.
The user needs to see the complete picture of what's waiting for review.
## Error Handling
**CRITICAL**: If something goes wrong or a command fails:
- **DO NOT** try to manually reproduce the failing steps yourself
- **DO NOT** try to fix things by running git commands or other manual operations
- Both scripts are idempotent and designed to handle partial completion gracefully
- If a script continues to fail after retrying, report the error to the user and wait for instructions

View File

@@ -12,7 +12,7 @@ jobs:
- name: Check awaiting-manual label
id: check-awaiting-manual-label
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const { labels, number: prNumber } = context.payload.pull_request;

View File

@@ -12,7 +12,7 @@ jobs:
- name: Check awaiting-mathlib label
id: check-awaiting-mathlib-label
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const { labels, number: prNumber } = context.payload.pull_request;

View File

@@ -3,9 +3,6 @@ name: build-template
on:
workflow_call:
inputs:
check-level:
type: string
required: true
config:
type: string
required: true
@@ -116,10 +113,10 @@ jobs:
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*' || '' }}
key: ${{ matrix.name }}-build-v3-${{ github.sha }}
key: ${{ matrix.name }}-build-v4-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-build-v3
${{ matrix.name }}-build-v4
# open nix-shell once for initial setup
- name: Setup
run: |
@@ -216,7 +213,7 @@ jobs:
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: matrix.release
with:
name: build-${{ matrix.name }}
@@ -230,7 +227,7 @@ jobs:
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/$TARGET_STAGE -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.test)
if: matrix.test
- name: Test Summary
uses: test-summary/action@v2
with:

View File

@@ -11,8 +11,8 @@ jobs:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha }}
filter: blob:none
fetch-depth: 0
filter: tree:0
- name: Find base commit
if: github.event_name == 'pull_request'
@@ -31,7 +31,7 @@ jobs:
- if: github.event_name == 'pull_request'
name: Set label
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const { owner, repo, number: issue_number } = context.issue;

View File

@@ -31,10 +31,6 @@ jobs:
configure:
runs-on: ubuntu-latest
outputs:
# 0: PRs without special label
# 1: PRs with `merge-ci` label, merge queue checks, master commits
# 2: PRs with `release-ci` label, releases (incl. nightlies)
check-level: ${{ steps.set-level.outputs.check-level }}
# The build matrix, dynamically generated here
matrix: ${{ steps.set-matrix.outputs.matrix }}
# secondary build jobs that should not block the CI success/merge queue
@@ -110,6 +106,9 @@ jobs:
TAG_NAME="${GITHUB_REF##*/}"
echo "RELEASE_TAG=$TAG_NAME" >> "$GITHUB_OUTPUT"
# 0: PRs without special label
# 1: PRs with `merge-ci` label, merge queue checks, master commits
# 2: PRs with `release-ci` label, releases (incl. nightlies)
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
@@ -117,6 +116,7 @@ jobs:
# rerun the workflow run after setting the `release-ci`/`merge-ci` labels.
run: |
check_level=0
fast=false
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
@@ -129,19 +129,24 @@ jobs:
elif echo "$labels" | grep -q "merge-ci"; then
check_level=1
fi
if echo "$labels" | grep -q "fast-ci"; then
fast=true
fi
fi
echo "check-level=$check_level" >> "$GITHUB_OUTPUT"
echo "fast=$fast" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const level = ${{ steps.set-level.outputs.check-level }};
console.log(`level: ${level}`);
const fast = ${{ steps.set-level.outputs.fast }};
console.log(`level: ${level}, fast: ${fast}`);
// use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
@@ -152,7 +157,8 @@ jobs:
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
"check-level": 2,
"enabled": level >= 2,
"test": true,
"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*",
@@ -165,17 +171,19 @@ jobs:
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
"os": "ubuntu-latest",
// usually not a bottleneck so make exclusive to `fast-ci`
"os": large && fast ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"release": true,
// Special handling for release jobs. We want:
// 1. To run it in PRs so developers get PR toolchains (so secondary is sufficient)
// 1. To run it in PRs so developers get PR toolchains (so secondary without tests 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,
"enabled": isPr || level != 1 || isPushToMaster,
"test": level >= 1,
"secondary": level == 0,
"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*",
@@ -186,26 +194,28 @@ jobs:
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"check-level": 0,
"test": true,
"enabled": true,
"check-rebootstrap": level >= 1,
"check-stage3": level >= 2,
"test": true,
// NOTE: `test-speedcenter` currently seems to be broken on `ubuntu-latest`
"test-speedcenter": large && level >= 2,
// made explicit until it can be assumed to have propagated to PRs
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// We are not warning-free yet on all platforms, start here
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-Werror",
},
{
"name": "Linux Reldebug",
"os": "ubuntu-latest",
"check-level": 2,
"enabled": level >= 2,
"test": true,
"CMAKE_PRESET": "reldebug",
},
// TODO: suddenly started failing in CI
/*{
"name": "Linux fsanitize",
"os": "ubuntu-latest",
"check-level": 2,
"enabled": level >= 2,
"test": true,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_PRESET": "sanitize",
// exclude seriously slow/problematic tests (laketests crash)
@@ -213,9 +223,10 @@ jobs:
},*/
{
"name": "macOS",
"os": "macos-13",
"os": "macos-15-intel",
"release": true,
"check-level": 2,
"test": false, // Tier 2 platform
"enabled": level >= 2,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
@@ -226,7 +237,7 @@ jobs:
{
"name": "macOS aarch64",
// standard GH runner only comes with 7GB so use large runner if possible when running tests
"os": large && !isPr ? "nscloud-macos-sonoma-arm64-6x14" : "macos-14",
"os": large && (fast || level >= 1) ? "nscloud-macos-sequoia-arm64-6x14" : "macos-15",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"shell": "bash -euxo pipefail {0}",
@@ -235,14 +246,16 @@ jobs:
"binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
// See "Linux release" for release job levels; Grove is not a concern here
"check-level": isPr ? 0 : 2,
"secondary": isPr,
"enabled": isPr || level != 1,
"test": level >= 1,
"secondary": level == 0,
},
{
"name": "Windows",
"os": large && level == 2 ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"os": large && (fast || level == 2) ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"release": true,
"check-level": 2,
"enabled": level >= 2,
"test": true,
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\"",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
@@ -254,7 +267,8 @@ jobs:
"os": "nscloud-ubuntu-22.04-arm64-4x16",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"check-level": 2,
"enabled": level >= 2,
"test": true,
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
@@ -267,7 +281,7 @@ jobs:
// "CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DPKG_CONFIG_EXECUTABLE=/usr/bin/i386-linux-gnu-pkg-config",
// "cmultilib": true,
// "release": true,
// "check-level": 2,
// "enabled": level >= 2,
// "cross": true,
// "shell": "bash -euxo pipefail {0}"
//}
@@ -279,7 +293,7 @@ jobs:
// "wasm": true,
// "cmultilib": true,
// "release": true,
// "check-level": 2,
// "enabled": level >= 2,
// "cross": true,
// "shell": "bash -euxo pipefail {0}",
// // Just a few selected tests because wasm is slow
@@ -293,7 +307,7 @@ jobs:
}
}
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
matrix = matrix.filter((job) => level >= job["check-level"]);
matrix = matrix.filter((job) => job["enabled"]);
core.setOutput('matrix', matrix.filter((job) => !job["secondary"]));
core.setOutput('matrix-secondary', matrix.filter((job) => job["secondary"]));
@@ -303,7 +317,6 @@ jobs:
uses: ./.github/workflows/build-template.yml
with:
config: ${{needs.configure.outputs.matrix}}
check-level: ${{ needs.configure.outputs.check-level }}
nightly: ${{ needs.configure.outputs.nightly }}
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
@@ -319,7 +332,6 @@ jobs:
uses: ./.github/workflows/build-template.yml
with:
config: ${{needs.configure.outputs.matrix-secondary}}
check-level: ${{ needs.configure.outputs.check-level }}
nightly: ${{ needs.configure.outputs.nightly }}
LEAN_VERSION_MAJOR: ${{ needs.configure.outputs.LEAN_VERSION_MAJOR }}
LEAN_VERSION_MINOR: ${{ needs.configure.outputs.LEAN_VERSION_MINOR }}
@@ -350,7 +362,7 @@ jobs:
content: |
A build of `${{ github.ref_name }}`, triggered by event `${{ github.event_name }}`, [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
- if: contains(needs.*.result, 'failure')
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
core.setFailed('Some jobs failed')
@@ -363,11 +375,11 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -392,8 +404,10 @@ jobs:
with:
# needed for tagging
fetch-depth: 0
# Doesn't seem to be working when additionally fetching from lean4-nightly
#filter: tree:0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: artifacts
- name: Prepare Nightly Release
@@ -411,7 +425,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@72f2c25fcb47643c292f7107632f7a47c1df5cd8
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
body_path: diff.md
prerelease: true

View File

@@ -110,7 +110,7 @@ jobs:
# material.
- id: deploy-alias
if: ${{ steps.should-run.outputs.should-run == 'true' }}
uses: actions/github-script@v7
uses: actions/github-script@v8
name: Compute Alias
with:
result-encoding: string

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- name: Add label based on comment
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

View File

@@ -11,7 +11,7 @@ jobs:
steps:
- name: Check PR body
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const { title, body, labels, draft } = context.payload.pull_request;

View File

@@ -48,17 +48,17 @@ 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}"
@@ -71,7 +71,7 @@ jobs:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -86,7 +86,7 @@ jobs:
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
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.
@@ -101,7 +101,7 @@ jobs:
- name: Report release status (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
await github.rest.repos.createCommitStatus({
@@ -115,7 +115,7 @@ jobs:
- name: Report release status (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
await github.rest.repos.createCommitStatus({
@@ -129,7 +129,7 @@ jobs:
- name: Add label
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
await github.rest.issues.addLabels({
@@ -200,7 +200,7 @@ jobs:
-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-mathlib-ci$"; then
echo "force-mathlib-ci label detected, forcing CI despite issues"
MESSAGE="Forcing Mathlib CI because the \`force-mathlib-ci\` label is present, despite problem: $MESSAGE"
@@ -301,7 +301,7 @@ jobs:
-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"
@@ -368,7 +368,7 @@ jobs:
- name: Report mathlib base
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }}
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const description =
@@ -401,6 +401,7 @@ jobs:
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
- name: Check if tag exists
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
@@ -425,7 +426,7 @@ jobs:
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 commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit --allow-empty -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 }}
@@ -434,7 +435,7 @@ jobs:
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 commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit --allow-empty -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -460,6 +461,7 @@ jobs:
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
- name: install elan
run: |
@@ -494,7 +496,7 @@ jobs:
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 }}"
git commit --allow-empty -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 and bumping Batteries."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
@@ -505,7 +507,7 @@ jobs:
git add lean-toolchain
lake update batteries
git add lake-manifest.json
git commit -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
git commit --allow-empty -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
@@ -530,6 +532,7 @@ jobs:
token: ${{ secrets.MANUAL_PR_BOT }}
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
filter: tree:0
- name: Check if tag in reference manual exists
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
@@ -555,7 +558,7 @@ jobs:
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 }}"
git commit --allow-empty -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 }}
@@ -565,7 +568,7 @@ jobs:
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 }}"
git commit --allow-empty -m "Update lean-toolchain for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes

View File

@@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check PR title
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const msg = context.payload.pull_request? context.payload.pull_request.title : context.payload.merge_group.head_commit.message;
console.log(`Message: ${msg}`)
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(msg)) {
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): (?![A-Z][a-z]).*[^.]($|\n\n)/.test(msg)) {
core.setFailed('PR title does not follow the Commit Convention (https://leanprover.github.io/lean4/doc/dev/commit_convention.html).');
}

View File

@@ -11,7 +11,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
days-before-stale: -1
days-before-pr-stale: 30

View File

@@ -69,10 +69,10 @@ jobs:
build/stage1/**/*.ir
build/stage1/**/*.c
build/stage1/**/*.c.o*
key: Linux Lake-build-v3-${{ github.sha }}
key: Linux Lake-build-v4-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
Linux Lake-build-v3
Linux Lake-build-v4
- if: env.should_update_stage0 == 'yes'
# sync options with `Linux Lake` to ensure cache reuse
run: |

1
.gitignore vendored
View File

@@ -20,7 +20,6 @@ tasks.json
settings.json
.gdb_history
.vscode/*
!.vscode/settings.json
script/__pycache__
*.produced.out
CMakeSettings.json

View File

@@ -7,9 +7,9 @@
/.github/ @kim-em
/RELEASES.md @kim-em
/src/kernel/ @leodemoura
/src/library/compiler/ @zwarich
/src/library/compiler/ @hargoniX
/src/lake/ @tydeu
/src/Lean/Compiler/ @leodemoura @zwarich
/src/Lean/Compiler/ @leodemoura @hargoniX
/src/Lean/Data/Lsp/ @mhuisi
/src/Lean/Elab/Deriving/ @kim-em
/src/Lean/Elab/Tactic/ @kim-em

View File

@@ -52,7 +52,7 @@ In the case of `@[extern]` all *irrelevant* types are removed first; see next se
Similarly, the signed integer types `Int8`, ..., `Int64`, `ISize` are also represented by the unsigned C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively, because they have a trivial structure.
* `Nat` and `Int` are represented by `lean_object *`.
Their runtime values is either a pointer to an opaque bignum object or, if the lowest bit of the "pointer" is 1 (`lean_is_scalar`), an encoded unboxed natural number or integer (`lean_box`/`lean_unbox`).
* A universe `Sort u`, type constructor `... → Sort u`, or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
* A universe `Sort u`, type constructor `... → Sort u`, `Void α` or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
* Any other type is represented by `lean_object *`.
Its runtime value is a pointer to an object of a subtype of `lean_object` (see the "Inductive types" section below) or the unboxed value `lean_box(cidx)` for the `cidx`th constructor of an inductive type if this constructor does not have any relevant parameters.
@@ -129,8 +129,7 @@ For all other modules imported by `lean`, the initializer is run without `builti
Thus `[init]` functions are run iff their module is imported, regardless of whether they have native code available or not, while `[builtin_init]` functions are only run for native executable or plugins, regardless of whether their module is imported or not.
`lean` uses built-in initializers for e.g. registering basic parsers that should be available even without importing their module (which is necessary for bootstrapping).
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.
The initializer for module `A.B` in a package `foo` is called `initialize_foo_A_B`. For modules in the Lean core (e.g., `Init.Prelude`), the initializer is called `initialize_Init_Prelude`. Module initializers will automatically initialize any imported modules. They are also 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.
@@ -141,8 +140,8 @@ 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 *);
lean_object * initialize_A_B(uint8_t builtin);
lean_object * initialize_C(uint8_t builtin);
...
argv = lean_setup_args(argc, argv); // if using process-related functionality
@@ -152,7 +151,7 @@ lean_initialize_runtime_module();
lean_object * res;
// use same default as for Lean executables
uint8_t builtin = 1;
res = initialize_A_B(builtin, lean_io_mk_world());
res = initialize_A_B(builtin);
if (lean_io_result_is_ok(res)) {
lean_dec_ref(res);
} else {
@@ -160,7 +159,7 @@ if (lean_io_result_is_ok(res)) {
lean_dec(res);
return ...; // do not access Lean declarations if initialization failed
}
res = initialize_C(builtin, lean_io_mk_world());
res = initialize_C(builtin);
if (lean_io_result_is_ok(res)) {
...

View File

@@ -59,7 +59,7 @@ All these tests are included by [src/shell/CMakeLists.txt](https://github.com/le
open Foo in
theorem tst2 (h : a ≤ b) : a + 2 ≤ b + 2 :=
Bla.
--^ textDocument/completion
--^ completion
```
In this example, the test driver [`test_single.sh`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/test_single.sh) will simulate an
auto-completion request at `Bla.`. The expected output is stored in

View File

@@ -94,10 +94,8 @@ theorem List.palindrome_of_eq_reverse (h : as.reverse = as) : Palindrome as := b
next => exact Palindrome.nil
next a => exact Palindrome.single a
next a b as ih =>
have : a = b := by simp_all
subst this
have : as.reverse = as := by simp_all
exact Palindrome.sandwich a (ih this)
obtain rfl, h, - := by simpa using h
exact Palindrome.sandwich b (ih h)
/-!
We now define a function that returns `true` iff `as` is a palindrome.

View File

@@ -25,6 +25,7 @@
} ({
buildInputs = with pkgs; [
cmake gmp libuv ccache pkg-config
llvmPackages.bintools # wrapped lld
llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
tree # for CI

View File

@@ -8,9 +8,15 @@
},
{
"path": "tests"
},
{
"path": "script"
}
],
"settings": {
// Open terminal at root, not current workspace folder
// (there is not way to directly refer to the root folder included as `.` above)
"terminal.integrated.cwd": "${workspaceFolder:src}/..",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"cmake.buildDirectory": "${workspaceFolder}/build/release",

View File

@@ -1,132 +0,0 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean
namespace Lean.Meta.Grind.Analyzer
/-!
A simple E-matching annotation analyzer.
For each theorem annotated as an E-matching candidate, it creates an artificial goal, executes `grind` and shows the
number of instances created.
For a theorem of the form `params -> type`, the artificial goal is of the form `params -> type -> False`.
-/
/--
`grind` configuration for the analyzer. We disable case-splits and lookahead,
increase the number of generations, and limit the number of instances generated.
-/
def config : Grind.Config := {
splits := 0
lookahead := false
mbtc := false
ematch := 20
instances := 100
gen := 10
}
structure Config where
/-- Minimum number of instantiations to trigger summary report -/
min : Nat := 10
/-- Minimum number of instantiations to trigger detailed report -/
detailed : Nat := 50
def mkParams : MetaM Params := do
let params Grind.mkParams config
let ematch getEMatchTheorems
let casesTypes Grind.getCasesTypes
return { params with ematch, casesTypes }
/-- Returns the total number of generated instances. -/
private def sum (cs : PHashMap Origin Nat) : Nat := Id.run do
let mut r := 0
for (_, c) in cs do
r := r + c
return r
private def thmsToMessageData (thms : PHashMap Origin Nat) : MetaM MessageData := do
let data := thms.toArray.filterMap fun (origin, c) =>
match origin with
| .decl declName => some (declName, c)
| _ => none
let data := data.qsort fun (d₁, c₁) (d₂, c₂) => if c₁ == c₂ then Name.lt d₁ d₂ else c₁ > c₂
let data data.mapM fun (declName, counter) =>
return .trace { cls := `thm } m!"{.ofConst (← mkConstWithLevelParams declName)} ↦ {counter}" #[]
return .trace { cls := `thm } "instances" data
/--
Analyzes theorem `declName`. That is, creates the artificial goal based on `declName` type,
and invokes `grind` on it.
-/
def analyzeEMatchTheorem (declName : Name) (c : Config) : MetaM Unit := do
let info getConstInfo declName
let mvarId forallTelescope info.type fun _ type => do
withLocalDeclD `h type fun _ => do
return ( mkFreshExprMVar (mkConst ``False)).mvarId!
let result Grind.main mvarId ( mkParams) (pure ())
let thms := result.counters.thm
let s := sum thms
if s > c.min then
IO.println s!"{declName} : {s}"
if s > c.detailed then
logInfo m!"{declName}\n{← thmsToMessageData thms}"
-- Not sure why this is failing: `down_pure` perhaps has an unnecessary universe parameter?
run_meta analyzeEMatchTheorem ``Std.Do.SPred.down_pure {}
/-- Analyzes all theorems in the standard library marked as E-matching theorems. -/
def analyzeEMatchTheorems (c : Config := {}) : MetaM Unit := do
let origins := ( getEMatchTheorems).getOrigins
let decls := origins.filterMap fun | .decl declName => some declName | _ => none
for declName in decls.mergeSort Name.lt do
try
analyzeEMatchTheorem declName c
catch e =>
logError m!"{declName} failed with {e.toMessageData}"
logInfo m!"Finished analyzing {decls.length} theorems"
/-- Macro for analyzing E-match theorems with unlimited heartbeats -/
macro "#analyzeEMatchTheorems" : command => `(
set_option maxHeartbeats 0 in
run_meta analyzeEMatchTheorems
)
#analyzeEMatchTheorems
-- -- We can analyze specific theorems using commands such as
set_option trace.grind.ematch.instance true
-- 1. grind immediately sees `(#[] : Array α) = ([] : List α).toArray` but probably this should be hidden.
-- 2. `Vector.toArray_empty` keys on `Array.mk []` rather than `#v[].toArray`
-- I guess we could add `(#[].extract _ _).extract _ _` as a stop pattern.
run_meta analyzeEMatchTheorem ``Array.extract_empty {}
-- Neither `Option.bind_some` nor `Option.bind_fun_some` fire, because the terms appear inside
-- lambdas. So we get crazy things like:
-- `fun x => ((some x).bind some).bind fun x => (some x).bind fun x => (some x).bind some`
-- We could consider replacing `filterMap_some` with
-- `filterMap g (filterMap f xs) = filterMap (f >=> g) xs`
-- to avoid the lambda that `grind` struggles with, but this would require more API around the fish.
run_meta analyzeEMatchTheorem ``Array.filterMap_some {}
-- Not entirely certain what is wrong here, but certainly
-- `eq_empty_of_append_eq_empty` is firing too often.
-- Ideally we could instantiate this is we fine `xs ++ ys` in the same equivalence class,
-- note just as soon as we see `xs ++ ys`.
-- I've tried removing this in https://github.com/leanprover/lean4/pull/10162
run_meta analyzeEMatchTheorem ``Array.range'_succ {}
-- Perhaps the same story here.
run_meta analyzeEMatchTheorem ``Array.range_succ {}
-- `zip_map_left` and `zip_map_right` are bad grind lemmas,
-- checking if they can be removed in https://github.com/leanprover/lean4/pull/10163
run_meta analyzeEMatchTheorem ``Array.zip_map {}
-- It seems crazy to me that as soon as we have `0 >>> n = 0`, we instantiate based on the
-- pattern `0 >>> n >>> m` by substituting `0` into `0 >>> n` to produce the `0 >>> n >>> n`.
-- I don't think any forbidden subterms can help us here. I don't know what to do. :-(
run_meta analyzeEMatchTheorem ``Int.zero_shiftRight {}

75
script/Modulize.lean Normal file
View File

@@ -0,0 +1,75 @@
import Lake.CLI.Main
/-!
Usage: `lean --run script/Modulize.lean [--meta] file1.lean file2.lean ...`
A simple script that inserts `module` and `public section` into un-modulized files and
bumps their imports to `public`.
When `--meta` is passed, `public meta section` and `public meta import` is used instead.
-/
open Lean Parser.Module
def main (args : List String) : IO Unit := do
let mut args := args
let mut doMeta := false
while !args.isEmpty && args[0]!.startsWith "-" do
match args[0]! with
| "--meta" => doMeta := true
| arg => throw <| .userError s!"unknown flag '{arg}'"
args := args.tail
for path in args do
-- Parse the input file
let mut text IO.FS.readFile path
let inputCtx := Parser.mkInputContext text path
let (header, parserState, msgs) Parser.parseHeader inputCtx
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
msgs.forM fun msg => msg.toString >>= IO.println
throw <| .userError "parse errors in file"
let `(header| $[module%$moduleTk?]? $imps:import*) := header
| throw <| .userError s!"unexpected header syntax of {path}"
if moduleTk?.isSome then
continue
-- initial whitespace if empty header
let startPos := header.raw.getPos? |>.getD parserState.pos
let dummyEnv mkEmptyEnvironment
let (initCmd, parserState', _) :=
Parser.parseCommand inputCtx { env := dummyEnv, options := {} } parserState msgs
-- insert section if any trailing command
if !initCmd.isOfKind ``Parser.Command.eoi then
let insertPos? :=
-- put below initial module docstring if any
guard (initCmd.isOfKind ``Parser.Command.moduleDoc) *> initCmd.getTailPos? <|>
-- else below header
header.raw.getTailPos?
let insertPos := insertPos?.getD startPos -- empty header
let mut sec := if doMeta then
"public meta section"
else
"@[expose] public section"
if !imps.isEmpty then
sec := "\n\n" ++ sec
if insertPos?.isNone then
sec := sec ++ "\n\n"
text := text.extract 0 insertPos ++ sec ++ text.extract insertPos text.rawEndPos
-- prepend each import with `public `
for imp in imps.reverse do
let insertPos := imp.raw.getPos?.get!
let prfx := if doMeta then "public meta " else "public "
text := text.extract 0 insertPos ++ prfx ++ text.extract insertPos text.rawEndPos
-- insert `module` header
let mut initText := text.extract 0 startPos
if !initText.trim.isEmpty then
-- If there is a header comment, preserve it and put `module` in the line after
initText := initText.trimRight ++ "\n"
text := initText ++ "module\n\n" ++ text.extract startPos text.rawEndPos
IO.FS.writeFile path text

616
script/Shake.lean Normal file
View File

@@ -0,0 +1,616 @@
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Sebastian Ullrich
-/
import Lake.CLI.Main
import Lean.ExtraModUses
/-! # `lake exe shake` command
This command will check the current project (or a specified target module) and all dependencies for
unused imports. This works by looking at generated `.olean` files to deduce required imports and
ensuring that every import is used to contribute some constant or other elaboration dependency
recorded by `recordExtraModUse`. Because recompilation is not needed this is quite fast (about 8
seconds to check `Mathlib` and all dependencies).
-/
/-- help string for the command line interface -/
def help : String := "Lean project tree shaking tool
Usage: lake exe shake [OPTIONS] <MODULE>..
Arguments:
<MODULE>
A module path like `Mathlib`. All files transitively reachable from the
provided module(s) will be checked.
Options:
--force
Skips the `lake build --no-build` sanity check
--fix
Apply the suggested fixes directly. Make sure you have a clean checkout
before running this, so you can review the changes.
"
open Lean
/-- We use `Nat` as a bitset for doing efficient set operations.
The bit indexes will usually be a module index. -/
structure Bitset where
toNat : Nat
deriving Inhabited, DecidableEq, Repr
namespace Bitset
instance : EmptyCollection Bitset where
emptyCollection := { toNat := 0 }
instance : Insert Nat Bitset where
insert i s := { toNat := s.toNat ||| (1 <<< i) }
instance : Singleton Nat Bitset where
singleton i := insert i
instance : Inter Bitset where
inter a b := { toNat := a.toNat &&& b.toNat }
instance : Union Bitset where
union a b := { toNat := a.toNat ||| b.toNat }
instance : XorOp Bitset where
xor a b := { toNat := a.toNat ^^^ b.toNat }
def has (s : Bitset) (i : Nat) : Bool := s {i}
end Bitset
/-- The kind of a module dependency, corresponding to the homonymous `ExtraModUse` fields. -/
structure NeedsKind where
isExported : Bool
isMeta : Bool
deriving Inhabited, BEq, Repr, Hashable
namespace NeedsKind
@[match_pattern] abbrev priv : NeedsKind := { isExported := false, isMeta := false }
@[match_pattern] abbrev pub : NeedsKind := { isExported := true, isMeta := false }
@[match_pattern] abbrev metaPriv : NeedsKind := { isExported := false, isMeta := true }
@[match_pattern] abbrev metaPub : NeedsKind := { isExported := true, isMeta := true }
def all : Array NeedsKind := #[pub, priv, metaPub, metaPriv]
def ofImport : Lean.Import NeedsKind
| { isExported := true, isMeta := true, .. } => .metaPub
| { isExported := true, isMeta := false, .. } => .pub
| { isExported := false, isMeta := true, .. } => .metaPriv
| { isExported := false, isMeta := false, .. } => .priv
end NeedsKind
/-- Logically, a map `NeedsKind → Bitset`. -/
structure Needs where
pub : Bitset
priv : Bitset
metaPub : Bitset
metaPriv : Bitset
deriving Inhabited, Repr
def Needs.empty : Needs := default
def Needs.get (needs : Needs) (k : NeedsKind) : Bitset :=
match k with
| .pub => needs.pub
| .priv => needs.priv
| .metaPub => needs.metaPub
| .metaPriv => needs.metaPriv
def Needs.has (needs : Needs) (k : NeedsKind) (i : ModuleIdx) : Bool :=
needs.get k |>.has i
def Needs.set (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
match k with
| .pub => { needs with pub := s }
| .priv => { needs with priv := s }
| .metaPub => { needs with metaPub := s }
| .metaPriv => { needs with metaPriv := s }
def Needs.modify (needs : Needs) (k : NeedsKind) (f : Bitset Bitset) : Needs :=
needs.set k (f (needs.get k))
def Needs.union (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
needs.modify k (· s)
def Needs.sub (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
needs.modify k (fun s' => s' ^^^ (s' s))
/-- The main state of the checker, containing information on all loaded modules. -/
structure State where
env : Environment
/--
`transDeps[i]` is the (non-reflexive) transitive closure of `mods[i].imports`. More specifically,
* `j ∈ transDeps[i].pub` if `i -(public import)->+ j`
* `j ∈ transDeps[i].priv` if `i -(import ...)-> _ -(public import)->* j`
* `j ∈ transDeps[i].priv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].pub/priv`
* `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import)->* j`
* `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import)->* j`
* `j ∈ transDeps[i].metaPriv` if `i -(import ...)-> i'` and `j ∈ transDeps[i'].metaPub`
* `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].metaPub/metaPriv`
-/
transDeps : Array Needs := #[]
/--
`transDepsOrig` is the initial value of `transDeps` before changes potentially resulting from
changes to upstream headers.
-/
transDepsOrig : Array Needs := #[]
def State.mods (s : State) := s.env.header.moduleData
def State.modNames (s : State) := s.env.header.moduleNames
/--
Given module `j`'s transitive dependencies, computes the union of `transImps` and the transitive
dependencies resulting from importing the module via `imp` according to the rules of
`State.transDeps`.
-/
def addTransitiveImps (transImps : Needs) (imp : Import) (j : Nat) (impTransImps : Needs) : Needs := Id.run do
let mut transImps := transImps
-- `j ∈ transDeps[i].pub` if `i -(public import)->+ j`
if imp.isExported && !imp.isMeta then
transImps := transImps.union .pub {j} |>.union .pub (impTransImps.get .pub)
if !imp.isExported && !imp.isMeta then
-- `j ∈ transDeps[i].priv` if `i -(import ...)-> _ -(public import)->* j`
transImps := transImps.union .priv {j} |>.union .priv (impTransImps.get .pub)
if imp.importAll then
-- `j ∈ transDeps[i].priv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].pub/priv`
transImps := transImps.union .priv (impTransImps.get .pub impTransImps.get .priv)
-- `j ∈ transDeps[i].metaPub` if `i -(public (meta)? import)->* _ -(public meta import)-> _ -(public (meta)? import)->* j`
if imp.isExported then
transImps := transImps.union .metaPub (impTransImps.get .metaPub)
if imp.isMeta then
transImps := transImps.union .metaPub {j} |>.union .metaPub (impTransImps.get .pub impTransImps.get .metaPub)
if !imp.isExported then
if imp.isMeta then
-- `j ∈ transDeps[i].metaPriv` if `i -(meta import ...)-> _ -(public (meta)? import)->* j`
transImps := transImps.union .metaPriv {j} |>.union .metaPriv (impTransImps.get .pub impTransImps.get .metaPub)
if imp.importAll then
-- `j ∈ transDeps[i].metaPriv` if `i -(import all)->+ i'` and `j ∈ transDeps[i'].metaPub/metaPriv`
transImps := transImps.union .metaPriv (impTransImps.get .metaPub impTransImps.get .metaPriv)
else
-- `j ∈ transDeps[i].metaPriv` if `i -(import ...)-> i'` and `j ∈ transDeps[i'].metaPub`
transImps := transImps.union .metaPriv (impTransImps.get .metaPub)
transImps
/-- Calculates the needs for a given module `mod` from constants and recorded extra uses. -/
def calcNeeds (env : Environment) (i : ModuleIdx) : Needs := Id.run do
let mut needs := default
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
let pubCI? := guard (!isPrivateName ci.name) *> (env.setExporting true).find? ci.name
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
needs := visitExpr k ci.type needs
if let some e := ci.value? (allowOpaque := true) then
-- type and value has identical visibility under `meta`
let k := if k.isMeta then k else
if pubCI?.any (·.hasValue (allowOpaque := true)) then .pub else .priv
needs := visitExpr k e needs
for use in getExtraModUses env i do
let j := env.getModuleIdx? use.module |>.get!
needs := needs.union { use with } {j}
return needs
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) e deps :=
Lean.Expr.foldConsts e deps fun c deps => match env.getModuleIdxFor? c with
| some j =>
let k := { k with isMeta := k.isMeta && !isMeta env c }
if j != i then deps.union k {j} else deps
| _ => deps
/--
Calculates the same as `calcNeeds` but tracing each module to a use-def declaration pair or
`none` if merely a recorded extra use.
-/
def getExplanations (env : Environment) (i : ModuleIdx) :
Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name)) := Id.run do
let mut deps := default
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
let pubCI? := guard (!isPrivateName ci.name) *> (env.setExporting true).find? ci.name
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
deps := visitExpr k ci.name ci.type deps
if let some e := ci.value? (allowOpaque := true) then
let k := if k.isMeta then k else
if pubCI?.any (·.hasValue (allowOpaque := true)) then .pub else .priv
deps := visitExpr k ci.name e deps
for use in getExtraModUses env i do
let j := env.getModuleIdx? use.module |>.get!
if !deps.contains (j, { use with }) then
deps := deps.insert (j, { use with }) none
return deps
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) name e deps :=
Lean.Expr.foldConsts e deps fun c deps => match env.getModuleIdxFor? c with
| some i =>
let k := { k with isMeta := k.isMeta && !isMeta env c }
if
if let some (some (name', _)) := deps[(i, k)]? then
decide (name.toString.length < name'.toString.length)
else true
then
deps.insert (i, k) (name, c)
else
deps
| _ => deps
partial def initStateFromEnv (env : Environment) : State := Id.run do
let mut s := { env }
for i in 0...env.header.moduleData.size do
let mod := env.header.moduleData[i]!
let mut imps := #[]
let mut transImps := Needs.empty
for imp in mod.imports do
let j := env.getModuleIdx? imp.module |>.get!
imps := imps.push j
transImps := addTransitiveImps transImps imp j s.transDeps[j]!
s := { s with transDeps := s.transDeps.push transImps }
s := { s with transDepsOrig := s.transDeps }
return s
/-- The list of edits that will be applied in `--fix`. `edits[i] = (removed, added)` where:
* If `j ∈ removed` then we want to delete module named `j` from the imports of `i`
* If `j ∈ added` then we want to add module index `j` to the imports of `i`.
-/
abbrev Edits := Std.HashMap Name (Array Import × Array Import)
/-- Register that we want to remove `tgt` from the imports of `src`. -/
def Edits.remove (ed : Edits) (src : Name) (tgt : Import) : Edits :=
match ed.get? src with
| none => ed.insert src (#[tgt], #[])
| some (a, b) => ed.insert src (a.push tgt, b)
/-- Register that we want to add `tgt` to the imports of `src`. -/
def Edits.add (ed : Edits) (src : Name) (tgt : Import) : Edits :=
match ed.get? src with
| none => ed.insert src (#[], #[tgt])
| some (a, b) => ed.insert src (a, b.push tgt)
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.Module.import` list
and `endPos` is the position of the end of the header.
-/
def parseHeaderFromString (text path : String) :
IO (System.FilePath × Parser.InputContext ×
TSyntax ``Parser.Module.header × String.Pos.Raw) := do
let inputCtx := Parser.mkInputContext text path
let (header, parserState, msgs) Parser.parseHeader inputCtx
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
msgs.forM fun msg => msg.toString >>= IO.println
throw <| .userError "parse errors in file"
-- the insertion point for `add` is the first newline after the imports
let insertion := header.raw.getTailPos?.getD parserState.pos
let insertion := text.findAux (· == '\n') text.endPos insertion + '\n'
pure (path, inputCtx, header, insertion)
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.Module.import` list
and `endPos` is the position of the end of the header.
-/
def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
IO (System.FilePath × Parser.InputContext ×
TSyntax ``Parser.Module.header × String.Pos.Raw) := do
-- Parse the input file
let some path srcSearchPath.findModuleWithExt "lean" mod
| throw <| .userError s!"error: failed to find source file for {mod}"
let text IO.FS.readFile path
parseHeaderFromString text path.toString
def decodeHeader : TSyntax ``Parser.Module.header Option (TSyntax `module) × Option (TSyntax `prelude) × TSyntaxArray ``Parser.Module.import
| `(Parser.Module.header| $[module%$moduleTk?]? $[prelude%$preludeTk?]? $imports*) =>
(moduleTk?.map .mk, preludeTk?.map .mk, imports)
| _ => unreachable!
def decodeImport : TSyntax ``Parser.Module.import Import
| `(Parser.Module.import| $[public%$pubTk?]? $[meta%$metaTk?]? import $[all%$allTk?]? $id) =>
{ module := id.getId, isExported := pubTk?.isSome, isMeta := metaTk?.isSome, importAll := allTk?.isSome }
| stx => panic! s!"unexpected syntax {stx}"
/-- Analyze and report issues from module `i`. Arguments:
* `srcSearchPath`: Used to find the path for error reporting purposes
* `i`: the module index
* `needs`: the module's calculated needs
* `pinned`: dependencies that should be preserved even if unused
* `edits`: accumulates the list of edits to apply if `--fix` is true
* `addOnly`: if true, only add missing imports, do not remove unused ones
-/
def visitModule (srcSearchPath : SearchPath)
(i : Nat) (needs : Needs) (preserve : Needs) (edits : Edits) (headerStx : TSyntax ``Parser.Module.header)
(addOnly := false) (githubStyle := false) (explain := false) : StateT State IO Edits := do
let s get
-- Do transitive reduction of `needs` in `deps`.
let mut deps := needs
let (_, prelude?, imports) := decodeHeader headerStx
if prelude?.isNone then
deps := deps.union .pub {s.env.getModuleIdx? `Init |>.get!}
for imp in imports do
if addOnly || imp.raw.getTrailing?.any (·.toString.toSlice.contains "shake: keep") then
let imp := decodeImport imp
let j := s.env.getModuleIdx? imp.module |>.get!
let k := NeedsKind.ofImport imp
deps := deps.union k {j}
for j in [0:s.mods.size] do
let transDeps := s.transDeps[j]!
for k in NeedsKind.all do
if s.transDepsOrig[i]!.has k j && preserve.has k j then
deps := deps.union k {j}
if deps.has k j then
let transDeps := addTransitiveImps .empty { k with module := .anonymous } j transDeps
for k' in NeedsKind.all do
deps := deps.sub k' (transDeps.sub k' {j} |>.get k')
-- Any import which is not in `transDeps` was unused.
-- Also accumulate `newDeps` which is the transitive closure of the remaining imports
let mut toRemove : Array Import := #[]
let mut newDeps := Needs.empty
for imp in s.mods[i]!.imports do
let j := s.env.getModuleIdx? imp.module |>.get!
if
-- skip folder-nested imports
s.modNames[i]!.isPrefixOf imp.module ||
imp.importAll then
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
else
let k := NeedsKind.ofImport imp
-- A private import should also be removed if the public version is needed
if !deps.has k j || !k.isExported && deps.has { k with isExported := true } j then
toRemove := toRemove.push imp
else
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
-- If `newDeps` does not cover `deps`, then we have to add back some imports until it does.
-- To minimize new imports we pick only new imports which are not transitively implied by
-- another new import
let mut toAdd : Array Import := #[]
for j in [0:s.mods.size] do
for k in NeedsKind.all do
if deps.has k j && !newDeps.has k j && !newDeps.has { k with isExported := true } j then
let imp := { k with module := s.modNames[j]! }
toAdd := toAdd.push imp
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
-- mark and report the removals
let mut edits := toRemove.foldl (init := edits) fun edits imp =>
edits.remove s.modNames[i]! imp
if !toAdd.isEmpty || !toRemove.isEmpty || explain then
if let some path srcSearchPath.findModuleWithExt "lean" s.modNames[i]! then
println! "{path}:"
else
println! "{s.modNames[i]!}:"
if !toRemove.isEmpty then
println! " remove {toRemove}"
if githubStyle then
try
let (path, inputCtx, stx, endHeader) parseHeader srcSearchPath s.modNames[i]!
let (_, _, imports) := decodeHeader stx
for stx in imports do
if toRemove.any fun imp => imp == decodeImport stx then
let pos := inputCtx.fileMap.toPosition stx.raw.getPos?.get!
println! "{path}:{pos.line}:{pos.column+1}: warning: unused import \
(use `lake exe shake --fix` to fix this, or `lake exe shake --update` to ignore)"
if !toAdd.isEmpty then
-- we put the insert message on the beginning of the last import line
let pos := inputCtx.fileMap.toPosition endHeader
println! "{path}:{pos.line-1}:1: warning: \
add {toAdd} instead"
catch _ => pure ()
-- mark and report the additions
edits := toAdd.foldl (init := edits) fun edits imp =>
edits.add s.modNames[i]! imp
if !toAdd.isEmpty then
println! " add {toAdd}"
-- recalculate transitive dependencies of downstream modules
let mut newTransDepsI := Needs.empty
for imp in s.mods[i]!.imports do
if !toRemove.contains imp then
let j := s.env.getModuleIdx? imp.module |>.get!
newTransDepsI := addTransitiveImps newTransDepsI imp j s.transDeps[j]!
for imp in toAdd do
let j := s.env.getModuleIdx? imp.module |>.get!
newTransDepsI := addTransitiveImps newTransDepsI imp j s.transDeps[j]!
set { s with transDeps := s.transDeps.set! i newTransDepsI }
if explain then
let explanation := getExplanations s.env i
let sanitize n := if n.hasMacroScopes then (sanitizeName n).run' { options := {} } else n
let run (imp : Import) := do
let j := s.env.getModuleIdx? imp.module |>.get!
if let some exp? := explanation[(j, NeedsKind.ofImport imp)]? then
println! " note: `{imp}` required"
if let some (n, c) := exp? then
println! " because `{sanitize n}` refers to `{sanitize c}`"
else
println! " because of additional compile-time dependencies"
for j in s.mods[i]!.imports do
if !toRemove.contains j then
run j
for i in toAdd do run i
return edits
/-- Convert a list of module names to a bitset of module indexes -/
def toBitset (s : State) (ns : List Name) : Bitset :=
ns.foldl (init := ) fun c name =>
match s.env.getModuleIdxFor? name with
| some i => c {i}
| none => c
/-- The parsed CLI arguments. See `help` for more information -/
structure Args where
/-- `--help`: shows the help -/
help : Bool := false
/-- `--force`: skips the `lake build --no-build` sanity check -/
force : Bool := false
/-- `--gh-style`: output messages that can be parsed by `gh-problem-matcher-wrap` -/
githubStyle : Bool := false
/-- `--explain`: give constants explaining why each module is needed -/
explain : Bool := false
/-- `--fix`: apply the fixes directly -/
fix : Bool := false
/-- `<MODULE>..`: the list of root modules to check -/
mods : Array Name := #[]
local instance : Ord Import where
compare a b :=
if a.isExported && !b.isExported then
Ordering.lt
else if !a.isExported && b.isExported then
Ordering.gt
else
a.module.cmp b.module
/-- The main entry point. See `help` for more information on arguments. -/
def main (args : List String) : IO UInt32 := do
initSearchPath ( findSysroot)
-- Parse the arguments
let rec parseArgs (args : Args) : List String Args
| [] => args
| "--help" :: rest => parseArgs { args with help := true } rest
| "--force" :: rest => parseArgs { args with force := true } rest
| "--fix" :: rest => parseArgs { args with fix := true } rest
| "--explain" :: rest => parseArgs { args with explain := true } rest
| "--gh-style" :: rest => parseArgs { args with githubStyle := true } rest
| "--" :: rest => { args with mods := args.mods ++ rest.map (·.toName) }
| other :: rest => parseArgs { args with mods := args.mods.push other.toName } rest
let args := parseArgs {} args
-- Bail if `--help` is passed
if args.help then
IO.println help
IO.Process.exit 0
if !args.force then
if ( IO.Process.output { cmd := "lake", args := #["build", "--no-build"] }).exitCode != 0 then
IO.println "There are out of date oleans. Run `lake build` or `lake exe cache get` first"
IO.Process.exit 1
-- Determine default module(s) to run shake on
let defaultTargetModules : Array Name try
let (elanInstall?, leanInstall?, lakeInstall?) Lake.findInstall?
let config Lake.MonadError.runEIO <| Lake.mkLoadConfig { elanInstall?, leanInstall?, lakeInstall? }
let some workspace Lake.loadWorkspace config |>.toBaseIO
| throw <| IO.userError "failed to load Lake workspace"
let defaultTargetModules := workspace.root.defaultTargets.flatMap fun target =>
if let some lib := workspace.root.findLeanLib? target then
lib.roots
else if let some exe := workspace.root.findLeanExe? target then
#[exe.config.root]
else
#[]
pure defaultTargetModules
catch _ =>
pure #[]
let srcSearchPath getSrcSearchPath
-- the list of root modules
let mods := if args.mods.isEmpty then defaultTargetModules else args.mods
-- Only submodules of `pkg` will be edited or have info reported on them
let pkg := mods[0]!.components.head!
-- Load all the modules
let imps := mods.map ({ module := · })
let (_, s) importModulesCore imps (isExported := true) |>.run
let s := s.markAllExported
let env finalizeImport s (isModule := true) imps {} (leakEnv := false) (loadExts := false)
StateT.run' (s := initStateFromEnv env) do
let s get
-- Parse the config file
-- Run the calculation of the `needs` array in parallel
let needs := s.mods.mapIdx fun i _ =>
Task.spawn fun _ => calcNeeds s.env i
-- Parse headers in parallel
let headers s.mods.mapIdxM fun i _ =>
BaseIO.asTask (parseHeader srcSearchPath s.modNames[i]! |>.toBaseIO)
if args.fix then
println! "The following changes will be made automatically:"
-- Check all selected modules
let mut edits : Edits :=
let mut revNeeds : Needs := default
for i in [0:s.mods.size], t in needs, header in headers do
match header.get with
| .ok (_, _, stx, _) =>
edits visitModule (addOnly := !pkg.isPrefixOf s.modNames[i]!)
srcSearchPath i t.get revNeeds edits stx args.githubStyle args.explain
if isExtraRevModUse s.env i then
revNeeds := revNeeds.union .priv {i}
| .error e =>
println! e.toString
if !args.fix then
-- return error if any issues were found
return if edits.isEmpty then 0 else 1
-- Apply the edits to existing files
let mut count := 0
for mod in s.modNames, header? in headers do
let some (remove, add) := edits[mod]? | continue
let add : Array Import := add.qsortOrd
-- Parse the input file
let .ok (path, inputCtx, stx, insertion) := header?.get | continue
let (_, _, imports) := decodeHeader stx
let text := inputCtx.fileMap.source
-- Calculate the edit result
let mut pos : String.Pos.Raw := 0
let mut out : String := ""
let mut seen : Std.HashSet Import := {}
for stx in imports do
let mod := decodeImport stx
if remove.contains mod || seen.contains mod then
out := out ++ text.extract pos stx.raw.getPos?.get!
-- We use the end position of the syntax, but include whitespace up to the first newline
pos := text.findAux (· == '\n') text.rawEndPos stx.raw.getTailPos?.get! + '\n'
seen := seen.insert mod
out := out ++ text.extract pos insertion
for mod in add do
if !seen.contains mod then
seen := seen.insert mod
out := out ++ s!"{mod}\n"
out := out ++ text.extract insertion text.rawEndPos
IO.FS.writeFile path out
count := count + 1
-- Since we throw an error upon encountering issues, we can be sure that everything worked
-- if we reach this point of the script.
if count > 0 then
println! "Successfully applied {count} suggestions."
else
println! "No edits required."
return 0

View File

@@ -60,7 +60,7 @@ if (arity == fixed + {n}) \{
for j in [n:max + 1] do
let fs := mkFsArgs (j - n)
let sep := if j = n then "" else ", "
emit s!" case {j}: \{ obj* r = FN{j}(f)({fs}{sep}{args}); lean_free_small_object(f); return r; }\n"
emit s!" case {j}: \{ obj* r = FN{j}(f)({fs}{sep}{args}); lean_free_object(f); return r; }\n"
emit " }
}
switch (arity) {\n"
@@ -162,7 +162,7 @@ static obj* fix_args(obj* f, unsigned n, obj*const* as) {
for (unsigned i = 0; i < fixed; i++, source++, target++) {
*target = *source;
}
lean_free_small_object(f);
lean_free_object(f);
}
for (unsigned i = 0; i < n; i++, as++, target++) {
*target = *as;

View File

@@ -10,6 +10,16 @@ Tests language server memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
-/
def determineRSS (pid : UInt32) : IO Nat := do
let status IO.FS.readFile s!"/proc/{pid}/smaps_rollup"
let some rssLine := status.splitOn "\n" |>.find? (·.startsWith "Rss:")
| throw <| IO.userError "No RSS in proc status"
let rssLine := rssLine.dropPrefix "Rss:"
let rssLine := rssLine.dropWhile Char.isWhitespace
let some rssInKB := rssLine.takeWhile Char.isDigit |>.toNat?
| throw <| IO.userError "Cannot parse RSS"
return rssInKB
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let file IO.FS.realPath file
@@ -34,11 +44,14 @@ def main (args : List String) : IO Unit := do
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let n := iters.toNat!
let mut lastRSS? : Option Nat := none
let mut totalRSSDelta : Int := 0
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }
for i in [0:iters.toNat!] do
for i in [0:n] do
if i > 0 then
versionNo := versionNo + 1
let params : DidChangeTextDocumentParams := {
@@ -61,9 +74,16 @@ def main (args : List String) : IO Unit := do
IO.eprintln diag.message
requestNo := requestNo + 1
let status IO.FS.readFile s!"/proc/{(← read).pid}/status"
for line in status.splitOn "\n" |>.filter (·.startsWith "RssAnon") do
IO.eprintln line
let rss determineRSS ( read).pid
-- The first `didChange` usually results in a significantly higher RSS increase than
-- the others, so we ignore it.
if i > 1 then
if let some lastRSS := lastRSS? then
totalRSSDelta := totalRSSDelta + ((rss : Int) - (lastRSS : Int))
lastRSS? := some rss
let avgRSSDelta := totalRSSDelta / (n - 2)
IO.println s!"avg-reelab-rss-delta: {avgRSSDelta}"
let _ Ipc.collectDiagnostics requestNo uri versionNo
( Ipc.stdin).writeLspMessage (Message.notification "exit" none)

View File

@@ -0,0 +1,89 @@
import Lean.Data.Lsp
import Lean.Elab.Import
open Lean
open Lean.Lsp
open Lean.JsonRpc
/-!
Tests watchdog memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
-/
def determineRSS (pid : UInt32) : IO Nat := do
let status IO.FS.readFile s!"/proc/{pid}/smaps_rollup"
let some rssLine := status.splitOn "\n" |>.find? (·.startsWith "Rss:")
| throw <| IO.userError "No RSS in proc status"
let rssLine := rssLine.dropPrefix "Rss:"
let rssLine := rssLine.dropWhile Char.isWhitespace
let some rssInKB := rssLine.takeWhile Char.isDigit |>.toNat?
| throw <| IO.userError "Cannot parse RSS"
return rssInKB
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let file IO.FS.realPath file
let uri := s!"file://{file}"
Ipc.runWith leanCmd (#["--server", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
let capabilities := {
textDocument? := some {
completion? := some {
completionItem? := some {
insertReplaceSupport? := true
}
}
}
}
Ipc.writeRequest 0, "initialize", { capabilities : InitializeParams }
discard <| Ipc.readResponseAs 0 InitializeResult
Ipc.writeNotification "initialized", InitializedParams.mk
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let n := iters.toNat!
let mut lastRSS? : Option Nat := none
let mut totalRSSDelta : Int := 0
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }
for i in [0:iters.toNat!] do
if i > 0 then
versionNo := versionNo + 1
let params : DidChangeTextDocumentParams := {
textDocument := {
uri := uri
version? := versionNo
}
contentChanges := #[TextDocumentContentChangeEvent.rangeChange {
start := headerEndPos
«end» := headerEndPos
} " "]
}
let params := toJson params
Ipc.writeNotification "textDocument/didChange", params
requestNo := requestNo + 1
let diags Ipc.collectDiagnostics requestNo uri versionNo
if let some diags := diags then
for diag in diags.param.diagnostics do
IO.eprintln diag.message
requestNo := requestNo + 1
Ipc.waitForILeans requestNo uri versionNo
let rss determineRSS ( read).pid
-- The first `didChange` usually results in a significantly higher RSS increase than
-- the others, so we ignore it.
if i > 1 then
if let some lastRSS := lastRSS? then
totalRSSDelta := totalRSSDelta + ((rss : Int) - (lastRSS : Int))
lastRSS? := some rss
let avgRSSDelta := totalRSSDelta / (n - 2)
IO.println s!"avg-reelab-rss-delta: {avgRSSDelta}"
let _ Ipc.collectDiagnostics requestNo uri versionNo
Ipc.shutdown requestNo
discard <| Ipc.waitForExit

9
script/lakefile.toml Normal file
View File

@@ -0,0 +1,9 @@
name = "scripts"
[[lean_exe]]
name = "modulize"
root = "Modulize"
[[lean_exe]]
name = "shake"
root = "Shake"

1
script/lean-toolchain Normal file
View File

@@ -0,0 +1 @@
lean4

View File

@@ -1,5 +1,58 @@
#!/usr/bin/env python3
"""
Release Checklist for Lean4 and Downstream Repositories
This script validates the status of a Lean4 release across all dependent repositories.
It checks whether repositories are ready for release and identifies missing steps.
IMPORTANT: Keep this documentation up-to-date when modifying the script's behavior!
What this script does:
1. Validates preliminary Lean4 release infrastructure:
- Checks that the release branch (releases/vX.Y.0) exists
- Verifies CMake version settings are correct
- Confirms the release tag exists
- Validates the release page exists on GitHub (created automatically by CI after tag push)
- Checks the release notes page on lean-lang.org (updated while bumping the `reference-manual` repository)
**IMPORTANT: If the release page doesn't exist, the script will skip checking
downstream repositories and the master branch configuration. The preliminary
infrastructure must be in place before the release process can proceed.**
**NOTE: The GitHub release page is created AUTOMATICALLY by CI after the tag is pushed.
DO NOT create it manually. Wait for CI to complete after pushing the tag.**
2. For each downstream repository (batteries, mathlib4, etc.):
- Checks if dependencies are ready (e.g., mathlib4 depends on batteries)
- Verifies the main branch is on the target toolchain (or newer)
- Checks if a PR exists to bump the toolchain (if not yet updated)
- Validates tags exist for the release version
- Ensures tags are merged into stable branches (for non-RC releases)
- Verifies bump branches exist and are configured correctly
- Special handling for ProofWidgets4 release tags
3. Optionally automates missing steps (when not in --dry-run mode):
- Creates missing release tags using push_repo_release_tag.py
- Merges tags into stable branches using merge_remote.py
Usage:
./release_checklist.py v4.24.0 # Check release status
./release_checklist.py v4.24.0 --verbose # Show detailed debug info
./release_checklist.py v4.24.0 --dry-run # Check only, don't execute fixes
For automated release management with Claude Code:
/release v4.24.0 # Run full release process with Claude
The script reads repository configurations from release_repos.yml and reports:
- ✅ for completed requirements
- ❌ for missing requirements (with instructions to fix)
- 🟡 for repositories waiting on dependencies
- ⮕ for automated actions being taken
This script is idempotent and safe to rerun multiple times.
"""
import argparse
import yaml
import requests
@@ -76,6 +129,39 @@ def release_page_exists(repo_url, tag_name, github_token):
response = requests.get(api_url, headers=headers)
return response.status_code == 200
def get_tag_workflow_status(repo_url, tag_name, github_token):
"""Get the status of CI workflows running for a specific tag."""
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
headers = {'Authorization': f'token {github_token}'} if github_token else {}
# Get workflow runs for the tag
# GitHub's workflow runs API uses the branch/tag name in the 'head_branch' field
api_url = f"{api_base}/actions/runs?event=push&head_branch={tag_name}"
response = requests.get(api_url, headers=headers)
if response.status_code != 200:
return None
data = response.json()
workflow_runs = data.get('workflow_runs', [])
if not workflow_runs:
return None
# Get the most recent workflow run for this tag
run = workflow_runs[0]
status = run.get('status')
conclusion = run.get('conclusion')
workflow_name = run.get('name', 'CI')
run_id = run.get('id')
return {
'status': status,
'conclusion': conclusion,
'workflow_name': workflow_name,
'run_id': run_id
}
def get_release_notes(tag_name):
"""Fetch release notes page title from lean-lang.org."""
# Strip -rcX suffix if present for the URL
@@ -84,20 +170,17 @@ def get_release_notes(tag_name):
try:
response = requests.get(reference_url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
# Extract title using regex
match = re.search(r"<title>(.*?)</title>", response.text, re.IGNORECASE | re.DOTALL)
if match:
return match.group(1).strip()
else:
print(f" ⚠️ Could not find <title> tag in {reference_url}")
return None
except requests.exceptions.RequestException as e:
print(f" ❌ Error fetching release notes from {reference_url}: {e}")
except requests.exceptions.RequestException:
return None
except Exception as e:
print(f" ❌ An unexpected error occurred while processing release notes: {e}")
except Exception:
return None
def get_branch_content(repo_url, branch, file_path, github_token):
@@ -286,6 +369,68 @@ def check_bump_branch_toolchain(url, bump_branch, github_token):
print(f" ✅ Bump branch correctly uses toolchain: {content}")
return True
def get_pr_ci_status(repo_url, pr_number, github_token):
"""Get the CI status for a pull request."""
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
headers = {'Authorization': f'token {github_token}'} if github_token else {}
# Get PR details to find the head SHA
pr_response = requests.get(f"{api_base}/pulls/{pr_number}", headers=headers)
if pr_response.status_code != 200:
return "unknown", "Could not fetch PR details"
pr_data = pr_response.json()
head_sha = pr_data['head']['sha']
# Get check runs for the commit
check_runs_response = requests.get(
f"{api_base}/commits/{head_sha}/check-runs",
headers=headers
)
if check_runs_response.status_code != 200:
return "unknown", "Could not fetch check runs"
check_runs_data = check_runs_response.json()
check_runs = check_runs_data.get('check_runs', [])
if not check_runs:
# No check runs, check for status checks (legacy)
status_response = requests.get(
f"{api_base}/commits/{head_sha}/status",
headers=headers
)
if status_response.status_code == 200:
status_data = status_response.json()
state = status_data.get('state', 'unknown')
if state == 'success':
return "success", "All status checks passed"
elif state == 'failure':
return "failure", "Some status checks failed"
elif state == 'pending':
return "pending", "Status checks in progress"
return "unknown", "No CI checks found"
# Analyze check runs
conclusions = [run['conclusion'] for run in check_runs if run.get('status') == 'completed']
in_progress = [run for run in check_runs if run.get('status') in ['queued', 'in_progress']]
if in_progress:
return "pending", f"{len(in_progress)} check(s) in progress"
if not conclusions:
return "pending", "Checks queued"
if all(c == 'success' for c in conclusions):
return "success", f"All {len(conclusions)} checks passed"
failed = sum(1 for c in conclusions if c in ['failure', 'timed_out', 'action_required'])
if failed > 0:
return "failure", f"{failed} check(s) failed"
# Some checks are cancelled, skipped, or neutral
return "warning", f"Some checks did not complete normally"
def pr_exists_with_title(repo_url, title, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + "/pulls"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
@@ -404,30 +549,57 @@ def main():
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")
release_page_ready = release_page_exists(lean_repo_url, toolchain, github_token)
if not release_page_ready:
print(f" ❌ Release page for {toolchain} does not exist (This will be created by CI.)")
# Check CI workflow status
workflow_status = get_tag_workflow_status(lean_repo_url, toolchain, github_token)
if workflow_status:
status = workflow_status['status']
conclusion = workflow_status['conclusion']
workflow_name = workflow_status['workflow_name']
run_id = workflow_status['run_id']
workflow_url = f"{lean_repo_url}/actions/runs/{run_id}"
if status == 'in_progress' or status == 'queued':
print(f" 🔄 {workflow_name} workflow is {status}: {workflow_url}")
elif status == 'completed':
if conclusion == 'success':
print(f"{workflow_name} workflow completed successfully: {workflow_url}")
elif conclusion == 'failure':
print(f"{workflow_name} workflow failed: {workflow_url}")
else:
print(f" ⚠️ {workflow_name} workflow completed with status: {conclusion}: {workflow_url}")
else:
print(f" {workflow_name} workflow status: {status}: {workflow_url}")
lean4_success = False
else:
print(f" ✅ Release page for {toolchain} exists")
# Check the actual release notes page title
# Check the actual release notes page title (informational only - does not block)
actual_title = get_release_notes(toolchain)
expected_title_prefix = f"Lean {toolchain.lstrip('v')}" # e.g., "Lean 4.19.0" or "Lean 4.19.0-rc1"
base_tag = toolchain.split('-')[0]
release_notes_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
if actual_title is None:
# Error already printed by get_release_notes
lean4_success = False
print(f" ⚠️ Release notes not found at {release_notes_url} (this will be fixed while updating the reference-manual repository)")
elif not actual_title.startswith(expected_title_prefix):
# Construct URL for the error message (using the base tag)
base_tag = toolchain.split('-')[0]
check_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
print(f" ❌ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {check_url}")
lean4_success = False
print(f" ⚠️ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {release_notes_url}")
else:
print(f" ✅ Release notes page title looks good ('{actual_title}').")
repo_status["lean4"] = lean4_success
# If the release page doesn't exist, skip repository checks and master branch checks
# The preliminary infrastructure must be in place first
if not release_page_exists(lean_repo_url, toolchain, github_token):
print("\n⚠️ Release process blocked: preliminary Lean4 infrastructure incomplete.")
print(" Complete the steps above, then rerun this script to proceed with downstream repositories.")
return
# Load repositories and perform further checks
print("\nChecking repositories...")
@@ -471,6 +643,19 @@ def main():
if pr_info:
pr_number, pr_url = pr_info
print(f" ✅ PR with title '{pr_title}' exists: #{pr_number} ({pr_url})")
# Check CI status
ci_status, ci_message = get_pr_ci_status(url, pr_number, github_token)
if ci_status == "success":
print(f" ✅ CI: {ci_message}")
elif ci_status == "failure":
print(f" ❌ CI: {ci_message}")
elif ci_status == "pending":
print(f" 🔄 CI: {ci_message}")
elif ci_status == "warning":
print(f" ⚠️ CI: {ci_message}")
else:
print(f" ❓ CI: {ci_message}")
else:
print(f" ❌ PR with title '{pr_title}' does not exist")
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")

View File

@@ -52,6 +52,7 @@ def sort_sections_order():
return [
"Language",
"Library",
"Tactics",
"Compiler",
"Pretty Printing",
"Documentation",

View File

@@ -112,3 +112,11 @@ repositories:
branch: master
dependencies:
- mathlib4
- name: lean-fro.org
url: https://github.com/leanprover/lean-fro.org
toolchain-tag: false
stable-branch: false
branch: master
dependencies:
- verso

View File

@@ -1,30 +1,53 @@
#!/usr/bin/env python3
"""
Execute release steps for Lean4 repositories.
Execute Release Steps for Lean4 Downstream Repositories
This script helps automate the release process for Lean4 and its dependent repositories
by actually executing the step-by-step instructions for updating toolchains, creating tags,
and managing branches.
This script automates the process of updating a downstream repository to a new Lean4 release.
It handles creating branches, updating toolchains, merging changes, building, testing, and
creating pull requests.
IMPORTANT: Keep this documentation up-to-date when modifying the script's behavior!
What this script does:
1. Sets up the downstream_releases/ directory for cloning repositories
2. Clones or updates the target repository
3. Creates a branch named bump_to_{version} for the changes
4. Updates the lean-toolchain file to the target version
5. Handles repository-specific variations:
- Different dependency update mechanisms
- Special merging strategies for repositories with nightly-testing branches
- Safety checks for repositories using bump branches
- Custom build and test procedures
6. Commits the changes with message "chore: bump toolchain to {version}"
7. Builds the project (with a clean .lake cache)
8. Runs tests if available
9. Pushes the branch to GitHub
10. Creates a pull request (or reports if one already exists)
Usage:
python3 release_steps.py <version> <repo>
./release_steps.py v4.24.0 batteries # Update batteries to v4.24.0
./release_steps.py v4.24.0-rc1 mathlib4 # Update mathlib4 to v4.24.0-rc1
Arguments:
version: The version to set in the lean-toolchain file (e.g., v4.6.0)
repo: The repository name as specified in release_repos.yml
The script reads repository configurations from release_repos.yml.
Each repository has specific handling for merging, dependencies, and testing.
Example:
python3 release_steps.py v4.6.0 mathlib4
python3 release_steps.py v4.6.0 batteries
This script is idempotent - it's safe to rerun if it fails partway through.
Existing branches, commits, and PRs will be reused rather than duplicated.
The script reads repository configurations from release_repos.yml in the same directory.
Each repository may have specific requirements for:
- Branch management
- Toolchain updates
- Dependency updates
- Tagging conventions
- Stable branch handling
Error handling:
- If build or tests fail, the script continues to create the PR anyway
- Manual conflicts must be resolved by the user
- Network issues during push/PR creation are reported with manual instructions
"""
import argparse
@@ -377,6 +400,18 @@ def execute_release_steps(repo, version, config):
except subprocess.CalledProcessError as e:
print(red("Tests failed, but continuing with PR creation..."))
print(red(f"Test error: {e}"))
elif repo_name == "lean-fro.org":
# Update lean-toolchain in examples/hero
print(blue("Updating examples/hero/lean-toolchain..."))
docs_toolchain = repo_path / "examples" / "hero" / "lean-toolchain"
with open(docs_toolchain, "w") as f:
f.write(f"leanprover/lean4:{version}\n")
print(green(f"Updated examples/hero/lean-toolchain to leanprover/lean4:{version}"))
print(blue("Running `lake update`..."))
run_command("lake update", cwd=repo_path, stream_output=True)
print(blue("Running `lake update` in examples/hero..."))
run_command("lake update", cwd=repo_path / "examples" / "hero", stream_output=True)
elif repo_name == "cslib":
print(blue("Updating lakefile.toml..."))
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
@@ -554,8 +589,19 @@ def execute_release_steps(repo, version, config):
# Clean lake cache for a fresh build
print(blue("Cleaning lake cache..."))
run_command("rm -rf .lake", cwd=repo_path)
run_command("lake clean", cwd=repo_path)
# Check if downstream of Mathlib and get cache if so
mathlib_package_dir = repo_path / ".lake" / "packages" / "mathlib"
if mathlib_package_dir.exists():
print(blue("Project is downstream of Mathlib, fetching cache..."))
try:
run_command("lake exe cache get", cwd=repo_path, stream_output=True)
print(green("Cache fetched successfully"))
except subprocess.CalledProcessError as e:
print(yellow("Failed to fetch cache, continuing anyway..."))
print(yellow(f"Cache fetch error: {e}"))
try:
run_command("lake build", cwd=repo_path, stream_output=True)
print(green("Build completed successfully"))

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 25)
set(LEAN_VERSION_MINOR 27)
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'")
@@ -34,7 +34,14 @@ if (NOT LEAN_PLATFORM_TARGET)
OUTPUT_VARIABLE LEAN_PLATFORM_TARGET OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
set(LEAN_EXTRA_LINKER_FLAGS "" CACHE STRING "Additional flags used by the linker")
set(LEAN_EXTRA_LINKER_FLAGS_DEFAULT "")
# Use lld by default, if available
find_program(LLD_PATH lld)
if(LLD_PATH)
string(APPEND LEAN_EXTRA_LINKER_FLAGS_DEFAULT " -fuse-ld=lld")
endif()
set(LEAN_EXTRA_LINKER_FLAGS ${LEAN_EXTRA_LINKER_FLAGS_DEFAULT} CACHE STRING "Additional flags used by the linker")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler")
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
@@ -82,6 +89,7 @@ option(USE_MIMALLOC "use mimalloc" ON)
# development-specific options
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" ON)
option(USE_LAKE_CACHE "Use the Lake artifact cache for stage 1 builds (requires USE_LAKE)" OFF)
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
@@ -797,6 +805,9 @@ install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
# symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
# get rid of all files in `src/lean` that may have been loaded from the cache
# (at the time of writing this, this is the case for some lake test .c files)
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/src/lean)
if(${STAGE} EQUAL 0)
file(CREATE_LINK ${CMAKE_SOURCE_DIR}/../../src ${CMAKE_BINARY_DIR}/src/lean RESULT _IGNORE_RES SYMBOLIC)
else()
@@ -823,10 +834,13 @@ if(LEAN_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "${LEAN_INSTALL_PREFIX}/lean-${LEAN_VERSION_STRING}${LEAN_INSTALL_SUFFIX}")
endif()
if (STAGE GREATER 1)
if (USE_LAKE_CACHE AND STAGE EQUAL 1)
set(LAKE_ARTIFACT_CACHE_TOML "true")
else()
# The build of stage2+ may depend on local changes made to src/ that are not reflected by the
# commit hash in stage1/bin/lean, so we make sure to disable the global cache
string(APPEND LEAN_EXTRA_LAKEFILE_TOML "\n\nenableArtifactCache = false")
set(LAKE_ARTIFACT_CACHE_TOML "false")
endif()
# Escape for `make`. Yes, twice.
@@ -844,15 +858,13 @@ endfunction()
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_CC "${LEANC_CC}")
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_FLAGS "${LEANC_INTERNAL_FLAGS}")
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_LINKER_FLAGS "${LEANC_INTERNAL_LINKER_FLAGS}")
set(LEANC_OPTS_TOML "${LEANC_OPTS} ${LEANC_EXTRA_CC_FLAGS} ${LEANC_INTERNAL_FLAGS}")
set(LINK_OPTS_TOML "${LEANC_INTERNAL_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/lib/lean ${LEAN_EXTRA_LINKER_FLAGS}")
toml_escape("${LEAN_EXTRA_MAKE_OPTS}" LEAN_EXTRA_OPTS_TOML)
toml_escape("${LEANC_OPTS_TOML}" LEANC_OPTS_TOML)
toml_escape("${LINK_OPTS_TOML}" LINK_OPTS_TOML)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(LAKE_LIB_PREFIX "lib")
if(${CMAKE_BUILD_TYPE} MATCHES "Debug|Release|RelWithDebInfo|MinSizeRel")
set(CMAKE_BUILD_TYPE_TOML "${CMAKE_BUILD_TYPE}")
else()
set(CMAKE_BUILD_TYPE_TOML "Release")
endif()
if(USE_LAKE)

View File

@@ -14,7 +14,6 @@ public import Init.ByCases
public import Init.RCases
public import Init.Core
public import Init.Control
public import Init.Data.Basic
public import Init.WF
public import Init.WFTactics
public import Init.Data

View File

@@ -7,7 +7,6 @@ Authors: Joachim Breitner
module
prelude
public import Init.Prelude
public import Init.Tactics
public section

View File

@@ -44,3 +44,10 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
@[simp] theorem dite_eq_ite [Decidable P] :
(dite P (fun _ => a) (fun _ => b)) = ite P a b := rfl
-- Remark: dite and ite are "defally equal" when we ignore the proofs.
@[deprecated dite_eq_ite (since := "2025-10-29")]
theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) : dite c (fun _ => t) (fun _ => e) = ite c t e :=
match h with
| isTrue _ => rfl
| isFalse _ => rfl

View File

@@ -8,7 +8,7 @@ module
prelude
public import Init.PropLemmas
public section
@[expose] public section
universe u v
@@ -181,9 +181,6 @@ theorem not_imp_iff_and_not : ¬(a → b) ↔ a ∧ ¬b := Decidable.not_imp_iff
theorem not_and_iff_not_or_not : ¬(a b) ¬a ¬b := Decidable.not_and_iff_not_or_not
@[deprecated not_and_iff_not_or_not (since := "2025-03-18")]
abbrev not_and_iff_or_not_not := @not_and_iff_not_or_not
theorem not_iff : ¬(a b) (¬a b) := Decidable.not_iff
@[simp] theorem imp_iff_left_iff : (b a b) a b := Decidable.imp_iff_left_iff

View File

@@ -16,5 +16,3 @@ public import Init.Control.Option
public import Init.Control.Lawful
public import Init.Control.StateCps
public import Init.Control.ExceptCps
public section

View File

@@ -45,9 +45,6 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
rfl
@[deprecated forIn_eq_forIn' (since := "2025-04-04")]
abbrev forIn_eq_forin' := @forIn_eq_forIn'
/--
Extracts the value from a `ForInStep`, ignoring whether it is `ForInStep.done` or `ForInStep.yield`.
-/

View File

@@ -6,8 +6,6 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Control.State
public import Init.Control.Except
public import Init.Data.ToString.Basic
public section

View File

@@ -10,7 +10,6 @@ module
prelude
public import Init.Control.Basic
public import Init.Control.Id
public import Init.Coe
@[expose] public section
@@ -149,6 +148,23 @@ This is the inverse of `ExceptT.mk`.
@[always_inline, inline, expose]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
/--
Use a monadic action that may throw an exception by providing explicit success and failure
continuations.
-/
@[always_inline, inline, expose]
def ExceptT.runK [Monad m] (x : ExceptT ε m α) (ok : α m β) (error : ε m β) : m β :=
x.run >>= (·.casesOn error ok)
/--
Returns the value of a computation, forgetting whether it was an exception or a success.
This corresponds to early return.
-/
@[always_inline, inline, expose]
def ExceptT.runCatch [Monad m] (x : ExceptT α m α) : m α :=
x.runK pure pure
namespace ExceptT
variable {ε : Type u} {m : Type u Type v} [Monad m]

View File

@@ -10,5 +10,3 @@ 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

@@ -7,8 +7,6 @@ module
prelude
public import Init.Ext
public import Init.SimpLemmas
public import Init.Meta
public section
@@ -172,6 +170,7 @@ theorem bind_pure_unit [Monad m] [LawfulMonad m] {x : m PUnit} : (x >>= fun _ =>
theorem map_congr [Functor m] {x : m α} {f g : α β} (h : a, f a = g a) : (f <$> x : m β) = g <$> x := by
simp [funext h]
@[deprecated seq_eq_bind_map (since := "2025-10-26")]
theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α β)) (x : m α) : mf <*> x = mf >>= fun f => f <$> x := by
rw [bind_map]
@@ -252,24 +251,9 @@ instance : LawfulMonad Id := by
@[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 pure_run (a : Id α) : pure a.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 -/
instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => rfl)
(bind_assoc := fun x _ _ => by cases x <;> rfl)
(bind_pure_comp := fun _ x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance

View File

@@ -7,12 +7,11 @@ module
prelude
public import Init.Control.Lawful.Basic
public import Init.Control.Except
import all Init.Control.Except
public import Init.Control.State
public import Init.Control.Option
import all Init.Control.Option
import all Init.Control.State
public import Init.Control.StateRef
public import Init.Ext
public section
@@ -110,6 +109,121 @@ instance : LawfulMonad (Except ε) := LawfulMonad.mk'
instance : LawfulApplicative (Except ε) := inferInstance
instance : LawfulFunctor (Except ε) := inferInstance
/-! # OptionT -/
namespace OptionT
@[ext] theorem ext {x y : OptionT m α} (h : x.run = y.run) : x = y := by
simp [run] at h
assumption
@[simp, grind =] theorem run_mk {m : Type u Type v} (x : m (Option α)) :
OptionT.run (OptionT.mk x) = x := by rfl
@[simp, grind =] theorem run_pure [Monad m] (x : α) : run (pure x : OptionT m α) = pure (some x) := by
simp [run, pure, OptionT.pure, OptionT.mk]
@[simp, grind =] theorem run_lift [Monad.{u, v} m] (x : m α) : run (OptionT.lift x : OptionT m α) = (return some ( x) : m (Option α)) := by
simp [run, OptionT.lift, OptionT.mk]
@[simp, grind =] theorem run_throw [Monad m] : run (throw e : OptionT m β) = pure none := by
simp [run, throw, throwThe, MonadExceptOf.throw, OptionT.fail, OptionT.mk]
@[simp, grind =] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α OptionT m β) : run (OptionT.lift x >>= f : OptionT m β) = x >>= fun a => run (f a) := by
simp [OptionT.run, OptionT.lift, bind, OptionT.bind, OptionT.mk]
@[simp, grind =] theorem bind_throw [Monad m] [LawfulMonad m] (f : α OptionT m β) : (throw e >>= f) = throw e := by
simp [throw, throwThe, MonadExceptOf.throw, bind, OptionT.bind, OptionT.mk, OptionT.fail]
@[simp, grind =] theorem run_bind (f : α OptionT m β) [Monad m] :
(x >>= f).run = Option.elimM x.run (pure none) (fun x => (f x).run) := by
change x.run >>= _ = _
simp [Option.elimM]
exact bind_congr fun |some _ => rfl | none => rfl
@[simp, grind =] theorem lift_pure [Monad m] [LawfulMonad m] {α : 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, grind =] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : OptionT m α)
: (f <$> x).run = Option.map f <$> x.run := by
simp [Functor.map, Option.map, bind_pure_comp]
apply bind_congr
intro a; cases a <;> simp [OptionT.pure, OptionT.mk]
protected theorem seq_eq {α β : Type u} [Monad m] (mf : OptionT m (α β)) (x : OptionT m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
protected theorem bind_pure_comp [Monad m] (f : α β) (x : OptionT m α) : x >>= pure f = f <$> x := by
intros; rfl
protected theorem seqLeft_eq {α β : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) : x <* y = const β <$> x <*> y := by
change (x >>= fun a => y >>= fun _ => pure a) = (const (α := α) β <$> x) >>= fun f => f <$> y
rw [ OptionT.bind_pure_comp]
apply ext
simp [Option.elimM, Option.elim]
apply bind_congr
intro
| none => simp
| some _ =>
simp [bind_pure_comp]; apply bind_congr; intro b;
cases b <;> simp [const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) : x *> y = const α id <$> x <*> y := by
change (x >>= fun _ => y) = (const α id <$> x) >>= fun f => f <$> y
rw [ OptionT.bind_pure_comp]
apply ext
simp [Option.elimM, Option.elim]
apply bind_congr
intro a; cases a <;> simp
instance [Monad m] [LawfulMonad m] : LawfulMonad (OptionT m) where
id_map := by intros; apply ext; simp
map_const := by intros; rfl
seqLeft_eq := OptionT.seqLeft_eq
seqRight_eq := OptionT.seqRight_eq
pure_seq := by intros; apply ext; simp [OptionT.seq_eq, Option.elimM, Option.elim]
bind_pure_comp := OptionT.bind_pure_comp
bind_map := by intros; rfl
pure_bind := by intros; apply ext; simp [Option.elimM, Option.elim]
bind_assoc := by intros; apply ext; simp [Option.elimM, Option.elim]; apply bind_congr; intro a; cases a <;> simp
@[simp] theorem run_seq [Monad m] [LawfulMonad m] (f : OptionT m (α β)) (x : OptionT m α) :
(f <*> x).run = Option.elimM f.run (pure none) (fun f => Option.map f <$> x.run) := by
simp [seq_eq_bind_map, Option.elimM, Option.elim]
@[simp] theorem run_seqLeft [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) :
(x <* y).run = Option.elimM x.run (pure none)
(fun x => Option.map (Function.const β x) <$> y.run) := by
simp [seqLeft_eq, seq_eq_bind_map, Option.elimM, OptionT.run_bind]
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : OptionT m α) (y : OptionT m β) :
(x *> y).run = Option.elimM x.run (pure none) (Function.const α y.run) := by
simp only [seqRight_eq, run_seq, Option.elimM, run_map, Option.elim, bind_map_left]
refine bind_congr (fun | some _ => by simp | none => by simp)
@[simp, grind =] theorem run_failure [Monad m] : (failure : OptionT m α).run = pure none := by rfl
@[simp] theorem map_failure [Monad m] [LawfulMonad m] {α β : Type _} (f : α β) :
f <$> (failure : OptionT m α) = (failure : OptionT m β) := by
simp [OptionT.mk, Functor.map, Alternative.failure, OptionT.fail, OptionT.bind]
@[simp] theorem run_orElse [Monad m] (x : OptionT m α) (y : OptionT m α) :
(x <|> y).run = Option.elimM x.run y.run (fun x => pure (some x)) :=
bind_congr fun | some _ => by rfl | none => by rfl
end OptionT
/-! # Option -/
instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => by rfl)
(bind_assoc := fun a _ _ => by cases a <;> rfl)
(bind_pure_comp := fun _ x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance
/-! # ReaderT -/
namespace ReaderT

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Control.Lawful.Basic
public import Init.RCases
public import Init.ByCases
public section

View File

@@ -9,5 +9,3 @@ 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

@@ -6,17 +6,13 @@ Authors: Quang Dao, Paul Reichert
module
prelude
public import Init.Control.Option
import all Init.Control.Option
public import Init.Control.Except
import all Init.Control.Except
public import Init.Control.ExceptCps
import all Init.Control.ExceptCps
public import Init.Control.StateRef
import all Init.Control.StateRef
public import Init.Control.StateCps
import all Init.Control.StateCps
public import Init.Control.Id
import all Init.Control.Id
public import Init.Control.Lawful.MonadLift.Lemmas
public import Init.Control.Lawful.Instances
@@ -64,10 +60,6 @@ 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

View File

@@ -23,7 +23,7 @@ theorem monadLift_map [LawfulMonad m] [LawfulMonad n] (f : α → β) (ma : m α
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]
simp only [seq_eq_bind_map, 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

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.Option.Basic
public import Init.Control.Basic
public import Init.Control.Except
public section
@@ -28,7 +27,7 @@ failure occurred.
/--
Executes an action that might fail in the underlying monad `m`, returning `none` in case of failure.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
x
@@ -39,13 +38,14 @@ variable {m : Type u → Type v} [Monad m] {α β : Type u}
Converts an action that returns an `Option` into one that might fail, with `none` indicating
failure.
-/
@[always_inline, inline, expose]
protected def mk (x : m (Option α)) : OptionT m α :=
x
/--
Sequences two potentially-failing actions. The second action is run only if the first succeeds.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def bind (x : OptionT m α) (f : α OptionT m β) : OptionT m β := OptionT.mk do
match ( x) with
| some a => f a
@@ -54,7 +54,7 @@ protected def bind (x : OptionT m α) (f : α → OptionT m β) : OptionT m β :
/--
Succeeds with the provided value.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def pure (a : α) : OptionT m α := OptionT.mk do
pure (some a)
@@ -69,7 +69,7 @@ instance {m : Type u → Type v} [Pure m] : Inhabited (OptionT m α) where
/--
Recovers from failures. Typically used via the `<|>` operator.
-/
@[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
@[always_inline, inline, expose] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
match ( x) with
| some a => pure (some a)
| _ => y ()
@@ -77,7 +77,7 @@ Recovers from failures. Typically used via the `<|>` operator.
/--
A recoverable failure.
-/
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
@[always_inline, inline, expose] protected def fail : OptionT m α := OptionT.mk do
pure none
instance : Alternative (OptionT m) where
@@ -90,7 +90,7 @@ Converts a computation from the underlying monad into one that could fail, even
This function is typically implicitly accessed via a `MonadLiftT` instance as part of [automatic
lifting](lean-manual://section/monad-lifting).
-/
@[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
@[always_inline, inline, expose] protected def lift (x : m α) : OptionT m α := OptionT.mk do
return some ( x)
instance : MonadLift m (OptionT m) := OptionT.lift
@@ -100,11 +100,11 @@ instance : MonadFunctor m (OptionT m) := ⟨fun f x => f x⟩
/--
Handles failures by treating them as exceptions of type `Unit`.
-/
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle ()
@[always_inline, inline, expose] protected def tryCatch (x : OptionT m α) (handle : PUnit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle
pure <| some a
instance : MonadExceptOf Unit (OptionT m) where
instance : MonadExceptOf PUnit (OptionT m) where
throw := fun _ => OptionT.fail
tryCatch := OptionT.tryCatch

View File

@@ -8,8 +8,6 @@ The Reader monad transformer for passing immutable State.
module
prelude
public import Init.Control.Basic
public import Init.Control.Id
public import Init.Control.Except
public section

View File

@@ -8,8 +8,6 @@ The State monad transformer.
module
prelude
public import Init.Control.Basic
public import Init.Control.Id
public import Init.Control.Except
public section

View File

@@ -8,7 +8,6 @@ notation, basic datatypes and type classes
module
prelude
public meta import Init.Prelude
public import Init.SizeOf
public section
@@ -144,8 +143,9 @@ Computed values are cached, so the value is not recomputed.
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
/-- Implementation detail. -/
@[inline] def Thunk.fnImpl (x : Thunk α) : Unit α := fun _ => x.get
@[csimp] 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
@@ -600,17 +600,6 @@ export LawfulSingleton (insert_empty_eq)
attribute [simp] insert_empty_eq
@[deprecated insert_empty_eq (since := "2025-03-12")]
theorem insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
[LawfulSingleton α β] (x : α) : (insert x : β) = singleton x :=
insert_empty_eq _
@[deprecated insert_empty_eq (since := "2025-03-12")]
theorem LawfulSingleton.insert_emptyc_eq [EmptyCollection β] [Insert α β] [Singleton α β]
[LawfulSingleton α β] (x : α) : (insert x : β) = singleton x :=
insert_empty_eq _
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/
class Sep (α : outParam <| Type u) (γ : Type v) where
/-- Computes `{ a ∈ c | p a }`. -/
@@ -1095,14 +1084,6 @@ theorem of_toBoolUsing_eq_true {p : Prop} {d : Decidable p} (h : toBoolUsing d =
theorem of_toBoolUsing_eq_false {p : Prop} {d : Decidable p} (h : toBoolUsing d = false) : ¬p :=
of_decide_eq_false h
set_option linter.missingDocs false in
@[deprecated of_toBoolUsing_eq_true (since := "2025-04-04")]
abbrev ofBoolUsing_eq_true := @of_toBoolUsing_eq_true
set_option linter.missingDocs false in
@[deprecated of_toBoolUsing_eq_false (since := "2025-04-04")]
abbrev ofBoolUsing_eq_false := @of_toBoolUsing_eq_false
instance : Decidable True :=
isTrue trivial
@@ -1165,6 +1146,7 @@ end
else isFalse (fun h => absurd (h hp) hq)
else isTrue (fun h => absurd h hp)
@[inline]
instance {p q} [Decidable p] [Decidable q] : Decidable (p q) :=
if hp : p then
if hq : q then
@@ -1206,17 +1188,13 @@ theorem dif_neg {c : Prop} {h : Decidable c} (hnc : ¬c) {α : Sort u} {t : c
| isTrue hc => absurd hc hnc
| isFalse _ => rfl
-- Remark: dite and ite are "defally equal" when we ignore the proofs.
theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) : dite c (fun _ => t) (fun _ => e) = ite c t e :=
match h with
| isTrue _ => rfl
| isFalse _ => rfl
@[macro_inline]
instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) :=
match dC with
| isTrue _ => dT
| isFalse _ => dE
@[inline]
instance {c : Prop} {t : c Prop} {e : ¬c Prop} [dC : Decidable c] [dT : h, Decidable (t h)] [dE : h, Decidable (e h)] : Decidable (if h : c then t h else e h) :=
match dC with
| isTrue hc => dT hc
@@ -1367,12 +1345,12 @@ namespace Subtype
theorem exists_of_subtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x)
| a, h => a, h
set_option linter.missingDocs false in
@[deprecated exists_of_subtype (since := "2025-04-04")]
abbrev existsOfSubtype := @exists_of_subtype
variable {α : Sort u} {p : α Prop}
variable {α : Type u} {p : α Prop}
protected theorem ext : {a1 a2 : {x // p x}}, val a1 = val a2 a1 = a2
| _, _, _, _, rfl => rfl
@[deprecated Subtype.ext (since := "2025-10-26")]
protected theorem eq : {a1 a2 : {x // p x}}, val a1 = val a2 a1 = a2
| _, _, _, _, rfl => rfl
@@ -1387,9 +1365,9 @@ instance {α : Type u} {p : α → Prop} [BEq α] [ReflBEq α] : ReflBEq {x : α
rfl {x} := BEq.refl x.1
instance {α : Type u} {p : α Prop} [BEq α] [LawfulBEq α] : LawfulBEq {x : α // p x} where
eq_of_beq h := Subtype.eq (eq_of_beq h)
eq_of_beq h := Subtype.ext (eq_of_beq h)
instance {α : Type u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
instance {α : Sort u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
fun a, h₁ b, h₂ =>
if h : a = b then isTrue (by subst h; exact rfl)
else isFalse (fun h' => Subtype.noConfusion h' (fun h' => absurd h' h))
@@ -1490,6 +1468,8 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
@[simp] theorem Prod.map_apply (f : α β) (g : γ δ) (x) (y) :
Prod.map f g (x, y) = (f x, g y) := rfl
-- We add `@[grind =]` to these in `Init.Data.Prod`.
@[simp] theorem Prod.map_fst (f : α β) (g : γ δ) (x) : (Prod.map f g x).1 = f x.1 := rfl
@[simp] theorem Prod.map_snd (f : α β) (g : γ δ) (x) : (Prod.map f g x).2 = g x.2 := rfl
@@ -1506,20 +1486,24 @@ protected theorem PSigma.eta {α : Sort u} {β : α → Sort v} {a₁ a₂ : α}
/-! # Universe polymorphic unit -/
theorem PUnit.ext (a b : PUnit) : a = b := by
cases a; cases b; exact rfl
@[deprecated PUnit.ext (since := "2025-10-26")]
theorem PUnit.subsingleton (a b : PUnit) : a = b := by
cases a; cases b; exact rfl
theorem PUnit.eq_punit (a : PUnit) : a = :=
PUnit.subsingleton a
PUnit.ext a
instance : Subsingleton PUnit :=
Subsingleton.intro PUnit.subsingleton
Subsingleton.intro PUnit.ext
instance : Inhabited PUnit where
default :=
instance : DecidableEq PUnit :=
fun a b => isTrue (PUnit.subsingleton a b)
fun a b => isTrue (PUnit.ext a b)
/-! # Setoid -/
@@ -1580,6 +1564,7 @@ instance {p q : Prop} [d : Decidable (p ↔ q)] : Decidable (p = q) :=
gen_injective_theorems% Array
gen_injective_theorems% BitVec
gen_injective_theorems% ByteArray
gen_injective_theorems% Char
gen_injective_theorems% DoResultBC
gen_injective_theorems% DoResultPR
@@ -1604,8 +1589,8 @@ gen_injective_theorems% PSigma
gen_injective_theorems% PSum
gen_injective_theorems% Sigma
gen_injective_theorems% String
gen_injective_theorems% String.Pos
gen_injective_theorems% Substring
gen_injective_theorems% String.Pos.Raw
gen_injective_theorems% Substring.Raw
gen_injective_theorems% Subtype
gen_injective_theorems% Sum
gen_injective_theorems% Task
@@ -2522,8 +2507,7 @@ class Antisymm (r : αα → Prop) : Prop where
/-- An antisymmetric relation `r` satisfies `r a b → r b a → a = b`. -/
antisymm (a b : α) : r a b r b a a = b
/-- `Asymm r` means that the binary relation `r` is asymmetric, that is,
`r a b → ¬ r b a`. -/
/-- `Asymm r` means that the binary relation `r` is asymmetric, that is, `r a b → ¬ r b a`. -/
class Asymm (r : α α Prop) : Prop where
/-- An asymmetric relation satisfies `r a b → ¬ r b a`. -/
asymm : a b, r a b ¬r b a
@@ -2533,16 +2517,19 @@ class Symm (r : αα → Prop) : Prop where
/-- A symmetric relation satisfies `r a b → r b a`. -/
symm : a b, r a b r b a
/-- `Total X r` means that the binary relation `r` on `X` is total, that is, that for any
`x y : X` we have `r x y` or `r y x`. -/
/-- `Total X r` means that the binary relation `r` on `X` is total, that is, `r a b` or `r b a`. -/
class Total (r : α α Prop) : Prop where
/-- A total relation satisfies `r a b r b a`. -/
/-- A total relation satisfies `r a b` or `r b a`. -/
total : a b, r a b r b a
/-- `Irrefl r` means the binary relation `r` is irreflexive, that is, `r x x` never
holds. -/
/-- `Irrefl r` means the binary relation `r` is irreflexive, that is, `r x x` never holds. -/
class Irrefl (r : α α Prop) : Prop where
/-- An irreflexive relation satisfies `¬ r a a`. -/
irrefl : a, ¬r a a
/-- `Trichotomous r` says that `r` is trichotomous, that is, `¬ r a b → ¬ r b a → a = b`. -/
class Trichotomous (r : α α Prop) : Prop where
/-- An trichotomous relation `r` satisfies `¬ r a b → ¬ r b a → a = b`. -/
trichotomous (a b : α) : ¬ r a b ¬ r b a a = b
end Std

View File

@@ -6,7 +6,6 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Basic
public import Init.Data.Nat
public import Init.Data.Bool
public import Init.Data.BitVec
@@ -30,6 +29,7 @@ public import Init.Data.Random
public import Init.Data.ToString
public import Init.Data.Range
public import Init.Data.Hashable
public import Init.Data.LawfulHashable
public import Init.Data.OfScientific
public import Init.Data.Format
public import Init.Data.Stream
@@ -52,5 +52,3 @@ public import Init.Data.Slice
public import Init.Data.Order
public import Init.Data.Rat
public import Init.Data.Dyadic
public section

View File

@@ -7,7 +7,6 @@ Authors: Dany Fabian
module
prelude
public import Init.Classical
public import Init.ByCases
@[expose] public section

View File

@@ -30,5 +30,3 @@ 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,7 @@ Authors: Joachim Breitner, Mario Carneiro
module
prelude
public import Init.Data.Array.Mem
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Count
public import Init.Data.List.Attach
import all Init.Data.List.Attach
public section
@@ -84,10 +81,10 @@ well-founded recursion mechanism to prove that the function terminates.
simp [pmap]
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@[inline] private def pmapImpl {P : α Prop} (f : a, P a β) (xs : Array α) (H : a xs, P a) :
@[inline] def pmapImpl {P : α Prop} (f : a, P a β) (xs : Array α) (H : a xs, P a) :
Array β := (xs.attachWith _ H).map fun x, h' => f x h'
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
@[csimp] theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
funext α β p f xs H
cases xs
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith_eq_pmap]
@@ -95,16 +92,16 @@ well-founded recursion mechanism to prove that the function terminates.
intro a m h₁ h₂
congr
@[simp, grind =] theorem pmap_empty {P : α Prop} (f : a, P a β) : pmap f #[] (by simp) = #[] := rfl
@[simp] theorem pmap_empty {P : α Prop} (f : a, P a β) : pmap f #[] (by simp) = #[] := rfl
@[simp, grind =] theorem pmap_push {P : α Prop} (f : a, P a β) (a : α) (xs : Array α) (h : b xs.push a, P b) :
@[simp] 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, grind =] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@[simp, grind =] theorem attachWith_empty {P : α Prop} (H : x #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
@[simp] 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) =
@@ -125,13 +122,11 @@ 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
@@ -147,14 +142,14 @@ theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H :
subst w
simp
@[simp, grind =] theorem attach_push {a : α} {xs : Array α} :
@[simp] 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, grind =] theorem attachWith_push {a : α} {xs : Array α} {P : α Prop} {H : x xs.push a, P x} :
@[simp] 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
@@ -176,9 +171,6 @@ theorem attach_map_val (xs : Array α) (f : α → β) :
cases xs
simp
@[deprecated attach_map_val (since := "2025-02-17")]
abbrev attach_map_coe := @attach_map_val
-- The argument `xs : Array α` is explicit to allow rewriting from right to left.
theorem attach_map_subtype_val (xs : Array α) : xs.attach.map Subtype.val = xs := by
cases xs; simp
@@ -187,9 +179,6 @@ theorem attachWith_map_val {p : α → Prop} {f : α → β} {xs : Array α} (H
((xs.attachWith p H).map fun (i : { i // p i}) => f i) = xs.map f := by
cases xs; simp
@[deprecated attachWith_map_val (since := "2025-02-17")]
abbrev attachWith_map_coe := @attachWith_map_val
theorem attachWith_map_subtype_val {p : α Prop} {xs : Array α} (H : a xs, p a) :
(xs.attachWith p H).map Subtype.val = xs := by
cases xs; simp
@@ -294,25 +283,23 @@ 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, grind =] theorem pmap_attach {xs : Array α} {p : {x // x xs} Prop} {f : a, p a β} (H) :
@[simp] 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, grind =] theorem pmap_attachWith {xs : Array α} {p : {x // q x} Prop} {f : a, p a β} (H₁ H₂) :
@[simp] 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
@@ -370,20 +357,18 @@ theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
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, grind =] theorem map_attachWith {xs : Array α} {P : α Prop} {H : (a : α), a xs P a}
@[simp] 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
@@ -401,9 +386,6 @@ theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
cases xs
ext <;> simp
@[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
@@ -439,7 +421,6 @@ 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
@@ -447,7 +428,7 @@ theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f
cases xs
simp [List.pmap_pmap, List.pmap_map]
@[simp, grind =] theorem pmap_append {p : ι Prop} {f : a : ι, p a α} {xs ys : Array ι}
@[simp] 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)) ++
@@ -462,7 +443,7 @@ theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Arr
xs.pmap f h₁ ++ ys.pmap f h₂ :=
pmap_append _
@[simp, grind =] theorem attach_append {xs ys : Array α} :
@[simp] 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
@@ -470,62 +451,59 @@ 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, grind =] theorem attachWith_append {P : α Prop} {xs ys : Array α}
@[simp] 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]
@[simp, grind =] theorem pmap_reverse {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp] 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, grind =] theorem attachWith_reverse {P : α Prop} {xs : Array α}
@[simp] 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, grind =] theorem attach_reverse {xs : Array α} :
@[simp] 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, grind =] theorem back?_pmap {P : α Prop} {f : (a : α) P a β} {xs : Array α}
@[simp] 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, grind =] theorem back?_attachWith {P : α Prop} {xs : Array α}
@[simp] 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, grind =]
@[simp]
theorem back?_attach {xs : Array α} :
xs.attach.back? = xs.back?.pbind fun a h => some a, mem_of_back? h := by
cases xs
@@ -771,9 +749,6 @@ and simplifies these to the function directly taking the value.
(Array.replicate n x).unattach = Array.replicate n x.1 := by
simp [unattach]
@[deprecated unattach_replicate (since := "2025-03-18")]
abbrev unattach_mkArray := @unattach_replicate
/-! ### Well-founded recursion preprocessing setup -/
@[wf_preprocess] theorem map_wfParam {xs : Array α} {f : α β} :

View File

@@ -6,10 +6,6 @@ Authors: Leonardo de Moura
module
prelude
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.GetElem
public import Init.Data.List.ToArrayImpl
import all Init.Data.List.ToArrayImpl
@@ -129,20 +125,11 @@ end Array
namespace List
@[deprecated Array.toArray_toList (since := "2025-02-17")]
abbrev toArray_toList := @Array.toArray_toList
-- This does not need to be a simp lemma, as already after the `whnfR` the right hand side is `as`.
theorem toList_toArray {as : List α} : as.toArray.toList = as := rfl
@[deprecated toList_toArray (since := "2025-02-17")]
abbrev _root_.Array.toList_toArray := @List.toList_toArray
@[simp, grind =] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
@[deprecated size_toArray (since := "2025-02-17")]
abbrev _root_.Array.size_toArray := @List.size_toArray
@[simp, grind =] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
xs.toArray[i] = xs[i]'(by simpa using h) := rfl
@@ -222,20 +209,6 @@ Examples:
def replicate {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
/--
Creates an array that contains `n` repetitions of `v`.
The corresponding `List` function is `List.replicate`.
Examples:
* `Array.mkArray 2 true = #[true, true]`
* `Array.mkArray 3 () = #[(), (), ()]`
* `Array.mkArray 0 "anything" = #[]`
-/
@[extern "lean_mk_array", deprecated replicate (since := "2025-03-18")]
def mkArray {α : Type u} (n : Nat) (v : α) : Array α where
toList := List.replicate n v
/--
Swaps two elements of an array. The modification is performed in-place when the reference to the
array is unique.
@@ -253,7 +226,7 @@ def swap (xs : Array α) (i j : @& Nat) (hi : i < xs.size := by get_elem_tactic)
let xs' := xs.set i v₂
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
@[simp, grind =] theorem size_swap {xs : Array α} {i j : Nat} {hi hj} : (xs.swap i j hi hj).size = xs.size := by
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]
@@ -412,10 +385,6 @@ 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"), expose]
def get? (xs : Array α) (i : Nat) : Option α :=
if h : i < xs.size then some xs[i] else none
/--
Swaps a new element with the element at the given index.
@@ -479,7 +448,7 @@ Examples:
-/
abbrev take (xs : Array α) (i : Nat) : Array α := extract xs 0 i
@[simp] theorem take_eq_extract {xs : Array α} {i : Nat} : xs.take i = xs.extract 0 i := rfl
@[simp, grind =] theorem take_eq_extract {xs : Array α} {i : Nat} : xs.take i = xs.extract 0 i := rfl
/--
Removes the first `i` elements of `xs`. If `xs` has fewer than `i` elements, the new array is empty.
@@ -493,7 +462,7 @@ Examples:
-/
abbrev drop (xs : Array α) (i : Nat) : Array α := extract xs i xs.size
@[simp] theorem drop_eq_extract {xs : Array α} {i : Nat} : xs.drop i = xs.extract i xs.size := rfl
@[simp, grind =] theorem drop_eq_extract {xs : Array α} {i : Nat} : xs.drop i = xs.extract i xs.size := rfl
@[inline]
unsafe def modifyMUnsafe [Monad m] (xs : Array α) (i : Nat) (f : α m α) : m (Array α) := do
@@ -765,8 +734,7 @@ of results.
def mapM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m β) (as : Array α) : m (Array β) :=
-- Note: we cannot use `foldlM` here for the reference implementation because this calls
-- `bind` and `pure` too many times. (We are not assuming `m` is a `LawfulMonad`)
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
map (i : Nat) (bs : Array β) : m (Array β) := do
let rec map (i : Nat) (bs : Array β) : m (Array β) := do
if hlt : i < as.size then
map (i+1) (bs.push ( f as[i]))
else
@@ -926,8 +894,7 @@ entire array is checked.
@[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.
loop (j : Nat) : m Bool := do
let rec loop (j : Nat) : m Bool := do
if hlt : j < stop then
have : j < as.size := Nat.lt_of_lt_of_le hlt h
if ( p as[j]) then
@@ -1265,8 +1232,7 @@ Examples:
-/
@[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) :=
let rec loop (j : Nat) :=
if h : j < as.size then
if p as[j] then some j else loop (j + 1)
else none
@@ -1283,8 +1249,7 @@ Examples:
-/
@[inline]
def findFinIdx? {α : Type u} (p : α Bool) (as : Array α) : Option (Fin as.size) :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
loop (j : Nat) :=
let rec loop (j : Nat) :=
if h : j < as.size then
if p as[j] then some j, h else loop (j + 1)
else none
@@ -1320,7 +1285,6 @@ Examples:
@[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.
def idxOfAux [BEq α] (xs : Array α) (v : α) (i : Nat) : Option (Fin xs.size) :=
if h : i < xs.size then
if xs[i] == v then some i, h
@@ -1331,7 +1295,7 @@ decreasing_by simp_wf; decreasing_trivial_pre_omega
/--
Returns the index of the first element equal to `a`, or the size of the array if no element is equal
Returns the index of the first element equal to `a`, or `none` if no element is equal
to `a`. The index is returned as a `Fin`, which guarantees that it is in bounds.
Examples:
@@ -1730,7 +1694,6 @@ Examples:
* `#[3, 2, 3, 4].popWhile (· > 2) = #[3, 2]`
* `(#[] : Array Nat).popWhile (· > 2) = #[]`
-/
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
def popWhile (p : α Bool) (as : Array α) : Array α :=
if h : as.size > 0 then
if p (as[as.size - 1]'(Nat.sub_lt h (by decide))) then
@@ -1741,7 +1704,7 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
as
decreasing_by simp_wf; decreasing_trivial_pre_omega
@[simp] theorem popWhile_empty {p : α Bool} :
@[simp, grind =] theorem popWhile_empty {p : α Bool} :
popWhile p #[] = #[] := by
simp [popWhile]
@@ -1755,8 +1718,7 @@ Examples:
* `#[0, 1, 2, 3, 2, 1].takeWhile (· < 0) = #[]`
-/
def takeWhile (p : α Bool) (as : Array α) : Array α :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
go (i : Nat) (acc : Array α) : Array α :=
let rec go (i : Nat) (acc : Array α) : Array α :=
if h : i < as.size then
let a := as[i]
if p a then
@@ -1779,7 +1741,6 @@ Examples:
* `#["apple", "pear", "orange"].eraseIdx 1 = #["apple", "orange"]`
* `#["apple", "pear", "orange"].eraseIdx 2 = #["apple", "pear"]`
-/
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
def eraseIdx (xs : Array α) (i : Nat) (h : i < xs.size := by get_elem_tactic) : Array α :=
if h' : i + 1 < xs.size then
let xs' := xs.swap (i + 1) i
@@ -1790,7 +1751,8 @@ termination_by xs.size - i
decreasing_by simp_wf; exact Nat.sub_succ_lt_self _ _ h
-- This is required in `Lean.Data.PersistentHashMap`.
@[simp] theorem size_eraseIdx {xs : Array α} (i : Nat) (h) : (xs.eraseIdx i h).size = xs.size - 1 := by
@[simp, grind =]
theorem size_eraseIdx {xs : Array α} (i : Nat) (h) : (xs.eraseIdx i h).size = xs.size - 1 := by
induction xs, i, h using Array.eraseIdx.induct with
| @case1 xs i h h' xs' ih =>
unfold eraseIdx
@@ -1812,7 +1774,6 @@ Examples:
* `#["apple", "pear", "orange"].eraseIdxIfInBounds 3 = #["apple", "pear", "orange"]`
* `#["apple", "pear", "orange"].eraseIdxIfInBounds 5 = #["apple", "pear", "orange"]`
-/
@[grind]
def eraseIdxIfInBounds (xs : Array α) (i : Nat) : Array α :=
if h : i < xs.size then xs.eraseIdx i h else xs
@@ -1875,8 +1836,7 @@ Examples:
* `#["tues", "thur", "sat"].insertIdx 3 "wed" = #["tues", "thur", "sat", "wed"]`
-/
@[inline] def insertIdx (as : Array α) (i : Nat) (a : α) (_ : i as.size := by get_elem_tactic) : Array α :=
let rec @[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
loop (as : Array α) (j : Fin as.size) :=
let rec loop (as : Array α) (j : Fin as.size) :=
if i < j then
let j' : Fin as.size := j-1, Nat.lt_of_le_of_lt (Nat.pred_le _) j.2
let as := as.swap j' j
@@ -1930,7 +1890,6 @@ def insertIdxIfInBounds (as : Array α) (i : Nat) (a : α) : Array α :=
else
as
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size bs.size) (i : Nat) : Bool :=
if h : i < as.size then
let a := as[i]
@@ -1959,7 +1918,7 @@ def isPrefixOf [BEq α] (as bs : Array α) : Bool :=
else
false
@[semireducible, specialize] -- This is otherwise irreducible because it uses well-founded recursion.
@[specialize]
def zipWithMAux {m : Type v Type w} [Monad m] (as : Array α) (bs : Array β) (f : α β m γ) (i : Nat) (cs : Array γ) : m (Array γ) := do
if h : i < as.size then
let a := as[i]
@@ -2122,7 +2081,6 @@ private def allDiffAuxAux [BEq α] (as : Array α) (a : α) : forall (i : Nat),
have : i < as.size := Nat.lt_trans (Nat.lt_succ_self _) h;
a != as[i] && allDiffAuxAux as a i this
@[semireducible] -- This is otherwise irreducible because it uses well-founded recursion.
private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
if h : i < as.size then
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
@@ -2176,5 +2134,3 @@ instance [ToString α] : ToString (Array α) where
toString xs := String.Internal.append "#" (toString xs.toList)
end Array
export Array (mkArray)

View File

@@ -6,10 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Nat.Linear
public import Init.NotationExtra
public section

View File

@@ -6,9 +6,7 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Array.Basic
public import Init.Data.Int.DivMod.Lemmas
public import Init.Omega
public section
universe u v

View File

@@ -8,7 +8,6 @@ module
prelude
public import Init.Data.List.TakeDrop
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public section
@@ -24,29 +23,6 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
namespace Array
/--
Use the indexing notation `a[i]` instead.
Access an element from an array without needing a runtime bounds checks,
using a `Nat` index and a proof that it is in bounds.
This function does not use `get_elem_tactic` to automatically find the proof that
the index is in bounds. This is because the tactic itself needs to look up values in
arrays.
-/
@[deprecated "Use indexing notation `as[i]` instead" (since := "2025-02-17")]
def get {α : Type u} (xs : @& Array α) (i : @& Nat) (h : LT.lt i xs.size) : α :=
xs.toList.get i, h
/--
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"), expose]
def get! {α : Type u} [Inhabited α] (xs : @& Array α) (i : @& Nat) : α :=
Array.getD xs i default
theorem foldlM_toList.aux [Monad m]
{f : β α m β} {xs : Array α} {i j} (H : xs.size i + j) {b} :
foldlM.loop f xs xs.size (Nat.le_refl _) i j b = (xs.toList.drop j).foldlM f b := by
@@ -55,7 +31,7 @@ theorem foldlM_toList.aux [Monad m]
· cases Nat.not_le_of_gt _ (Nat.zero_add _ H)
· rename_i i; rw [Nat.succ_add] at H
simp [foldlM_toList.aux (j := j+1) H]
rw (occs := [2]) [ List.getElem_cons_drop_succ_eq_drop _]
rw (occs := [2]) [ List.getElem_cons_drop _]
simp
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; simp
@@ -108,9 +84,6 @@ abbrev push_toList := @toList_push
@[simp, grind =] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
@[deprecated toList_pop (since := "2025-02-17")]
abbrev pop_toList := @Array.toList_pop
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
@[simp, grind =] theorem toList_append {xs ys : Array α} :
@@ -127,9 +100,15 @@ abbrev pop_toList := @Array.toList_pop
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, List.nil_append]
@[simp, grind _=_] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
@[simp] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
apply ext'; simp only [toList_append, List.append_assoc]
grind_pattern append_assoc => (xs ++ ys) ++ zs where
xs =/= #[]; ys =/= #[]; zs =/= #[]
grind_pattern append_assoc => xs ++ (ys ++ zs) where
xs =/= #[]; ys =/= #[]; zs =/= #[]
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
@[simp, grind =] theorem toList_appendList {xs : Array α} {l : List α} :
@@ -137,6 +116,4 @@ abbrev pop_toList := @Array.toList_pop
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]
end Array

View File

@@ -6,7 +6,6 @@ Authors: Kim Morrison
module
prelude
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.Count
@@ -63,7 +62,7 @@ theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + coun
rcases xs with xs
simp [List.length_eq_countP_add_countP (p := p)]
@[grind _=_]
@[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]
@@ -100,9 +99,6 @@ theorem countP_le_size : countP p xs ≤ xs.size := by
theorem countP_replicate {a : α} {n : Nat} : countP p (replicate n a) = if p a then n else 0 := by
simp [ List.toArray_replicate, List.countP_replicate]
@[deprecated countP_replicate (since := "2025-03-18")]
abbrev countP_mkArray := @countP_replicate
theorem boole_getElem_le_countP {xs : Array α} {i : Nat} (h : i < xs.size) :
(if p xs[i] then 1 else 0) xs.countP p := by
rcases xs with xs
@@ -263,15 +259,9 @@ theorem count_eq_size {xs : Array α} : count a xs = xs.size ↔ ∀ b ∈ xs, a
@[simp] theorem count_replicate_self {a : α} {n : Nat} : count a (replicate n a) = n := by
simp [ List.toArray_replicate]
@[deprecated count_replicate_self (since := "2025-03-18")]
abbrev count_mkArray_self := @count_replicate_self
theorem count_replicate {a b : α} {n : Nat} : count a (replicate n b) = if b == a then n else 0 := by
simp [ List.toArray_replicate, List.count_replicate]
@[deprecated count_replicate (since := "2025-03-18")]
abbrev count_mkArray := @count_replicate
theorem filter_beq {xs : Array α} (a : α) : xs.filter (· == a) = replicate (count a xs) a := by
rcases xs with xs
simp [List.filter_beq]
@@ -285,9 +275,6 @@ theorem replicate_count_eq_of_count_eq_size {xs : Array α} (h : count a xs = xs
rw [ toList_inj]
simp [List.replicate_count_eq_of_count_eq_length (by simpa using h)]
@[deprecated replicate_count_eq_of_count_eq_size (since := "2025-03-18")]
abbrev mkArray_count_eq_of_count_eq_size := @replicate_count_eq_of_count_eq_size
@[simp] theorem count_filter {xs : Array α} (h : p a) : count a (filter p xs) = count a xs := by
rcases xs with xs
simp [List.count_filter, h]

View File

@@ -6,11 +6,9 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.BEq
public import Init.Data.List.Nat.BEq
public import Init.ByCases
public section
@@ -91,11 +89,41 @@ theorem isEqv_self_beq [BEq α] [ReflBEq α] (xs : Array α) : Array.isEqv xs xs
theorem isEqv_self [DecidableEq α] (xs : Array α) : Array.isEqv xs xs (· = ·) = true := by
simp [isEqv, isEqvAux_self]
instance [DecidableEq α] : DecidableEq (Array α) :=
fun xs ys =>
match h:isEqv xs ys (fun a b => a = b) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
def instDecidableEqImpl [DecidableEq α] : DecidableEq (Array α) := fun xs ys =>
match h:isEqv xs ys (fun a b => a = b) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse (by subst ·; rw [isEqv_self] at h; contradiction)
instance instDecidableEq [DecidableEq α] : DecidableEq (Array α) := fun xs ys =>
match xs with
| [] =>
match ys with
| [] => isTrue rfl
| _ :: _ => isFalse (Array.noConfusion · (List.noConfusion ·))
| a :: as =>
match ys with
| [] => isFalse (Array.noConfusion · (List.noConfusion ·))
| b :: bs => instDecidableEqImpl a :: as b :: bs
@[csimp]
theorem instDecidableEq_csimp : @instDecidableEq = @instDecidableEqImpl :=
Subsingleton.allEq _ _
/--
Equality with `#[]` is decidable even if the underlying type does not have decidable equality.
-/
instance instDecidableEqEmp (xs : Array α) : Decidable (xs = #[]) :=
match xs with
| [] => isTrue rfl
| _ :: _ => isFalse (Array.noConfusion · (List.noConfusion ·))
/--
Equality with `#[]` is decidable even if the underlying type does not have decidable equality.
-/
instance instDecidableEmpEq (ys : Array α) : Decidable (#[] = ys) :=
match ys with
| [] => isTrue rfl
| _ :: _ => isFalse (Array.noConfusion · (List.noConfusion ·))
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then

View File

@@ -6,11 +6,8 @@ Authors: Kim Morrison
module
prelude
public import Init.Data.Array.Basic
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
@@ -142,25 +139,16 @@ theorem eraseP_replicate {n : Nat} {a : α} {p : α → Bool} :
simp only [ List.toArray_replicate, List.eraseP_toArray, List.eraseP_replicate]
split <;> simp
@[deprecated eraseP_replicate (since := "2025-03-18")]
abbrev eraseP_mkArray := @eraseP_replicate
@[simp] theorem eraseP_replicate_of_pos {n : Nat} {a : α} (h : p a) :
(replicate n a).eraseP p = replicate (n - 1) a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[deprecated eraseP_replicate_of_pos (since := "2025-03-18")]
abbrev eraseP_mkArray_of_pos := @eraseP_replicate_of_pos
@[simp] theorem eraseP_replicate_of_neg {n : Nat} {a : α} (h : ¬p a) :
(replicate n a).eraseP p = replicate n a := by
simp only [ List.toArray_replicate, List.eraseP_toArray]
simp [h]
@[deprecated eraseP_replicate_of_neg (since := "2025-03-18")]
abbrev eraseP_mkArray_of_neg := @eraseP_replicate_of_neg
theorem eraseP_eq_iff {p} {xs : Array α} :
xs.eraseP p = ys
(( a xs, ¬ p a) xs = ys)
@@ -281,9 +269,6 @@ theorem erase_replicate [LawfulBEq α] {n : Nat} {a b : α} :
simp only [List.erase_replicate, beq_iff_eq, List.toArray_replicate]
split <;> simp
@[deprecated erase_replicate (since := "2025-03-18")]
abbrev erase_mkArray := @erase_replicate
-- The arguments `a b` are explicit,
-- so they can be specified to prevent `simp` repeatedly applying the lemma.
@[grind =]
@@ -311,19 +296,20 @@ theorem erase_eq_iff [LawfulBEq α] {a : α} {xs : Array α} :
simp only [ List.toArray_replicate, List.erase_toArray]
simp
@[deprecated erase_replicate_self (since := "2025-03-18")]
abbrev erase_mkArray_self := @erase_replicate_self
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
rw [erase_of_not_mem]
simp_all
@[deprecated erase_replicate_ne (since := "2025-03-18")]
abbrev erase_mkArray_ne := @erase_replicate_ne
end erase
/-! ### eraseIdxIfInBounds -/
@[grind =]
theorem eraseIdxIfInBounds_eq {xs : Array α} {i : Nat} :
xs.eraseIdxIfInBounds i = if h : i < xs.size then xs.eraseIdx i else xs := by
simp [eraseIdxIfInBounds]
/-! ### eraseIdx -/
theorem eraseIdx_eq_eraseIdxIfInBounds {xs : Array α} {i : Nat} (h : i < xs.size) :
@@ -425,9 +411,6 @@ theorem eraseIdx_replicate {n : Nat} {a : α} {k : Nat} {h} :
simp only [ List.toArray_replicate, List.eraseIdx_toArray]
simp [List.eraseIdx_replicate, h]
@[deprecated eraseIdx_replicate (since := "2025-03-18")]
abbrev eraseIdx_mkArray := @eraseIdx_replicate
theorem mem_eraseIdx_iff_getElem {x : α} {xs : Array α} {k} {h} : x xs.eraseIdx k h i w, i k xs[i]'w = x := by
rcases xs with xs
simp [List.mem_eraseIdx_iff_getElem, *]

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.TakeDrop
public section
@@ -201,7 +200,7 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
simp [getElem?_extract]
omega
@[simp, grind =] theorem extract_extract {as : Array α} {i j k l : Nat} :
@[simp] 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
@@ -209,6 +208,9 @@ theorem getElem?_extract_of_succ {as : Array α} {j : Nat} :
· simp only [size_extract] at h₁ h₂
simp [Nat.add_assoc]
grind_pattern extract_extract => (as.extract i j).extract k l where
as =/= #[]
theorem extract_eq_empty_of_eq_empty {as : Array α} {i j : Nat} (h : as = #[]) :
as.extract i j = #[] := by
simp [h]
@@ -290,9 +292,6 @@ theorem extract_append_right {as bs : Array α} :
· simp only [size_extract, size_replicate] at h₁ h₂
simp only [getElem_extract, getElem_replicate]
@[deprecated extract_replicate (since := "2025-03-18")]
abbrev extract_mkArray := @extract_replicate
theorem extract_eq_extract_right {as : Array α} {i j j' : Nat} :
as.extract i j = as.extract i j' min (j - i) (as.size - i) = min (j' - i) (as.size - i) := by
rcases as with as
@@ -410,8 +409,6 @@ theorem popWhile_append {xs ys : Array α} :
rcases ys with ys
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, List.dropWhile_append,
List.isEmpty_iff, List.isEmpty_toArray, List.isEmpty_reverse]
-- Why do these not fire with `simp`?
rw [List.popWhile_toArray, List.isEmpty_toArray, List.isEmpty_reverse]
split
· rfl
· simp
@@ -430,32 +427,20 @@ theorem popWhile_append {xs ys : Array α} :
(replicate n a).takeWhile p = (replicate n a).filter p := by
simp [ List.toArray_replicate]
@[deprecated takeWhile_replicate_eq_filter (since := "2025-03-18")]
abbrev takeWhile_mkArray_eq_filter := @takeWhile_replicate_eq_filter
theorem takeWhile_replicate {p : α Bool} :
(replicate n a).takeWhile p = if p a then replicate n a else #[] := by
simp [takeWhile_replicate_eq_filter, filter_replicate]
@[deprecated takeWhile_replicate (since := "2025-03-18")]
abbrev takeWhile_mkArray := @takeWhile_replicate
@[simp] theorem popWhile_replicate_eq_filter_not {p : α Bool} :
(replicate n a).popWhile p = (replicate n a).filter (fun a => !p a) := by
simp [ List.toArray_replicate, List.filter_reverse]
@[deprecated popWhile_replicate_eq_filter_not (since := "2025-03-18")]
abbrev popWhile_mkArray_eq_filter_not := @popWhile_replicate_eq_filter_not
theorem popWhile_replicate {p : α Bool} :
(replicate n a).popWhile p = if p a then #[] else replicate n a := by
simp only [popWhile_replicate_eq_filter_not, size_replicate, filter_replicate, Bool.not_eq_eq_eq_not,
Bool.not_true]
split <;> simp_all
@[deprecated popWhile_replicate (since := "2025-03-18")]
abbrev popWhile_mkArray := @popWhile_replicate
theorem extract_takeWhile {as : Array α} {i : Nat} :
(as.takeWhile p).extract 0 i = (as.extract 0 i).takeWhile p := by
rcases as with as

View File

@@ -6,7 +6,6 @@ Authors: François G. Dorais
module
prelude
public import Init.Data.List.FinRange
public import Init.Data.Array.OfFn
public section

View File

@@ -7,10 +7,7 @@ module
prelude
public import Init.Data.List.Nat.Find
public import Init.Data.Array.Basic
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
@@ -132,31 +129,19 @@ theorem getElem_zero_flatten {xss : Array (Array α)} (h) :
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [ List.toArray_replicate, List.findSome?_replicate]
@[deprecated findSome?_replicate (since := "2025-03-18")]
abbrev findSome?_mkArray := @findSome?_replicate
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
simp [findSome?_replicate, Nat.ne_of_gt h]
@[deprecated findSome?_replicate_of_pos (since := "2025-03-18")]
abbrev findSome?_mkArray_of_pos := @findSome?_replicate_of_pos
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) :
findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[deprecated findSome?_replicate_of_isSome (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isSome := @findSome?_replicate_of_isSome
@[simp] theorem findSome?_replicate_of_isNone (h : (f a).isNone) :
findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h]
@[deprecated findSome?_replicate_of_isNone (since := "2025-03-18")]
abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
/-! ### find? -/
@[simp, grind =] theorem find?_empty : find? p #[] = none := rfl
@@ -278,9 +263,6 @@ theorem find?_flatten_eq_none_iff {xss : Array (Array α)} {p : α → Bool} :
xss.flatten.find? p = none ys xss, x ys, !p x := by
simp
@[deprecated find?_flatten_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_none := @find?_flatten_eq_none_iff
/--
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
some array in `xs` contains `a`, and no earlier element of that array satisfies `p`.
@@ -306,9 +288,6 @@ theorem find?_flatten_eq_some_iff {xss : Array (Array α)} {p : α → Bool} {a
zs.toList, bs.toList.map Array.toList, by simpa using h,
by simpa using h₁, by simpa using h₂
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
@[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
@@ -318,62 +297,35 @@ theorem find?_flatMap_eq_none_iff {xs : Array α} {f : α → Array β} {p : β
(xs.flatMap f).find? p = none x xs, y f x, !p y := by
simp
@[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]
@[deprecated find?_replicate (since := "2025-03-18")]
abbrev find?_mkArray := @find?_replicate
@[simp] theorem find?_replicate_of_size_pos (h : 0 < n) :
find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[deprecated find?_replicate_of_size_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_length_pos := @find?_replicate_of_size_pos
@[simp] theorem find?_replicate_of_pos (h : p a) :
find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_pos (since := "2025-03-18")]
abbrev find?_mkArray_of_pos := @find?_replicate_of_pos
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
@[deprecated find?_replicate_of_neg (since := "2025-03-18")]
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, 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
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α Bool} :
(replicate n a).find? p = some b n 0 p a a = b := by
simp [ List.toArray_replicate]
@[deprecated find?_replicate_eq_some_iff (since := "2025-03-18")]
abbrev find?_mkArray_eq_some_iff := @find?_replicate_eq_some_iff
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
abbrev find?_mkArray_eq_some := @find?_replicate_eq_some_iff
@[simp] theorem get_find?_replicate {n : Nat} {a : α} {p : α Bool} (h) :
((replicate n a).find? p).get h = a := by
simp [ List.toArray_replicate]
@[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} :
@@ -613,9 +565,6 @@ theorem findIdx?_flatten {xss : Array (Array α)} {p : α → Bool} :
simp only [List.findIdx?_toArray]
simp
@[deprecated findIdx?_replicate (since := "2025-03-18")]
abbrev findIdx?_mkArray := @findIdx?_replicate
theorem findIdx?_eq_findSome?_zipIdx {xs : Array α} {p : α Bool} :
xs.findIdx? p = xs.zipIdx.findSome? fun a, i => if p a then some i else none := by
rcases xs with xs

View File

@@ -50,6 +50,6 @@ where
getLit_eq (xs : Array α) (i : Nat) (h₁ : xs.size = n) (h₂ : i < n) : xs.getLit i h₁ h₂ = getElem xs.toList i ((id (α := xs.toList.length = n) h₁) h₂) :=
rfl
go (i : Nat) (hi : i xs.size) : toListLitAux xs n hsz i hi (xs.toList.drop i) = xs.toList := by
induction i <;> simp only [List.drop, toListLitAux, getLit_eq, List.getElem_cons_drop_succ_eq_drop, *]
induction i <;> simp only [List.drop, toListLitAux, getLit_eq, List.getElem_cons_drop, *]
end Array

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.InsertIdx
public section

View File

@@ -6,17 +6,10 @@ Authors: Mario Carneiro, Kim Morrison
module
prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.List.Range
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.List.Nat.Modify
public import Init.Data.List.Nat.Basic
public import Init.Data.List.Monadic
public import Init.Data.List.OfFn
public import Init.Data.Array.Mem
public import Init.Data.Array.DecidableEq
public import Init.Data.Range.Lemmas
public import Init.TacticsExtra
public import Init.Data.List.ToArray
import all Init.Data.List.Control
import all Init.Data.Array.Basic
@@ -80,9 +73,6 @@ theorem ne_empty_of_size_pos (h : 0 < xs.size) : xs ≠ #[] := by
@[simp] theorem size_eq_zero_iff : xs.size = 0 xs = #[] :=
eq_empty_of_size_eq_zero, fun h => h rfl
@[deprecated size_eq_zero_iff (since := "2025-02-24")]
abbrev size_eq_zero := @size_eq_zero_iff
theorem eq_empty_iff_size_eq_zero : xs = #[] xs.size = 0 :=
size_eq_zero_iff.symm
@@ -107,17 +97,10 @@ theorem exists_mem_of_size_eq_add_one {xs : Array α} (h : xs.size = n + 1) :
theorem size_pos_iff {xs : Array α} : 0 < xs.size xs #[] :=
Nat.pos_iff_ne_zero.trans (not_congr size_eq_zero_iff)
@[deprecated size_pos_iff (since := "2025-02-24")]
abbrev size_pos := @size_pos_iff
theorem size_eq_one_iff {xs : Array α} : xs.size = 1 a, xs = #[a] := by
cases xs
simpa using List.length_eq_one_iff
@[deprecated size_eq_one_iff (since := "2025-02-24")]
abbrev size_eq_one := @size_eq_one_iff
/-! ## L[i] and L[i]? -/
theorem getElem?_eq_none_iff {xs : Array α} : xs[i]? = none xs.size i := by
@@ -262,12 +245,13 @@ theorem back_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.pus
replace h := List.append_inj_right' h (by simp)
simpa using h
theorem pop_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : xs = ys := by
theorem push_eq_push {a b : α} {xs ys : Array α} : xs.push a = ys.push b a = b xs = ys := by
cases xs
cases ys
simp at h
replace h := List.append_inj_left' h (by simp)
simp [h]
simp [And.comm]
theorem pop_eq_of_push_eq {a b : α} {xs ys : Array α} (h : xs.push a = ys.push b) : xs = ys :=
(push_eq_push.1 h).2
theorem push_inj_left {a : α} {xs ys : Array α} : xs.push a = ys.push a xs = ys :=
pop_eq_of_push_eq, fun h => by simp [h]
@@ -275,15 +259,6 @@ theorem push_inj_left {a : α} {xs ys : Array α} : xs.push a = ys.push a ↔ xs
theorem push_inj_right {a b : α} {xs : Array α} : xs.push a = xs.push b a = b :=
back_eq_of_push_eq, fun h => by simp [h]
theorem push_eq_push {a b : α} {xs ys : Array α} : xs.push a = ys.push b a = b xs = ys := by
constructor
· intro h
exact back_eq_of_push_eq h, pop_eq_of_push_eq h
· rintro rfl, rfl
rfl
theorem push_eq_append_singleton {as : Array α} {x : α} : as.push x = as ++ #[x] := rfl
theorem exists_push_of_ne_empty {xs : Array α} (h : xs #[]) :
(ys : Array α) (a : α), xs = ys.push a := by
rcases xs with xs
@@ -334,43 +309,26 @@ theorem singleton_inj : #[a] = #[b] ↔ a = b := by
@[simp, grind =] theorem size_replicate {n : Nat} {v : α} : (replicate n v).size = n :=
List.length_replicate ..
@[deprecated size_replicate (since := "2025-03-18")]
abbrev size_mkArray := @size_replicate
@[simp] theorem toList_replicate : (replicate n a).toList = List.replicate n a := by
simp only [replicate]
@[deprecated toList_replicate (since := "2025-03-18")]
abbrev toList_mkArray := @toList_replicate
@[simp, grind =] theorem replicate_zero : replicate 0 a = #[] := rfl
@[deprecated replicate_zero (since := "2025-03-18")]
abbrev mkArray_zero := @replicate_zero
@[grind =]
theorem replicate_succ : replicate (n + 1) a = (replicate n a).push a := by
apply toList_inj.1
simp [List.replicate_succ']
@[deprecated replicate_succ (since := "2025-03-18")]
abbrev mkArray_succ := @replicate_succ
@[simp, grind =] theorem getElem_replicate {n : Nat} {v : α} {i : Nat} (h : i < (replicate n v).size) :
(replicate n v)[i] = v := by simp [ getElem_toList]
@[deprecated getElem_replicate (since := "2025-03-18")]
abbrev getElem_mkArray := @getElem_replicate
@[grind =] theorem getElem?_replicate {n : Nat} {v : α} {i : Nat} :
(replicate n v)[i]? = if i < n then some v else none := by
simp [getElem?_def]
@[deprecated getElem?_replicate (since := "2025-03-18")]
abbrev getElem?_mkArray := @getElem?_replicate
/-! ### mem -/
@[grind ]
theorem not_mem_empty (a : α) : ¬ a #[] := by simp
@[simp, grind =] theorem mem_push {xs : Array α} {x y : α} : x xs.push y x xs x = y := by
@@ -542,18 +500,12 @@ theorem isEmpty_eq_false_iff_exists_mem {xs : Array α} :
@[simp] theorem isEmpty_iff {xs : Array α} : xs.isEmpty xs = #[] := by
cases xs <;> simp
@[deprecated isEmpty_iff (since := "2025-02-17")]
abbrev isEmpty_eq_true := @isEmpty_iff
@[grind ]
theorem empty_of_isEmpty {xs : Array α} (h : xs.isEmpty) : xs = #[] := Array.isEmpty_iff.mp h
@[simp] theorem isEmpty_eq_false_iff {xs : Array α} : xs.isEmpty = false xs #[] := by
cases xs <;> simp
@[deprecated isEmpty_eq_false_iff (since := "2025-02-17")]
abbrev isEmpty_eq_false := @isEmpty_eq_false_iff
theorem isEmpty_iff_size_eq_zero {xs : Array α} : xs.isEmpty xs.size = 0 := by
rw [isEmpty_iff, size_eq_zero_iff]
@@ -857,6 +809,11 @@ theorem contains_eq_true_of_mem [BEq α] [ReflBEq α] {a : α} {as : Array α} (
theorem elem_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
elem a xs = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
@[grind =]
theorem contains_iff_mem [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
xs.contains a = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
@[deprecated contains_iff_mem (since := "2025-10-26")]
theorem contains_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
xs.contains a = true a xs := mem_of_contains_eq_true, contains_eq_true_of_mem
@@ -1096,12 +1053,6 @@ theorem mem_or_eq_of_mem_setIfInBounds
cases xs
simp
@[deprecated beq_empty_eq (since := "2025-04-04")]
abbrev beq_empty_iff := @beq_empty_eq
@[deprecated empty_beq_eq (since := "2025-04-04")]
abbrev empty_beq_iff := @empty_beq_eq
@[simp, grind =] theorem push_beq_push [BEq α] {a b : α} {xs ys : Array α} :
(xs.push a == ys.push b) = (xs == ys && a == b) := by
cases xs
@@ -1122,9 +1073,6 @@ theorem size_eq_of_beq [BEq α] {xs ys : Array α} (h : xs == ys) : xs.size = ys
rw [Bool.eq_iff_iff]
simp +contextual
@[deprecated replicate_beq_replicate (since := "2025-03-18")]
abbrev mkArray_beq_mkArray := @replicate_beq_replicate
private theorem beq_of_beq_singleton [BEq α] {a b : α} : #[a] == #[b] a == b := by
intro h
have : isEqv #[a] #[b] BEq.beq = true := h
@@ -1188,7 +1136,7 @@ where
aux (i bs) :
mapM.map f xs i bs = (xs.toList.drop i).foldlM (fun bs a => bs.push <$> f a) bs := by
unfold mapM.map; split
· rw [ List.getElem_cons_drop_succ_eq_drop _]
· rw [ List.getElem_cons_drop _]
simp only [aux (i + 1), map_eq_pure_bind, List.foldlM_cons, bind_assoc,
pure_bind]
rfl
@@ -1680,12 +1628,15 @@ theorem filterMap_eq_filter {p : α → Bool} (w : stop = as.size) :
cases as
simp
@[grind =]
theorem filterMap_filterMap {f : α Option β} {g : β Option γ} {xs : Array α} :
filterMap g (filterMap f xs) = filterMap (fun x => (f x).bind g) xs := by
cases xs
simp [List.filterMap_filterMap]
grind_pattern filterMap_filterMap => filterMap g (filterMap f xs) where
f =/= some
g =/= some
@[grind =]
theorem map_filterMap {f : α Option β} {g : β γ} {xs : Array α} :
map g (filterMap f xs) = filterMap (fun x => (f x).map g) xs := by
@@ -1740,9 +1691,6 @@ theorem forall_none_of_filterMap_eq_empty (h : filterMap f xs = #[]) : ∀ x ∈
cases xs
simp
@[deprecated filterMap_eq_empty_iff (since := "2025-04-04")]
abbrev filterMap_eq_nil_iff := @filterMap_eq_empty_iff
theorem filterMap_eq_push_iff {f : α Option β} {xs : Array α} {ys : Array β} {b : β} :
filterMap f xs = ys.push b as a bs,
xs = as.push a ++ bs filterMap f as = ys f a = some b ( x, x bs f x = none) := by
@@ -1905,6 +1853,9 @@ theorem getElem_of_append {xs ys zs : Array α} (eq : xs = ys.push a ++ zs) (h :
theorem push_eq_append {a : α} {as : Array α} : as.push a = as ++ #[a] := rfl
@[deprecated push_eq_append (since := "2025-10-26")]
theorem push_eq_append_singleton {as : Array α} {x : α} : as.push x = as ++ #[x] := rfl
theorem append_inj {xs₁ xs₂ ys₁ ys₂ : Array α} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) :
xs₁ = xs₂ ys₁ = ys₂ := by
rcases xs₁ with s₁
@@ -2105,11 +2056,22 @@ theorem append_eq_map_iff {f : α → β} :
| nil => simp
| cons as => induction as.toList <;> simp [*]
@[simp] theorem flatten_toArray_map {L : List (List α)} :
@[simp] theorem flatten_toArray_map_toArray {L : List (List α)} :
(L.map List.toArray).toArray.flatten = L.flatten.toArray := by
apply ext'
simp [Function.comp_def]
@[deprecated flatten_toArray_map_toArray (since := "2025-10-26")]
theorem flatten_toArray_map {L : List (List α)} :
(L.map List.toArray).toArray.flatten = L.flatten.toArray := by
simp
@[grind =]
theorem flatten_map_toArray_toArray {L : List (List α)} :
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
simp
@[deprecated flatten_map_toArray_toArray (since := "2025-10-26")]
theorem flatten_map_toArray {L : List (List α)} :
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
simp
@@ -2156,32 +2118,33 @@ theorem forall_mem_flatten {p : α → Prop} {xss : Array (Array α)} :
theorem flatten_eq_flatMap {xss : Array (Array α)} : flatten xss = xss.flatMap id := by
induction xss using array₂_induction
rw [flatten_toArray_map, List.flatten_eq_flatMap]
rw [flatten_toArray_map_toArray, List.flatten_eq_flatMap]
simp [List.flatMap_map]
@[simp, grind _=_] theorem map_flatten {f : α β} {xss : Array (Array α)} :
(flatten xss).map f = (map (map f) xss).flatten := by
induction xss using array₂_induction with
| of xss =>
simp only [flatten_toArray_map, List.map_toArray, List.map_flatten, List.map_map,
simp only [flatten_toArray_map_toArray, List.map_toArray, List.map_flatten, List.map_map,
Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
rw [ Function.comp_def, List.map_map, flatten_toArray_map_toArray]
@[simp, grind =] theorem filterMap_flatten {f : α Option β} {xss : Array (Array α)} {stop : Nat} (w : stop = xss.flatten.size) :
filterMap f (flatten xss) 0 stop = flatten (map (filterMap f) xss) := by
subst w
induction xss using array₂_induction
simp only [flatten_toArray_map, List.size_toArray, List.length_flatten, List.filterMap_toArray',
List.filterMap_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
List.filterMap_toArray', List.filterMap_flatten, List.map_toArray, List.map_map,
Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map_toArray]
@[simp, grind =] theorem filter_flatten {p : α Bool} {xss : Array (Array α)} {stop : Nat} (w : stop = xss.flatten.size) :
filter p (flatten xss) 0 stop = flatten (map (filter p) xss) := by
subst w
induction xss using array₂_induction
simp only [flatten_toArray_map, List.size_toArray, List.length_flatten, List.filter_toArray',
List.filter_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map]
simp only [flatten_toArray_map_toArray, List.size_toArray, List.length_flatten,
List.filter_toArray', List.filter_flatten, List.map_toArray, List.map_map, Function.comp_def]
rw [ Function.comp_def, List.map_map, flatten_toArray_map_toArray]
theorem flatten_filter_not_isEmpty {xss : Array (Array α)} :
flatten (xss.filter fun xs => !xs.isEmpty) = xss.flatten := by
@@ -2204,23 +2167,23 @@ theorem flatten_filter_ne_empty [DecidablePred fun xs : Array α => xs ≠ #[]]
induction xss using array₂_induction
rcases xs with l
have this : [l.toArray] = [l].map List.toArray := by simp
simp only [List.push_toArray, flatten_toArray_map, List.append_toArray]
rw [this, List.map_append, flatten_toArray_map]
simp only [List.push_toArray, flatten_toArray_map_toArray, List.append_toArray]
rw [this, List.map_append, flatten_toArray_map_toArray]
simp
theorem flatten_flatten {xss : Array (Array (Array α))} : flatten (flatten xss) = flatten (map flatten xss) := by
induction xss using array₃_induction with
| of xss =>
rw [flatten_toArray_map]
rw [flatten_toArray_map_toArray]
have : (xss.map (fun xs => xs.map List.toArray)).flatten = xss.flatten.map List.toArray := by
induction xss with
| nil => simp
| cons xs xss ih =>
simp only [List.map_cons, List.flatten_cons, ih, List.map_append]
rw [this, flatten_toArray_map, List.flatten_flatten, List.map_toArray, Array.map_map,
rw [this, flatten_toArray_map_toArray, List.flatten_flatten, List.map_toArray, Array.map_map,
List.map_toArray, map_map, Function.comp_def]
simp only [Function.comp_apply, flatten_toArray_map]
rw [List.map_toArray, Function.comp_def, List.map_map, flatten_toArray_map]
simp only [Function.comp_apply, flatten_toArray_map_toArray]
rw [List.map_toArray, Function.comp_def, List.map_map, flatten_toArray_map_toArray]
theorem flatten_eq_push_iff {xss : Array (Array α)} {ys : Array α} {y : α} :
xss.flatten = ys.push y
@@ -2229,13 +2192,13 @@ theorem flatten_eq_push_iff {xss : Array (Array α)} {ys : Array α} {y : α} :
induction xss using array₂_induction with
| of xs =>
rcases ys with ys
rw [flatten_toArray_map, List.push_toArray, mk.injEq, List.flatten_eq_append_iff]
rw [flatten_toArray_map_toArray, List.push_toArray, mk.injEq, List.flatten_eq_append_iff]
constructor
· rintro (as, bs, rfl, rfl, h | as, bs, c, cs, ds, rfl, rfl, h)
· rw [List.singleton_eq_flatten_iff] at h
obtain xs, ys, rfl, h₁, h₂ := h
exact ((as ++ xs).map List.toArray).toArray, #[], (ys.map List.toArray).toArray, by simp,
by simpa using h₂, by rw [flatten_toArray_map]; simpa
by simpa using h₂, by rw [flatten_toArray_map_toArray]; simpa
· rw [List.singleton_eq_append_iff] at h
obtain (h₁, h₂ | h₁, h₂) := h
· simp at h₁
@@ -2268,8 +2231,8 @@ theorem push_eq_flatten_iff {xss : Array (Array α)} {ys : Array α} {y : α} :
-- zs = cs ++ ds.flatten := by sorry
/-- Two arrays of subarrays are equal iff their flattens coincide, as well as the sizes of the
subarrays. -/
/-- Two arrays of arrays are equal iff their flattens coincide, as well as the sizes of the
arrays. -/
theorem eq_iff_flatten_eq {xss₁ xss₂ : Array (Array α)} :
xss₁ = xss₂ xss₁.flatten = xss₂.flatten map size xss₁ = map size xss₂ := by
cases xss₁ using array₂_induction with
@@ -2280,18 +2243,12 @@ theorem eq_iff_flatten_eq {xss₁ xss₂ : Array (Array α)} :
rw [List.map_inj_right]
simp +contextual
theorem flatten_toArray_map_toArray {xss : List (List α)} :
(xss.map List.toArray).toArray.flatten = xss.flatten.toArray := by
simp
/-! ### flatMap -/
theorem flatMap_def {xs : Array α} {f : α Array β} : xs.flatMap f = flatten (map f xs) := by
rcases xs with l
simp [flatten_toArray, Function.comp_def, List.flatMap_def]
@[simp, grind =] theorem flatMap_empty {β} {f : α Array β} : (#[] : Array α).flatMap f = #[] := rfl
theorem flatMap_toList {xs : Array α} {f : α List β} :
xs.toList.flatMap f = (xs.flatMap (fun a => (f a).toArray)).toList := by
rcases xs with l
@@ -2302,6 +2259,7 @@ theorem flatMap_toList {xs : Array α} {f : α → List β} :
rcases xs with l
simp
@[deprecated List.flatMap_toArray_cons (since := "2025-10-29")]
theorem flatMap_toArray_cons {β} {f : α Array β} {a : α} {as : List α} :
(a :: as).toArray.flatMap f = f a ++ as.toArray.flatMap f := by
simp [flatMap]
@@ -2312,6 +2270,7 @@ theorem flatMap_toArray_cons {β} {f : α → Array β} {a : α} {as : List α}
intro cs
induction as generalizing cs <;> simp_all
@[deprecated List.flatMap_toArray (since := "2025-10-29")]
theorem flatMap_toArray {β} {f : α Array β} {as : List α} :
as.toArray.flatMap f = (as.flatMap (fun a => (f a).toList)).toArray := by
simp
@@ -2412,77 +2371,44 @@ theorem flatMap_eq_foldl {f : α → Array β} {xs : Array α} :
@[simp] theorem replicate_one : replicate 1 a = #[a] := rfl
@[deprecated replicate_one (since := "2025-03-18")]
abbrev mkArray_one := @replicate_one
/-- Variant of `replicate_succ` that prepends `a` at the beginning of the array. -/
theorem replicate_succ' : replicate (n + 1) a = #[a] ++ replicate n a := by
apply Array.ext'
simp [List.replicate_succ]
@[deprecated replicate_succ' (since := "2025-03-18")]
abbrev mkArray_succ' := @replicate_succ'
@[simp, grind =] theorem mem_replicate {a b : α} {n} : b replicate n a n 0 b = a := by
unfold replicate
simp only [List.mem_toArray, List.mem_replicate]
@[deprecated mem_replicate (since := "2025-03-18")]
abbrev mem_mkArray := @mem_replicate
@[grind ] theorem eq_of_mem_replicate {a b : α} {n} (h : b replicate n a) : b = a := (mem_replicate.1 h).2
@[deprecated eq_of_mem_mkArray (since := "2025-03-18")]
abbrev eq_of_mem_mkArray := @eq_of_mem_replicate
theorem forall_mem_replicate {p : α Prop} {a : α} {n} :
( b, b replicate n a p b) n = 0 p a := by
cases n <;> simp [mem_replicate]
@[deprecated forall_mem_replicate (since := "2025-03-18")]
abbrev forall_mem_mkArray := @forall_mem_replicate
@[simp] theorem replicate_succ_ne_empty {n : Nat} {a : α} : replicate (n+1) a #[] := by
simp [replicate_succ]
@[deprecated replicate_succ_ne_empty (since := "2025-03-18")]
abbrev mkArray_succ_ne_empty := @replicate_succ_ne_empty
@[simp] theorem replicate_eq_empty_iff {n : Nat} {a : α} : replicate n a = #[] n = 0 := by
cases n <;> simp
@[deprecated replicate_eq_empty_iff (since := "2025-03-18")]
abbrev mkArray_eq_empty_iff := @replicate_eq_empty_iff
@[simp] theorem replicate_inj : replicate n a = replicate m b n = m (n = 0 a = b) := by
rw [ toList_inj]
simp
@[deprecated replicate_inj (since := "2025-03-18")]
abbrev mkArray_inj := @replicate_inj
theorem eq_replicate_of_mem {a : α} {xs : Array α} (h : (b) (_ : b xs), b = a) : xs = replicate xs.size a := by
rw [ toList_inj]
simpa using List.eq_replicate_of_mem (by simpa using h)
@[deprecated eq_replicate_of_mem (since := "2025-03-18")]
abbrev eq_mkArray_of_mem := @eq_replicate_of_mem
theorem eq_replicate_iff {a : α} {n} {xs : Array α} :
xs = replicate n a xs.size = n (b) (_ : b xs), b = a := by
rw [ toList_inj]
simpa using List.eq_replicate_iff (l := xs.toList)
@[deprecated eq_replicate_iff (since := "2025-03-18")]
abbrev eq_mkArray_iff := @eq_replicate_iff
theorem map_eq_replicate_iff {xs : Array α} {f : α β} {b : β} :
xs.map f = replicate xs.size b x xs, f x = b := by
simp [eq_replicate_iff]
@[deprecated map_eq_replicate_iff (since := "2025-03-18")]
abbrev map_eq_mkArray_iff := @map_eq_replicate_iff
@[simp] theorem map_const {xs : Array α} {b : β} : map (Function.const α b) xs = replicate xs.size b :=
map_eq_replicate_iff.mpr fun _ _ => rfl
@@ -2499,143 +2425,86 @@ theorem map_const' {xs : Array α} {b : β} : map (fun _ => b) xs = replicate xs
apply Array.ext'
simp
@[deprecated set_replicate_self (since := "2025-03-18")]
abbrev set_mkArray_self := @set_replicate_self
@[simp] theorem setIfInBounds_replicate_self : (replicate n a).setIfInBounds i a = replicate n a := by
apply Array.ext'
simp
@[deprecated setIfInBounds_replicate_self (since := "2025-03-18")]
abbrev setIfInBounds_mkArray_self := @setIfInBounds_replicate_self
@[simp] theorem replicate_append_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
apply Array.ext'
simp
@[deprecated replicate_append_replicate (since := "2025-03-18")]
abbrev mkArray_append_mkArray := @replicate_append_replicate
theorem append_eq_replicate_iff {xs ys : Array α} {a : α} :
xs ++ ys = replicate n a
xs.size + ys.size = n xs = replicate xs.size a ys = replicate ys.size a := by
simp [ toList_inj, List.append_eq_replicate_iff]
@[deprecated append_eq_replicate_iff (since := "2025-03-18")]
abbrev append_eq_mkArray_iff := @append_eq_replicate_iff
theorem replicate_eq_append_iff {xs ys : Array α} {a : α} :
replicate n a = xs ++ ys
xs.size + ys.size = n xs = replicate xs.size a ys = replicate ys.size a := by
rw [eq_comm, append_eq_replicate_iff]
@[deprecated replicate_eq_append_iff (since := "2025-03-18")]
abbrev replicate_eq_mkArray_iff := @replicate_eq_append_iff
@[simp] theorem map_replicate : (replicate n a).map f = replicate n (f a) := by
apply Array.ext'
simp
@[deprecated map_replicate (since := "2025-03-18")]
abbrev map_mkArray := @map_replicate
@[grind =] theorem filter_replicate (w : stop = n) :
(replicate n a).filter p 0 stop = if p a then replicate n a else #[] := by
apply Array.ext'
simp only [w]
split <;> simp_all
@[deprecated filter_replicate (since := "2025-03-18")]
abbrev filter_mkArray := @filter_replicate
@[simp] theorem filter_replicate_of_pos (w : stop = n) (h : p a) :
(replicate n a).filter p 0 stop = replicate n a := by
simp [filter_replicate, h, w]
@[deprecated filter_replicate_of_pos (since := "2025-03-18")]
abbrev filter_mkArray_of_pos := @filter_replicate_of_pos
@[simp] theorem filter_replicate_of_neg (w : stop = n) (h : ¬ p a) :
(replicate n a).filter p 0 stop = #[] := by
simp [filter_replicate, h, w]
@[deprecated filter_replicate_of_neg (since := "2025-03-18")]
abbrev filter_mkArray_of_neg := @filter_replicate_of_neg
theorem filterMap_replicate {f : α Option β} (w : stop = n := by simp) :
(replicate n a).filterMap f 0 stop = match f a with | none => #[] | .some b => replicate n b := by
apply Array.ext'
simp only [w, size_replicate, toList_filterMap', toList_replicate, List.filterMap_replicate]
split <;> simp_all
@[deprecated filterMap_replicate (since := "2025-03-18")]
abbrev filterMap_mkArray := @filterMap_replicate
-- This is not a useful `simp` lemma because `b` is unknown.
theorem filterMap_replicate_of_some {f : α Option β} (h : f a = some b) :
(replicate n a).filterMap f = replicate n b := by
simp [filterMap_replicate, h]
@[deprecated filterMap_replicate_of_some (since := "2025-03-18")]
abbrev filterMap_mkArray_of_some := @filterMap_replicate_of_some
@[simp] theorem filterMap_replicate_of_isSome {f : α Option β} (h : (f a).isSome) :
(replicate n a).filterMap f = replicate n (Option.get _ h) := by
match w : f a, h with
| some b, _ => simp [filterMap_replicate, w]
@[deprecated filterMap_replicate_of_isSome (since := "2025-03-18")]
abbrev filterMap_mkArray_of_isSome := @filterMap_replicate_of_isSome
@[simp] theorem filterMap_replicate_of_none {f : α Option β} (h : f a = none) :
(replicate n a).filterMap f = #[] := by
simp [filterMap_replicate, h]
@[deprecated filterMap_replicate_of_none (since := "2025-03-18")]
abbrev filterMap_mkArray_of_none := @filterMap_replicate_of_none
@[simp] theorem flatten_replicate_empty : (replicate n (#[] : Array α)).flatten = #[] := by
rw [ toList_inj]
simp
@[deprecated flatten_replicate_empty (since := "2025-03-18")]
abbrev flatten_mkArray_empty := @flatten_replicate_empty
@[simp] theorem flatten_replicate_singleton : (replicate n #[a]).flatten = replicate n a := by
rw [ toList_inj]
simp
@[deprecated flatten_replicate_singleton (since := "2025-03-18")]
abbrev flatten_mkArray_singleton := @flatten_replicate_singleton
@[simp] theorem flatten_replicate_replicate : (replicate n (replicate m a)).flatten = replicate (n * m) a := by
rw [ toList_inj]
simp
@[deprecated flatten_replicate_replicate (since := "2025-03-18")]
abbrev flatten_mkArray_replicate := @flatten_replicate_replicate
theorem flatMap_replicate {f : α Array β} : (replicate n a).flatMap f = (replicate n (f a)).flatten := by
rw [ toList_inj]
simp [List.flatMap_replicate]
@[deprecated flatMap_replicate (since := "2025-03-18")]
abbrev flatMap_mkArray := @flatMap_replicate
@[simp] theorem isEmpty_replicate : (replicate n a).isEmpty = decide (n = 0) := by
rw [ List.toArray_replicate, List.isEmpty_toArray]
simp
@[deprecated isEmpty_replicate (since := "2025-03-18")]
abbrev isEmpty_mkArray := @isEmpty_replicate
@[simp] theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
rw [ List.toArray_replicate, List.sum_toArray]
simp
@[deprecated sum_replicate_nat (since := "2025-03-18")]
abbrev sum_mkArray_nat := @sum_replicate_nat
/-! ### Preliminaries about `swap` needed for `reverse`. -/
@[grind =]
@@ -2677,8 +2546,8 @@ theorem getElem?_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} : (xs.swap i
split <;> rename_i h₃
· simp only [ h₃, Nat.not_le.2 (Nat.lt_succ_self _), Nat.le_refl, false_and]
exact (List.getElem?_reverse' (Eq.trans (by simp +arith) h)).symm
simp only [Nat.succ_le, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
Nat.lt_succ.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
simp only [Nat.succ_le_iff, Nat.lt_iff_le_and_ne.trans (and_iff_left h₃),
Nat.lt_succ_iff.symm.trans (Nat.lt_iff_le_and_ne.trans (and_iff_left (Ne.symm h₂)))]
· rw [H]; split <;> rename_i h₂
· cases Nat.le_antisymm (Nat.not_lt.1 h₁) (Nat.le_trans h₂.1 h₂.2)
cases Nat.le_antisymm h₂.1 h₂.2
@@ -2693,7 +2562,7 @@ theorem getElem?_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} : (xs.swap i
split
· rfl
· rename_i h
simp only [ show k < _ + 1 _ from Nat.lt_succ (n := xs.size - 1), this, Nat.zero_le,
simp only [ show k < _ + 1 _ from Nat.lt_succ_iff (n := xs.size - 1), this, Nat.zero_le,
true_and, Nat.not_lt] at h
rw [List.getElem?_eq_none_iff.2 _, List.getElem?_eq_none_iff.2 (xs.toList.length_reverse _)]
@@ -2822,9 +2691,6 @@ theorem flatten_reverse {xss : Array (Array α)} :
rw [ toList_inj]
simp
@[deprecated reverse_replicate (since := "2025-03-18")]
abbrev reverse_mkArray := @reverse_replicate
/-! ### extract -/
theorem extract_loop_zero {xs ys : Array α} {start : Nat} : extract.loop xs 0 start ys = ys := by
@@ -2844,8 +2710,8 @@ theorem extract_loop_eq_aux {xs ys : Array α} {size start : Nat} :
| zero => rw [extract_loop_zero, extract_loop_zero, append_empty]
| succ size ih =>
if h : start < xs.size then
rw [extract_loop_succ (h := h), ih, push_eq_append_singleton]
rw [extract_loop_succ (h := h), ih (ys := #[].push _), push_eq_append_singleton, empty_append]
rw [extract_loop_succ (h := h), ih, push_eq_append]
rw [extract_loop_succ (h := h), ih (ys := #[].push _), push_eq_append, empty_append]
rw [append_assoc]
else
rw [extract_loop_of_ge (h := Nat.le_of_not_lt h)]
@@ -2996,11 +2862,6 @@ theorem _root_.List.toArray_drop {l : List α} {k : Nat} :
(l.drop k).toArray = l.toArray.extract k := by
rw [List.drop_eq_extract, List.extract_toArray, List.size_toArray]
@[deprecated extract_size (since := "2025-02-27")]
theorem take_size {xs : Array α} : xs.take xs.size = xs := by
cases xs
simp
/-! ### shrink -/
@[simp] private theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
@@ -3467,6 +3328,16 @@ theorem foldr_filterMap {f : α → Option β} {g : β → γγ} {xs : Arra
(xs.filterMap f).foldr g init = xs.foldr (fun x y => match f x with | some b => g b y | none => y) init := by
simp [foldr_filterMap']
theorem foldl_flatMap {f : α Array β} {g : γ β γ} {xs : Array α} {init : γ} :
(xs.flatMap f).foldl g init = xs.foldl (fun acc x => (f x).foldl g acc) init := by
rcases xs with l
simp [List.foldl_flatMap]
theorem foldr_flatMap {f : α Array β} {g : β γ γ} {xs : Array α} {init : γ} :
(xs.flatMap f).foldr g init = xs.foldr (fun x acc => (f x).foldr g acc) init := by
rcases xs with l
simp [List.foldr_flatMap]
theorem foldl_map_hom' {g : α β} {f : α α α} {f' : β β β} {a : α} {xs : Array α}
{stop : Nat} (h : x y, f' (g x) (g y) = g (f x y)) (w : stop = xs.size) :
(xs.map g).foldl f' (g a) 0 stop = g (xs.foldl f a) := by
@@ -3586,8 +3457,6 @@ theorem foldr_eq_foldl_reverse {xs : Array α} {f : α → β → β} {b} :
subst w
rw [foldr_eq_foldl_reverse, foldl_push_eq_append rfl, map_reverse]
@[deprecated foldr_push_eq_append (since := "2025-02-09")] abbrev foldr_flip_push_eq_append := @foldr_push_eq_append
theorem foldl_assoc {op : α α α} [ha : Std.Associative op] {xs : Array α} {a₁ a₂} :
xs.foldl op (op a₁ a₂) = op a₁ (xs.foldl op a₂) := by
rcases xs with l
@@ -3686,11 +3555,6 @@ theorem mem_of_back? {xs : Array α} {a : α} (h : xs.back? = some a) : a ∈ xs
rcases ys with ys
simp only [List.append_toArray, List.back_toArray, List.getLast_append, List.isEmpty_iff,
List.isEmpty_toArray]
split
· rw [dif_pos]
simpa only [List.isEmpty_toArray]
· rw [dif_neg]
simpa only [List.isEmpty_toArray]
theorem back_append_right {xs ys : Array α} (h : 0 < ys.size) :
(xs ++ ys).back (by simp; omega) = ys.back h := by
@@ -3741,15 +3605,9 @@ theorem back?_replicate {a : α} {n : Nat} :
rw [replicate_eq_toArray_replicate]
simp only [List.back?_toArray, List.getLast?_replicate]
@[deprecated back?_replicate (since := "2025-03-18")]
abbrev back?_mkArray := @back?_replicate
@[simp] theorem back_replicate {xs : Array α} (w : 0 < n) : (replicate n xs).back (by simpa using w) = xs := by
simp [back_eq_getElem]
@[deprecated back_replicate (since := "2025-03-18")]
abbrev back_mkArray := @back_replicate
/-! ## Additional operations -/
/-! ### leftpad -/
@@ -3767,9 +3625,6 @@ theorem size_rightpad {n : Nat} {a : α} {xs : Array α} :
theorem elem_push_self [BEq α] [LawfulBEq α] {xs : Array α} {a : α} : (xs.push a).elem a = true := by simp
@[deprecated elem_push_self (since := "2025-04-04")]
abbrev elem_cons_self := @elem_push_self
theorem contains_eq_any_beq [BEq α] {xs : Array α} {a : α} : xs.contains a = xs.any (a == ·) := by
rcases xs with xs
simp [List.contains_eq_any_beq]
@@ -3783,11 +3638,6 @@ theorem contains_iff_exists_mem_beq [BEq α] {xs : Array α} {a : α} :
-- With `LawfulBEq α`, it would be better to use `contains_iff_mem` directly.
grind_pattern contains_iff_exists_mem_beq => xs.contains a
@[grind _=_]
theorem contains_iff_mem [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.contains a a xs := by
simp
@[simp, grind =]
theorem contains_toList [BEq α] {xs : Array α} {x : α} :
xs.toList.contains x = xs.contains x := by
@@ -3847,9 +3697,6 @@ theorem pop_append {xs ys : Array α} :
@[simp, grind =] theorem pop_replicate {n : Nat} {a : α} : (replicate n a).pop = replicate (n - 1) a := by
ext <;> simp
@[deprecated pop_replicate (since := "2025-03-18")]
abbrev pop_mkArray := @pop_replicate
/-! ## Logic -/
/-! ### any / all -/
@@ -4079,16 +3926,10 @@ theorem all_filterMap {xs : Array α} {f : α → Option β} {p : β → Bool} :
(replicate n a).any f = if n = 0 then false else f a := by
induction n <;> simp_all [replicate_succ']
@[deprecated any_replicate (since := "2025-03-18")]
abbrev any_mkArray := @any_replicate
@[simp] theorem all_replicate {n : Nat} {a : α} :
(replicate n a).all f = if n = 0 then true else f a := by
induction n <;> simp_all +contextual [replicate_succ']
@[deprecated all_replicate (since := "2025-03-18")]
abbrev all_mkArray := @all_replicate
/-! ### modify -/
@[simp, grind =] theorem size_modify {xs : Array α} {i : Nat} {f : α α} : (xs.modify i f).size = xs.size := by
@@ -4263,17 +4104,11 @@ theorem replace_extract {xs : Array α} {i : Nat} :
(replicate n a).replace a b = #[b] ++ replicate (n - 1) a := by
cases n <;> simp_all [replicate_succ', replace_append]
@[deprecated replace_replicate_self (since := "2025-03-18")]
abbrev replace_mkArray_self := @replace_replicate_self
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
(replicate n a).replace b c = replicate n a := by
rw [replace_of_not_mem]
simp_all
@[deprecated replace_replicate_ne (since := "2025-03-18")]
abbrev replace_mkArray_ne := @replace_replicate_ne
end replace
/-! ### toListRev -/
@@ -4441,9 +4276,6 @@ theorem getElem!_eq_getD [Inhabited α] {xs : Array α} {i} : xs[i]! = xs.getD i
@[deprecated mem_toList_iff (since := "2025-05-26")]
theorem mem_toList {a : α} {xs : Array α} : a xs.toList a xs := mem_def.symm
@[deprecated not_mem_empty (since := "2025-03-25")]
theorem not_mem_nil (a : α) : ¬ a #[] := nofun
/-! # get lemmas -/
theorem lt_of_getElem {x : α} {xs : Array α} {i : Nat} {hidx : i < xs.size} (_ : xs[i] = x) :
@@ -4455,6 +4287,7 @@ theorem getElem_fin_eq_getElem_toList {xs : Array α} {i : Fin xs.size} : xs[i]
@[simp] theorem ugetElem_eq_getElem {xs : Array α} {i : USize} (h : i.toNat < xs.size) :
xs[i] = xs[i.toNat] := rfl
@[deprecated getElem?_eq_none (since := "2025-10-26")]
theorem getElem?_size_le {xs : Array α} {i : Nat} (h : xs.size i) : xs[i]? = none := by
simp [getElem?_neg, h]
@@ -4474,6 +4307,7 @@ theorem getElem?_push_lt {xs : Array α} {x : α} {i : Nat} (h : i < xs.size) :
(xs.push x)[i]? = some xs[i] := by
rw [getElem?_pos (xs.push x) i (size_push _ Nat.lt_succ_of_lt h), getElem_push_lt]
@[deprecated getElem?_push_size (since := "2025-10-26")]
theorem getElem?_push_eq {xs : Array α} {x : α} : (xs.push x)[xs.size]? = some x := by
rw [getElem?_pos (xs.push x) xs.size (size_push _ Nat.lt_succ_self xs.size), getElem_push_eq]
@@ -4492,12 +4326,6 @@ theorem getElem?_push_eq {xs : Array α} {x : α} : (xs.push x)[xs.size]? = some
cases xs
simp
/-! ### contains -/
@[deprecated contains_iff (since := "2025-04-07")]
abbrev contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a a xs :=
contains_iff
/-! ### isPrefixOf -/
@[simp, grind =] theorem isPrefixOf_toList [BEq α] {xs ys : Array α} :
@@ -4611,7 +4439,8 @@ theorem uset_toArray {l : List α} {i : USize} {a : α} {h : i.toNat < l.toArray
apply ext'
simp
@[simp, grind =] theorem flatten_toArray {L : List (List α)} :
@[deprecated Array.flatten_map_toArray_toArray (since := "2025-10-26")]
theorem flatten_toArray {L : List (List α)} :
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
apply ext'
simp
@@ -4712,44 +4541,3 @@ namespace List
simp_all
end List
/-! ### Deprecations -/
namespace Array
set_option linter.deprecated false in
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (xs : Array α) (i : Nat) : xs.get? i = xs[i]? := rfl
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
set_option linter.deprecated false in
@[deprecated getElem!_eq_getD (since := "2025-02-12")]
theorem get!_eq_getD [Inhabited α] (xs : Array α) : xs.get! n = xs.getD n default := rfl
set_option linter.deprecated false in
@[deprecated "Use `a[i]!` instead of `a.get! i`." (since := "2025-02-12")]
theorem get!_eq_getD_getElem? [Inhabited α] (xs : Array α) (i : Nat) :
xs.get! i = xs[i]?.getD default := by
by_cases p : i < xs.size <;>
simp [get!, getD_eq_getD_getElem?, p]
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_getElem? := @get!_eq_getD_getElem?
set_option linter.deprecated false in
@[deprecated "`Array.get?` is deprecated, use `a[i]?` instead." (since := "2025-02-12")]
theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.get? i := by
simp [ getElem?_toList]
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
/-! ### set -/
@[deprecated getElem?_set_self (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
@[deprecated getElem?_set_ne (since := "2025-02-27")] abbrev get?_set_ne := @getElem?_set_ne
@[deprecated getElem?_set (since := "2025-02-27")] abbrev get?_set := @getElem?_set
@[deprecated get_set (since := "2025-02-27")] abbrev get_set := @getElem_set
@[deprecated get_set_ne (since := "2025-02-27")] abbrev get_set_ne := @getElem_set_ne
end Array

View File

@@ -8,5 +8,3 @@ module
prelude
public import Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lex.Lemmas
public section

View File

@@ -6,11 +6,8 @@ Author: Kim Morrison
module
prelude
public import Init.Core
import Init.Data.Array.Basic
import Init.Data.Nat.Lemmas
import Init.Data.Range.Polymorphic.Iterators
import Init.Data.Range.Polymorphic.Nat
public import Init.Data.Range.Polymorphic.Iterators
public import Init.Data.Range.Polymorphic.Nat
import Init.Data.Iterators.Consumers
public section
@@ -31,7 +28,7 @@ Specifically, `Array.lex as bs lt` is true if
def lex [BEq α] (as bs : Array α) (lt : α α Bool := by exact (· < ·)) : Bool := Id.run do
for h : i in 0...(min as.size bs.size) do
-- TODO: `get_elem_tactic` should be able to find this itself.
have : i < min as.size bs.size := Std.PRange.lt_upper_of_mem h
have : i < min as.size bs.size := Std.Rco.lt_upper_of_mem h
if lt as[i] bs[i] then
return true
else if as[i] != bs[i] then

View File

@@ -10,9 +10,7 @@ import all Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lex.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Lex
import Init.Data.Range.Polymorphic.Lemmas
import Init.Data.Range.Polymorphic.NatLemmas
import Init.Data.Order.Lemmas
public section
@@ -36,14 +34,24 @@ grind_pattern _root_.List.le_toArray => l₁.toArray ≤ l₂.toArray
grind_pattern lt_toList => xs.toList < ys.toList
grind_pattern le_toList => xs.toList ys.toList
@[simp]
protected theorem not_lt [LT α] {xs ys : Array α} : ¬ xs < ys ys xs := Iff.rfl
@[deprecated Array.not_lt (since := "2025-10-26")]
protected theorem not_lt_iff_ge [LT α] {xs ys : Array α} : ¬ xs < ys ys xs := Iff.rfl
@[simp]
protected theorem not_le [LT α] {xs ys : Array α} :
¬ xs ys ys < xs :=
Classical.not_not
@[deprecated Array.not_le (since := "2025-10-26")]
protected theorem not_le_iff_gt [LT α] {xs ys : Array α} :
¬ xs ys ys < xs :=
Classical.not_not
@[simp] theorem lex_empty [BEq α] {lt : α α Bool} {xs : Array α} : xs.lex #[] lt = false := by
rw [lex, Std.PRange.forIn'_eq_match]
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
simp [lex, Std.Rco.forIn'_eq_if]
private theorem cons_lex_cons.forIn'_congr_aux [Monad m] {as bs : ρ} {_ : Membership α ρ}
[ForIn' m ρ α inferInstance] (w : as = bs)
@@ -64,14 +72,14 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
(#[a] ++ xs).lex (#[b] ++ ys) lt =
(lt a b || a == b && xs.lex ys lt) := by
simp only [lex, size_append, List.size_toArray, List.length_cons, List.length_nil, Nat.zero_add,
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.PRange.forIn'_eq_forIn'_toList]
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.Rco.forIn'_eq_forIn'_toList]
conv =>
lhs; congr; congr
rw [cons_lex_cons.forIn'_congr_aux Std.PRange.toList_eq_match rfl (fun _ _ _ => rfl)]
simp only [Std.PRange.SupportsUpperBound.IsSatisfied, bind_pure_comp, map_pure]
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if_roo rfl (fun _ _ _ => rfl)]
simp only [bind_pure_comp, map_pure]
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
simp only [Std.PRange.toList_Rox_eq_toList_Rcx_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_Rco_succ_succ,
simp only [Std.toList_roo_eq_toList_rco_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_rco_succ_succ,
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
cases lt a b
@@ -83,16 +91,10 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
induction l₁ generalizing l₂ with
| nil =>
cases l₂
· rw [lex, Std.PRange.forIn'_eq_match]
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
· rw [lex, Std.PRange.forIn'_eq_match]
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
cases l₂ <;> simp [lex, Std.Rco.forIn'_eq_if]
| cons x l₁ ih =>
cases l₂ with
| nil =>
rw [lex, Std.PRange.forIn'_eq_match]
simp [Std.PRange.SupportsUpperBound.IsSatisfied]
| nil => simp [lex, Std.Rco.forIn'_eq_if]
| cons y l₂ =>
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
@@ -149,7 +151,7 @@ protected theorem lt_of_le_of_lt [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrd
@[deprecated Array.lt_of_le_of_lt (since := "2025-08-01")]
protected theorem lt_of_le_of_lt' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₂ : Std.Trichotomous (· < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys < zs) : xs < zs :=
letI := LE.ofLT α
@@ -163,7 +165,7 @@ protected theorem le_trans [LE α] [LT α] [LawfulOrderLT α] [IsLinearOrder α]
@[deprecated Array.le_trans (since := "2025-08-01")]
protected theorem le_trans' [LT α]
[i₁ : Std.Asymm (· < · : α α Prop)]
[i₂ : Std.Antisymm (¬ · < · : α α Prop)]
[i₂ : Std.Trichotomous (· < · : α α Prop)]
[i₃ : Trans (¬ · < · : α α Prop) (¬ · < ·) (¬ · < ·)]
{xs ys zs : Array α} (h₁ : xs ys) (h₂ : ys zs) : xs zs :=
letI := LE.ofLT α
@@ -187,12 +189,6 @@ protected theorem le_total [LT α]
[i : Std.Asymm (· < · : α α Prop)] (xs ys : Array α) : xs ys ys xs :=
List.le_total xs.toList ys.toList
@[simp] protected theorem not_lt [LT α]
{xs ys : Array α} : ¬ xs < ys ys xs := Iff.rfl
@[simp] protected theorem not_le [LT α]
{xs ys : Array α} : ¬ ys xs xs < ys := Classical.not_not
protected theorem le_of_lt [LT α]
[i : Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} (h : xs < ys) : xs ys :=
@@ -200,7 +196,7 @@ protected theorem le_of_lt [LT α]
protected theorem le_iff_lt_or_eq [LT α]
[Std.Irrefl (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Trichotomous (· < · : α α Prop)]
[Std.Asymm (· < · : α α Prop)]
{xs ys : Array α} : xs ys xs < ys xs = ys := by
simpa using List.le_iff_lt_or_eq (l₁ := xs.toList) (l₂ := ys.toList)
@@ -289,7 +285,7 @@ protected theorem lt_iff_exists [LT α] {xs ys : Array α} :
protected theorem le_iff_exists [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)] {xs ys : Array α} :
[Std.Trichotomous (· < · : α α Prop)] {xs ys : Array α} :
xs ys
(xs = ys.take xs.size)
( (i : Nat) (h₁ : i < xs.size) (h₂ : i < ys.size),
@@ -308,7 +304,7 @@ theorem append_left_lt [LT α] {xs ys zs : Array α} (h : ys < zs) :
theorem append_left_le [LT α]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Trichotomous (· < · : α α Prop)]
{xs ys zs : Array α} (h : ys zs) :
xs ++ ys xs ++ zs := by
cases xs
@@ -331,9 +327,9 @@ protected theorem map_lt [LT α] [LT β]
protected theorem map_le [LT α] [LT β]
[Std.Asymm (· < · : α α Prop)]
[Std.Antisymm (¬ · < · : α α Prop)]
[Std.Trichotomous (· < · : α α Prop)]
[Std.Asymm (· < · : β β Prop)]
[Std.Antisymm (¬ · < · : β β Prop)]
[Std.Trichotomous (· < · : β β Prop)]
{xs ys : Array α} {f : α β} (w : x y, x < y f x < f y) (h : xs ys) :
map f xs map f ys := by
cases xs

View File

@@ -6,10 +6,7 @@ Authors: Mario Carneiro, Kim Morrison
module
prelude
public import Init.Data.Array.Basic
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 Init.Data.List.MapIdx
import all Init.Data.List.MapIdx
@@ -299,9 +296,6 @@ theorem mapFinIdx_eq_replicate_iff {xs : Array α} {f : (i : Nat) → α → (h
rw [ toList_inj]
simp [List.mapFinIdx_eq_replicate_iff]
@[deprecated mapFinIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapFinIdx_eq_mkArray_iff := @mapFinIdx_eq_replicate_iff
@[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
@@ -441,9 +435,6 @@ theorem mapIdx_eq_replicate_iff {xs : Array α} {f : Nat → α → β} {b : β}
rw [ toList_inj]
simp [List.mapIdx_eq_replicate_iff]
@[deprecated mapIdx_eq_replicate_iff (since := "2025-03-18")]
abbrev mapIdx_eq_mkArray_iff := @mapIdx_eq_replicate_iff
@[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

View File

@@ -6,8 +6,6 @@ Authors: Leonardo de Moura, Joachim Breitner
module
prelude
public import Init.Data.Array.Basic
public import Init.Data.Nat.Linear
public import Init.Data.List.BasicAux
public section

View File

@@ -6,13 +6,9 @@ Authors: Kim Morrison
module
prelude
public import Init.Data.List.Control
import all Init.Data.List.Control
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Attach
public import Init.Data.List.Monadic
public section

View File

@@ -6,11 +6,8 @@ Authors: Kim Morrison
module
prelude
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Monadic
public import Init.Data.List.OfFn
public import Init.Data.List.FinRange
public section

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.List.Nat.Perm
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
@@ -85,9 +84,6 @@ theorem Perm.size_eq {xs ys : Array α} (p : xs ~ ys) : xs.size = ys.size := by
simp only [perm_iff_toList_perm] at p
simpa using p.length_eq
@[deprecated Perm.size_eq (since := "2025-04-17")]
abbrev Perm.length_eq := @Perm.size_eq
theorem Perm.mem_iff {a : α} {xs ys : Array α} (p : xs ~ ys) : a xs a ys := by
rcases xs with xs
rcases ys with ys
@@ -108,7 +104,7 @@ grind_pattern Perm.append => xs ~ ys, as ~ bs, ys ++ bs
theorem Perm.push (x : α) {xs ys : Array α} (p : xs ~ ys) :
xs.push x ~ ys.push x := by
rw [push_eq_append_singleton]
rw [push_eq_append]
exact p.append .rfl
grind_pattern Perm.push => xs ~ ys, xs.push x

View File

@@ -7,5 +7,3 @@ module
prelude
public import Init.Data.Array.QSort.Basic
public section

View File

@@ -6,14 +6,10 @@ Authors: Kim Morrison
module
prelude
public import Init.Data.Array.Lemmas
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.OfFn
import all Init.Data.Array.OfFn
public import Init.Data.Array.MapIdx
public import Init.Data.Array.Zip
public import Init.Data.List.Nat.Range
public section

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
module
prelude
public import Init.GetElem
public import Init.Data.Array.Basic
import Init.Data.Array.GetLit
public import Init.Data.Slice.Basic

View File

@@ -7,7 +7,6 @@ Authors: David Thrane Christiansen
module
prelude
public import Init.Data.Array.Basic
public import Init.Data.Array.Subarray
import all Init.Data.Array.Subarray
public import Init.Omega

View File

@@ -6,10 +6,8 @@ Authors: Markus Himmel
module
prelude
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.Lemmas
public import Init.Data.List.Nat.TakeDrop
public section

View File

@@ -6,10 +6,8 @@ Authors: Kim Morrison
module
prelude
public import Init.Data.Array.Basic
import all Init.Data.Array.Basic
public import Init.Data.Array.TakeDrop
public import Init.Data.List.Zip
public section
@@ -168,9 +166,6 @@ theorem zipWith_eq_append_iff {f : α → β → γ} {as : Array α} {bs : Array
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
simp [ List.toArray_replicate]
@[deprecated zipWith_replicate (since := "2025-03-18")]
abbrev zipWith_mkArray := @zipWith_replicate
theorem map_uncurry_zip_eq_zipWith {f : α β γ} {as : Array α} {bs : Array β} :
map (Function.uncurry f) (as.zip bs) = zipWith f as bs := by
cases as
@@ -296,9 +291,6 @@ theorem zip_eq_append_iff {as : Array α} {bs : Array β} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
simp [ List.toArray_replicate]
@[deprecated zip_replicate (since := "2025-03-18")]
abbrev zip_mkArray := @zip_replicate
theorem zip_eq_zip_take_min {as : Array α} {bs : Array β} :
zip as bs = zip (as.take (min as.size bs.size)) (bs.take (min as.size bs.size)) := by
cases as
@@ -350,9 +342,6 @@ theorem map_zipWithAll {δ : Type _} {f : α → β} {g : Option γ → Option
zipWithAll f (replicate n a) (replicate n b) = replicate n (f (some a) (some b)) := by
simp [ List.toArray_replicate]
@[deprecated zipWithAll_replicate (since := "2025-03-18")]
abbrev zipWithAll_mkArray := @zipWithAll_replicate
/-! ### zipWithM -/
@[simp, grind =]
@@ -410,7 +399,4 @@ theorem zip_of_prod {as : Array α} {bs : Array β} {xs : Array (α × β)} (hl
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
ext1 <;> simp
@[deprecated unzip_replicate (since := "2025-03-18")]
abbrev unzip_mkArray := @unzip_replicate
end Array

View File

@@ -1,20 +0,0 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Data.Nat.Basic
public import Init.Data.Fin.Basic
public import Init.Data.List.Basic
public import Init.Data.Char.Basic
public import Init.Data.String.Basic
public import Init.Data.Option.Basic
public import Init.Data.UInt
public import Init.Data.Repr
public import Init.Data.ToString.Basic
public import Init.Data.String.Extra
public section

View File

@@ -13,5 +13,3 @@ public import Init.Data.BitVec.Bitblast
public import Init.Data.BitVec.Decidable
public import Init.Data.BitVec.Lemmas
public import Init.Data.BitVec.Folds
public section

View File

@@ -6,11 +6,8 @@ Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex
module
prelude
public import Init.Data.Fin.Basic
public import Init.Data.Nat.Bitwise.Lemmas
public import Init.Data.Nat.Power2
public import Init.Data.Int.Bitwise.Basic
public import Init.Data.BitVec.BasicAux
@[expose] public section
@@ -29,10 +26,6 @@ set_option linter.missingDocs true
namespace BitVec
@[inline, deprecated BitVec.ofNatLT (since := "2025-02-13"), inherit_doc BitVec.ofNatLT]
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2 ^ n) : BitVec n :=
BitVec.ofNatLT i p
section Nat
/--
@@ -210,8 +203,8 @@ If `n` is `0`, then one digit is returned. Otherwise, `⌊(n + 3) / 4⌋` digits
-- `Internal` string functions by moving this definition out to a separate file that can live
-- downstream of `Init.Data.String.Basic`.
protected def toHex {n : Nat} (x : BitVec n) : String :=
let s := (Nat.toDigits 16 x.toNat).asString
let t := (List.replicate ((n+3) / 4 - String.Internal.length s) '0').asString
let s := String.ofList (Nat.toDigits 16 x.toNat)
let t := String.ofList (List.replicate ((n+3) / 4 - String.Internal.length s) '0')
String.Internal.append t s
/-- `BitVec` representation. -/
@@ -874,4 +867,7 @@ def clzAuxRec {w : Nat} (x : BitVec w) (n : Nat) : BitVec w :=
/-- Count the number of leading zeros. -/
def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
/-- Count the number of trailing zeros. -/
def ctz (x : BitVec w) : BitVec w := (x.reverse).clz
end BitVec

View File

@@ -6,17 +6,13 @@ Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix, Siddharth Bhat
module
prelude
public import Init.Data.Nat.Bitwise.Basic
import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.Mod
public import Init.Data.Int.DivMod
import all Init.Data.Int.DivMod
public import Init.Data.Int.LemmasAux
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Decidable
public import Init.Data.BitVec.Lemmas
public import Init.Data.BitVec.Folds
import Init.BinderPredicates
@[expose] public section
@@ -639,12 +635,11 @@ theorem mulRec_eq_mul_signExtend_setWidth (x y : BitVec w) (s : Nat) :
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
by_cases y.getLsbD 0
case pos hy =>
simp only [hy, reduceIte, setWidth_one_eq_ofBool_getLsb_zero,
ofBool_true, ofNat_eq_ofNat]
simp only [hy, reduceIte, setWidth_one, ofBool_true, ofNat_eq_ofNat]
rw [setWidth_ofNat_one_eq_ofNat_one_of_lt (by omega)]
simp
case neg hy =>
simp [hy, setWidth_one_eq_ofBool_getLsb_zero]
simp [hy, setWidth_one]
case succ s' hs =>
rw [mulRec_succ_eq, hs]
have heq :
@@ -1029,7 +1024,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
case neg.hrWidth =>
simp only
have hdr' : d (qr.r.shiftConcat (n.getLsbD (qr.wn - 1))) :=
BitVec.not_lt_iff_le.mp rltd
BitVec.not_lt.mp rltd
have hr' : ((qr.r.shiftConcat (n.getLsbD (qr.wn - 1)))).toNat < 2 ^ (qr.wr + 1) := by
apply toNat_shiftConcat_lt_of_lt <;> bv_omega
rw [BitVec.toNat_sub_of_le hdr']
@@ -1037,7 +1032,7 @@ theorem lawful_divSubtractShift (qr : DivModState w) (h : qr.Poised args) :
case neg.hqWidth =>
apply toNat_shiftConcat_lt_of_lt <;> omega
case neg.hdiv =>
have rltd' := (BitVec.not_lt_iff_le.mp rltd)
have rltd' := (BitVec.not_lt.mp rltd)
simp only [qr.toNat_shiftRight_sub_one_eq h,
BitVec.toNat_sub_of_le rltd',
toNat_shiftConcat_eq_of_lt (qr.wr_lt_w h) h.hrWidth]
@@ -1411,7 +1406,7 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
have := congrArg g h'
simpa [h] using this
@[simp]
@[deprecated BitVec.ne_intMin_of_msb_eq_false (since := "2025-10-26")]
theorem ne_intMin_of_lt_of_msb_false {x : BitVec w} (hw : 0 < w) (hx : x.msb = false) :
x intMin w := by
have := toNat_lt_of_msb_false hx
@@ -1516,7 +1511,7 @@ theorem sdiv_ne_intMin_of_ne_intMin {x y : BitVec w} (h : x ≠ intMin w) :
by_cases hx : x.msb <;> by_cases hy : y.msb
<;> simp only [hx, hy, neg_ne_intMin_inj]
<;> simp only [Bool.not_eq_true] at hx hy
<;> apply ne_intMin_of_lt_of_msb_false (by omega)
<;> apply ne_intMin_of_msb_eq_false (by omega)
<;> rw [msb_udiv]
<;> try simp only [hx, Bool.false_and]
· simp [h, ne_zero_of_msb_true, hx]
@@ -1628,7 +1623,7 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
· have ry := (intMin_udiv_eq_intMin_iff b).mp
simp only [hb1, imp_false] at ry
simp [msb_udiv, ha_intMin, hb1, ry, intMin_udiv_ne_zero_of_ne_zero, hb, hb0]
· have := @BitVec.ne_intMin_of_lt_of_msb_false w ((-a) / b) wpos (by simp [ha, ha0, ha_intMin])
· have := @BitVec.ne_intMin_of_msb_eq_false w wpos ((-a) / b) (by simp [ha, ha0, ha_intMin])
simp [msb_neg, h', this, ha, ha_intMin]
rw [toInt_eq_toNat_of_msb hb, toInt_eq_neg_toNat_neg_of_msb_true ha, Int.neg_tdiv,
Int.tdiv_eq_ediv_of_nonneg (by omega), sdiv_toInt_of_msb_true_of_msb_false]
@@ -1639,7 +1634,7 @@ theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1
rw [toInt_udiv_of_msb ha, toInt_eq_toNat_of_msb ha]
rw [toInt_eq_neg_toNat_neg_of_msb_true hb, Int.tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
· apply sdiv_ne_intMin_of_ne_intMin
apply ne_intMin_of_lt_of_msb_false (by omega) ha
apply ne_intMin_of_msb_eq_false (by omega) ha
· rw [sdiv, Int.tdiv_cases, udiv_eq, neg_eq, if_pos (toInt_nonneg_of_msb_false ha),
if_pos (toInt_nonneg_of_msb_false hb), ha, hb, toInt_udiv_of_msb ha,
toInt_eq_toNat_of_msb ha, toInt_eq_toNat_of_msb hb]
@@ -1931,7 +1926,7 @@ theorem toInt_sub_neg_umod {x y : BitVec w} (hxmsb : x.msb = true) (hymsb : y.ms
rw [Int.bmod_eq_of_le (by omega) (by omega)]
simp only [toInt_eq_toNat_of_msb hymsb, BitVec.toInt_eq_neg_toNat_neg_of_msb_true hxmsb,
Int.dvd_neg] at hdvd
simp only [hdvd, reduceIte, Int.natAbs_cast]
simp only [hdvd, reduceIte, Int.natAbs_natCast]
theorem srem_zero_of_dvd {x y : BitVec w} (h : y.toInt x.toInt) :
x.srem y = 0#w := by

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
import Init.Data.Int.Bitwise.Lemmas
import Init.Ext
public section

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.BitVec.Bootstrap
import Init.Ext
public section
@@ -49,11 +50,11 @@ instance instDecidableForallBitVecSucc (P : BitVec (n+1) → Prop) [DecidablePre
instance instDecidableExistsBitVecZero (P : BitVec 0 Prop) [Decidable (P 0#0)] :
Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
decidable_of_iff (¬ v, ¬ P v) (by exact Classical.not_forall_not)
instance instDecidableExistsBitVecSucc (P : BitVec (n+1) Prop) [DecidablePred P]
[Decidable ( (x : Bool) (v : BitVec n), ¬ P (v.cons x))] : Decidable ( v, P v) :=
decidable_of_iff (¬ v, ¬ P v) Classical.not_forall_not
decidable_of_iff (¬ v, ¬ P v) (by exact Classical.not_forall_not)
/--
For small numerals this isn't necessary (as typeclass search can use the above two instances),

View File

@@ -6,10 +6,8 @@ Authors: Joe Hendrix, Harun Khan
module
prelude
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.Lemmas
public import Init.Data.Nat.Lemmas
public import Init.Data.Fin.Iterate
public section
@@ -84,7 +82,7 @@ theorem iunfoldr_getLsbD' {f : Fin w → αα × Bool} (state : Nat → α)
intro i
simp only [getLsbD_cons]
have hj2 : j.val w := by simp
cases (Nat.lt_or_eq_of_le (Nat.lt_succ.mp i.isLt)) with
cases (Nat.lt_or_eq_of_le (Nat.lt_succ_iff.mp i.isLt)) with
| inl h3 => simp [(Nat.ne_of_lt h3)]
exact (ih hj2).1 i.val, h3
| inr h3 => simp [h3]

View File

@@ -6,22 +6,14 @@ Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed, Siddharth B
module
prelude
public import Init.Data.Bool
public import Init.Data.BitVec.Basic
import all Init.Data.BitVec.Basic
public import Init.Data.BitVec.BasicAux
import all Init.Data.BitVec.BasicAux
public import Init.Data.Fin.Lemmas
public import Init.Data.Nat.Lemmas
public import Init.Data.Nat.Div.Lemmas
public import Init.Data.Nat.Mod
public import Init.Data.Nat.Div.Lemmas
public import Init.Data.Int.Bitwise.Lemmas
public import Init.Data.Int.LemmasAux
public import Init.Data.Int.Pow
public import Init.Data.Int.LemmasAux
public import Init.Data.BitVec.Bootstrap
public import Init.Data.Order.Factories
public import Init.Data.List.BasicAux
import Init.Data.List.Lemmas
public section
@@ -42,14 +34,6 @@ namespace BitVec
simp only [Bool.and_eq_false_imp, decide_eq_true_eq]
omega
set_option linter.missingDocs false in
@[deprecated getLsbD_of_ge (since := "2025-04-04")]
abbrev getLsbD_ge := @getLsbD_of_ge
set_option linter.missingDocs false in
@[deprecated getMsbD_of_ge (since := "2025-04-04")]
abbrev getMsbD_ge := @getMsbD_of_ge
theorem lt_of_getLsbD {x : BitVec w} {i : Nat} : getLsbD x i = true i < w := by
if h : i < w then
simp [h]
@@ -74,16 +58,13 @@ theorem some_eq_getElem?_iff {l : BitVec w} : some a = l[n]? ↔ ∃ h : n < w,
theorem getElem_of_getElem? {l : BitVec w} : l[n]? = some a h : n < w, l[n] = a :=
getElem?_eq_some_iff.mp
set_option linter.missingDocs false in
@[deprecated getElem?_eq_some_iff (since := "2025-02-17")]
abbrev getElem?_eq_some := @getElem?_eq_some_iff
theorem getElem?_eq_none_iff {l : BitVec w} : l[n]? = none w n := by
simp
theorem none_eq_getElem?_iff {l : BitVec w} : none = l[n]? w n := by
simp
@[simp]
theorem getElem?_eq_none {l : BitVec w} (h : w n) : l[n]? = none := getElem?_eq_none_iff.mpr h
theorem getElem?_eq (l : BitVec w) (i : Nat) :
@@ -152,9 +133,6 @@ theorem two_pow_le_toNat_of_getElem_eq_true {i : Nat} {x : BitVec w}
rw [ getElem_eq_testBit_toNat x i hi]
exact hx
@[grind =] theorem msb_eq_getMsbD (x : BitVec w) : x.msb = x.getMsbD 0 := by
simp [BitVec.msb]
@[grind =] theorem getMsb_eq_getLsb (x : BitVec w) (i : Fin w) :
x.getMsb i = x.getLsb w - 1 - i, by omega := by
simp only [getMsb, getLsb]
@@ -181,18 +159,13 @@ theorem getLsbD_eq_getMsbD (x : BitVec w) (i : Nat) : x.getLsbD i = (decide (i <
apply getLsbD_of_ge
omega
@[simp] theorem getElem?_of_ge (x : BitVec w) (i : Nat) (ge : w i) : x[i]? = none := by
@[deprecated getElem?_eq_none (since := "2025-10-29")]
theorem getElem?_of_ge (x : BitVec w) (i : Nat) (ge : w i) : x[i]? = none := by
simp [ge]
@[simp] theorem getMsb?_of_ge (x : BitVec w) (i : Nat) (ge : w i) : getMsb? x i = none := by
simp [getMsb?_eq_getLsb?]; omega
set_option linter.missingDocs false in
@[deprecated getElem?_of_ge (since := "2025-04-04")] abbrev getLsb?_ge := @getElem?_of_ge
set_option linter.missingDocs false in
@[deprecated getMsb?_of_ge (since := "2025-04-04")] abbrev getMsb?_ge := @getMsb?_of_ge
theorem lt_of_getElem?_eq_some (x : BitVec w) (i : Nat) : x[i]? = some b i < w := by
cases h : x[i]? with
| none => simp
@@ -215,18 +188,6 @@ theorem lt_of_isSome_getMsb? (x : BitVec w) (i : Nat) : (getMsb? x i).isSome →
else
simp [Nat.ge_of_not_lt h]
set_option linter.missingDocs false in
@[deprecated lt_of_getElem?_eq_some (since := "2025-04-04")]
abbrev lt_of_getLsb?_eq_some := @lt_of_getElem?_eq_some
set_option linter.missingDocs false in
@[deprecated lt_of_isSome_getElem? (since := "2025-04-04")]
abbrev lt_of_getLsb?_isSome := @lt_of_isSome_getElem?
set_option linter.missingDocs false in
@[deprecated lt_of_isSome_getMsb? (since := "2025-04-04")]
abbrev lt_of_getMsb?_isSome := @lt_of_isSome_getMsb?
theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
x.getMsbD i = (x.getMsb? i).getD false := by
rw [getMsbD_eq_getLsbD]
@@ -350,25 +311,14 @@ theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b'
@[simp] theorem ofBool_xor_ofBool : ofBool b ^^^ ofBool b' = ofBool (b ^^ b') := by
cases b <;> cases b' <;> rfl
@[deprecated toNat_ofNatLT (since := "2025-02-13")]
theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp, grind =] theorem getLsbD_ofNatLT {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
getLsbD (x#'lt) i = x.testBit i := by
simp [getLsbD, BitVec.ofNatLT]
@[deprecated getLsbD_ofNatLT (since := "2025-02-13")]
theorem getLsbD_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
getLsbD (x#'lt) i = x.testBit i := getLsbD_ofNatLT x lt i
@[simp, grind =] theorem getMsbD_ofNatLT {n x i : Nat} (h : x < 2^n) :
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := by
simp [getMsbD, getLsbD]
@[deprecated getMsbD_ofNatLT (since := "2025-02-13")]
theorem getMsbD_ofNatLt {n x i : Nat} (h : x < 2^n) :
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := getMsbD_ofNatLT h
@[grind =]
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
@@ -467,12 +417,18 @@ theorem getElem?_zero_ofNat_one : (BitVec.ofNat (w+1) 1)[0]? = some true := by
-- This does not need to be a `@[simp]` theorem as it is already handled by `getElem?_eq_getElem`.
theorem getElem?_zero_ofBool (b : Bool) : (ofBool b)[0]? = some b := by
simp only [ofBool, ofNat_eq_ofNat, cond_eq_if]
simp only [ofBool, ofNat_eq_ofNat, cond_eq_ite]
split <;> simp_all
@[simp, grind =] theorem getElem_zero_ofBool (b : Bool) : (ofBool b)[0] = b := by
@[simp, grind =]
theorem getElem_ofBool_zero {b : Bool} : (ofBool b)[0] = b := by
rw [getElem_eq_iff, getElem?_zero_ofBool]
@[deprecated getElem_ofBool_zero (since := "2025-10-29")]
theorem getElem_zero_ofBool (b : Bool) : (ofBool b)[0] = b := by
simp
theorem getElem?_succ_ofBool (b : Bool) (i : Nat) : (ofBool b)[i + 1]? = none := by
simp
@@ -483,8 +439,6 @@ theorem getLsbD_ofBool (b : Bool) (i : Nat) : (ofBool b).getLsbD i = ((i = 0) &&
· simp only [ofBool, ofNat_eq_ofNat, cond_true, getLsbD_ofNat, Bool.and_true]
by_cases hi : i = 0 <;> simp [hi] <;> omega
theorem getElem_ofBool_zero {b : Bool} : (ofBool b)[0] = b := by simp
@[simp]
theorem getElem_ofBool {b : Bool} {h : i < 1}: (ofBool b)[i] = b := by
simp [ getLsbD_eq_getElem]
@@ -567,6 +521,10 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
@[grind _=_] theorem msb_eq_getMsbD_zero (x : BitVec w) : x.msb = x.getMsbD 0 := by
cases w <;> simp [getMsbD_eq_getLsbD, msb_eq_getLsbD_last]
@[deprecated msb_eq_getMsbD_zero (since := "2025-10-26")]
theorem msb_eq_getMsbD (x : BitVec w) : x.msb = x.getMsbD 0 := by
simp [BitVec.msb]
/-! ### cast -/
@[simp, grind =] theorem toFin_cast (h : w = v) (x : BitVec w) :
@@ -628,7 +586,7 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
simp only [toInt_eq_toNat_cond]
split
next g =>
rw [Int.bmod_pos] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
rw [Int.bmod_eq_emod_of_lt] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
omega
next g =>
rw [Int.bmod_neg] <;> simp only [Int.natCast_emod, toNat_mod_cancel]
@@ -1036,7 +994,14 @@ theorem msb_setWidth' (x : BitVec w) (h : w ≤ v) : (x.setWidth' h).msb = (deci
theorem msb_setWidth'' (x : BitVec w) : (x.setWidth (k + 1)).msb = x.getLsbD k := by
simp [BitVec.msb, getMsbD]
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem setWidth_one {x : BitVec w} :
x.setWidth 1 = ofBool (x.getLsbD 0) := by
ext i
simp [show i = 0 by omega]
/-- zero extending a bitvector to width 1 equals the boolean of the lsb. -/
@[deprecated setWidth_one (since := "2025-10-29")]
theorem setWidth_one_eq_ofBool_getLsb_zero (x : BitVec w) :
x.setWidth 1 = BitVec.ofBool (x.getLsbD 0) := by
ext i h
@@ -1052,12 +1017,6 @@ theorem setWidth_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
have hv := (@Nat.testBit_one_eq_true_iff_self_eq_zero i)
by_cases h : Nat.testBit 1 i = true <;> simp_all
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
theorem setWidth_one {x : BitVec w} :
x.setWidth 1 = ofBool (x.getLsbD 0) := by
ext i
simp [show i = 0 by omega]
@[simp, grind =] theorem setWidth_ofNat_of_le (h : v w) (x : Nat) : setWidth v (BitVec.ofNat w x) = BitVec.ofNat v x := by
apply BitVec.eq_of_toNat_eq
simp only [toNat_setWidth, toNat_ofNat]
@@ -1100,6 +1059,10 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w w') : (b.setWidth w').toNat = b.toNat := by
rw [BitVec.toNat_setWidth, Nat.mod_eq_of_lt]
exact BitVec.toNat_lt_twoPow_of_le h
/-! ## extractLsb -/
@[simp, grind =]
@@ -1227,7 +1190,7 @@ let x' = x.extractLsb' 7 5 = _ _ 9 8 7
@[simp] theorem getLsbD_extract (hi lo : Nat) (x : BitVec n) (i : Nat) :
getLsbD (extractLsb hi lo x) i = (i (hi-lo) && getLsbD x (lo+i)) := by
simp [getLsbD, Nat.lt_succ]
simp [getLsbD, Nat.lt_succ_iff]
@[simp] theorem getLsbD_extractLsb {hi lo : Nat} {x : BitVec n} {i : Nat} :
(extractLsb hi lo x).getLsbD i = (decide (i < hi - lo + 1) && x.getLsbD (lo + i)) := by
@@ -1287,6 +1250,17 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
ext i hi
omega
theorem extractLsb'_setWidth_of_le {b : BitVec w} {start len w' : Nat} (h : start + len w') :
(b.setWidth w').extractLsb' start len = b.extractLsb' start len := by
ext i h_i
simp
omega
theorem setWidth_extractLsb'_of_le {c : BitVec w} (h : len₁ len₂) :
(c.extractLsb' start len₂).setWidth len₁ = c.extractLsb' start len₁ := by
ext i hi
simp [show i < len₂ by omega]
/-! ### allOnes -/
@[simp, grind =] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -1530,6 +1504,12 @@ theorem extractLsb_and {x : BitVec w} {hi lo : Nat} :
@[simp, grind =] theorem ofNat_and {x y : Nat} : BitVec.ofNat w (x &&& y) = BitVec.ofNat w x &&& BitVec.ofNat w y :=
eq_of_toNat_eq (by simp [Nat.and_mod_two_pow])
theorem and_or_distrib_left {x y z : BitVec w} : x &&& (y ||| z) = (x &&& y) ||| (x &&& z) :=
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_left])
theorem and_or_distrib_right {x y z : BitVec w} : (x ||| y) &&& z = (x &&& z) ||| (y &&& z) :=
BitVec.eq_of_getElem_eq (by simp [Bool.and_or_distrib_right])
/-! ### xor -/
@[simp, grind =] theorem toNat_xor (x y : BitVec v) :
@@ -1666,11 +1646,11 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem ofInt_negSucc_eq_not_ofNat {w n : Nat} :
BitVec.ofInt w (Int.negSucc n) = ~~~.ofNat w n := by
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_coe, toNat_eq, toNat_ofNatLT, toNat_not,
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_natCast, toNat_eq, toNat_ofNatLT, toNat_not,
toNat_ofNat]
cases h : Int.negSucc n % ((2 ^ w : Nat) : Int)
case ofNat =>
rw [Int.ofNat_eq_coe, Int.negSucc_emod] at h
rw [Int.ofNat_eq_natCast, Int.negSucc_emod] at h
· dsimp only
omega
· omega
@@ -1752,9 +1732,6 @@ theorem not_eq_comm {x y : BitVec w} : ~~~ x = y ↔ x = ~~~ y := by
rw [h]
simp
set_option linter.missingDocs false in
@[deprecated getMsbD_not (since := "2025-04-04")] abbrev getMsb_not := @getMsbD_not
@[simp] theorem msb_not {x : BitVec w} : (~~~x).msb = (decide (0 < w) && !x.msb) := by
simp [BitVec.msb]
@@ -2180,6 +2157,10 @@ theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
have := lt_of_getLsbD ha
omega
theorem setWidth_ushiftRight_eq_extractLsb {b : BitVec w} : (b >>> w').setWidth w'' = b.extractLsb' w' w'' := by
ext i hi
simp
/-! ### ushiftRight reductions from BitVec to Nat -/
@[simp, grind =]
@@ -2570,10 +2551,6 @@ theorem signExtend_eq_setWidth_of_le (x : BitVec w) {v : Nat} (hv : v ≤ w) :
ext i h
simp [getElem_signExtend, show i < w by omega]
@[deprecated signExtend_eq_setWidth_of_le (since := "2025-03-07")]
theorem signExtend_eq_setWidth_of_lt (x : BitVec w) {v : Nat} (hv : v w) :
x.signExtend v = x.setWidth v := signExtend_eq_setWidth_of_le x hv
/-- Sign extending to the same bitwidth is a no op. -/
@[simp] theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by
rw [signExtend_eq_setWidth_of_le _ (Nat.le_refl _), setWidth_eq]
@@ -2970,10 +2947,9 @@ theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
(x.extractLsb' start₁ (len + len)).cast (by omega) := by
x.extractLsb' start₁ (len + len) := by
ext i h
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, getElem_cast, ite_eq_left_iff,
Nat.not_lt]
simp only [getElem_append, getElem_extractLsb', dite_eq_ite, ite_eq_left_iff, Nat.not_lt]
intro hi
congr 1
omega
@@ -3085,6 +3061,51 @@ theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by
simp [extractLsb'_append_eq_ite, show ¬ start < w by omega]
theorem extractLsb'_append_eq_left {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' w' w = a := by
simp [BitVec.extractLsb'_append_eq_of_le]
theorem extractLsb'_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).extractLsb' 0 w' = b := by
simp [BitVec.extractLsb'_append_eq_of_add_le]
theorem setWidth_append_eq_right {a : BitVec w} {b : BitVec w'} : (a ++ b).setWidth w' = b := by
ext i hi
simp [getLsbD_append, hi]
theorem append_left_inj {s₁ s₂ : BitVec w} (t : BitVec w') : s₁ ++ t = s₂ ++ t s₁ = s₂ := by
refine fun h => ?_, fun h => h rfl
ext i hi
simpa [getElem_append, dif_neg] using congrArg (·[i + w']'(by omega)) h
theorem append_right_inj (s : BitVec w) {t₁ t₂ : BitVec w'} : s ++ t₁ = s ++ t₂ t₁ = t₂ := by
refine fun h => ?_, fun h => h rfl
ext i hi
simpa [getElem_append, hi] using congrArg (·[i]) h
theorem setWidth_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} :
(b ++ b').setWidth w'' = (b.setWidth w'' <<< w') ||| b'.setWidth w'' := by
ext i hi
simp only [getElem_setWidth, getElem_or, getElem_shiftLeft]
rw [getLsbD_append]
split <;> simp_all
theorem setWidth_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} :
(b ++ b' ++ b'').setWidth w''' = (b.setWidth w''' <<< (w' + w'')) ||| (b'.setWidth w''' <<< w'') ||| b''.setWidth w''' := by
rw [BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
BitVec.setWidth_append_eq_shiftLeft_setWidth_or,
BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
theorem setWidth_append_append_append_eq_shiftLeft_setWidth_or {b : BitVec w} {b' : BitVec w'} {b'' : BitVec w''} {b''' : BitVec w'''} :
(b ++ b' ++ b'' ++ b''').setWidth w'''' = (b.setWidth w'''' <<< (w' + w'' + w''')) ||| (b'.setWidth w'''' <<< (w'' + w''')) |||
(b''.setWidth w'''' <<< w''') ||| b'''.setWidth w'''' := by
simp only [BitVec.setWidth_append_eq_shiftLeft_setWidth_or, BitVec.shiftLeft_or_distrib, BitVec.shiftLeft_add]
theorem and_setWidth_allOnes (w' w : Nat) (b : BitVec (w' + w)) :
b &&& (BitVec.allOnes w).setWidth (w' + w) = 0#w' ++ b.setWidth w := by
ext i hi
simp only [getElem_and, getElem_setWidth, getLsbD_allOnes]
rw [BitVec.getElem_append]
split <;> simp_all
/-! ### rev -/
@[grind =]
@@ -3589,9 +3610,6 @@ theorem sub_eq_add_neg {n} (x y : BitVec n) : x - y = x + - y := by
simp only [toNat_sub, toNat_add, toNat_neg, Nat.add_mod_mod]
rw [Nat.add_comm]
set_option linter.missingDocs false in
@[deprecated sub_eq_add_neg (since := "2025-04-04")] abbrev sub_toAdd := @sub_eq_add_neg
theorem add_left_neg (x : BitVec w) : -x + x = 0#w := by
apply toInt_inj.mp
simp [toInt_neg, Int.add_left_neg]
@@ -3631,10 +3649,6 @@ theorem neg_one_eq_allOnes : -1#w = allOnes w := by
have r : (2^w - 1) < 2^w := by omega
simp [Nat.mod_eq_of_lt q, Nat.mod_eq_of_lt r]
set_option linter.missingDocs false in
@[deprecated neg_one_eq_allOnes (since := "2025-04-04")]
abbrev negOne_eq_allOnes := @neg_one_eq_allOnes
theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1#w := by
apply eq_of_toNat_eq
simp only [toNat_neg, toNat_add, toNat_not, toNat_ofNat, Nat.add_mod_mod]
@@ -3696,6 +3710,9 @@ theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
apply eq_of_toInt_eq
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
theorem sub_sub (a b c : BitVec n) : a - b - c = a - (b + c) := by
simp [BitVec.sub_eq_add_neg, BitVec.add_assoc, BitVec.neg_add]
theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
apply eq_of_toInt_eq
simp [toInt_neg, Int.sub_eq_add_neg]
@@ -4041,10 +4058,14 @@ instance instLawfulOrderLT : LawfulOrderLT (BitVec n) := by
apply LawfulOrderLT.of_le
simpa using fun _ _ => BitVec.lt_asymm
theorem length_pos_of_lt {b b' : BitVec w} (h : b < b') : 0 < w :=
length_pos_of_ne (BitVec.ne_of_lt h)
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y x % y < y := by
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
apply Nat.mod_lt
@[deprecated BitVec.not_lt (since := "2025-10-26")]
theorem not_lt_iff_le {x y : BitVec w} : (¬ x < y) y x := by
constructor <;>
(intro h; simp only [lt_def, Nat.not_lt, le_def] at h ; omega)
@@ -4061,7 +4082,7 @@ theorem not_lt_zero {x : BitVec w} : ¬x < 0#w := of_decide_eq_false rfl
theorem le_zero_iff {x : BitVec w} : x 0#w x = 0#w := by
constructor
· intro h
have : x 0 := not_lt_iff_le.mp not_lt_zero
have : x 0 := BitVec.not_lt.mp not_lt_zero
exact Eq.symm (BitVec.le_antisymm this h)
· simp_all
@@ -4084,7 +4105,7 @@ theorem not_allOnes_lt {x : BitVec w} : ¬allOnes w < x := by
theorem allOnes_le_iff {x : BitVec w} : allOnes w x x = allOnes w := by
constructor
· intro h
have : x allOnes w := not_lt_iff_le.mp not_allOnes_lt
have : x allOnes w := BitVec.not_lt.mp not_allOnes_lt
exact Eq.symm (BitVec.le_antisymm h this)
· simp_all
@@ -4112,6 +4133,14 @@ theorem lt_of_msb_false_of_msb_true {x y : BitVec w} (hx : x.msb = false) (hy :
simp
omega
theorem lt_add_one {b : BitVec w} (h : b allOnes w) : b < b + 1 := by
simp only [ne_eq, toNat_inj, toNat_allOnes] at h
simp only [BitVec.lt_def, ofNat_eq_ofNat, toNat_add, toNat_ofNat, Nat.add_mod_mod]
rw [Nat.mod_eq_of_lt]
· exact Nat.lt_add_one _
· have := b.toNat_lt_twoPow_of_le (Nat.le_refl _)
omega
/-! ### udiv -/
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
@@ -4622,9 +4651,6 @@ theorem zero_smod {x : BitVec w} : (0#w).smod x = 0#w := by
@[simp, grind =] theorem getLsbD_ofBoolListLE : (ofBoolListLE bs).getLsbD i = bs.getD i false := by
induction bs generalizing i <;> cases i <;> simp_all [ofBoolListLE]
set_option linter.missingDocs false in
@[deprecated getLsbD_ofBoolListLE (since := "2025-04-04")] abbrev getLsb_ofBoolListLE := @getLsbD_ofBoolListLE
@[simp, grind =] theorem getMsbD_ofBoolListLE :
(ofBoolListLE bs).getMsbD i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false) := by
simp [getMsbD_eq_getLsbD]
@@ -4695,14 +4721,6 @@ theorem getLsbD_rotateLeftAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i
apply getLsbD_of_ge
omega
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateLeftAux_of_lt (since := "2025-04-04")]
abbrev getLsbD_rotateLeftAux_of_le := @getLsbD_rotateLeftAux_of_lt
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateLeftAux_of_ge (since := "2025-04-04")]
abbrev getLsbD_rotateLeftAux_of_geq := @getLsbD_rotateLeftAux_of_ge
/-- When `r < w`, we give a formula for `(x.rotateLeft r).getLsbD i`. -/
theorem getLsbD_rotateLeft_of_le {x : BitVec w} {r i : Nat} (hr: r < w) :
(x.rotateLeft r).getLsbD i =
@@ -4859,14 +4877,6 @@ theorem getLsbD_rotateRightAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i
apply getLsbD_of_ge
omega
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateRightAux_of_lt (since := "2025-04-04")]
abbrev getLsbD_rotateRightAux_of_le := @getLsbD_rotateRightAux_of_lt
set_option linter.missingDocs false in
@[deprecated getLsbD_rotateRightAux_of_ge (since := "2025-04-04")]
abbrev getLsbD_rotateRightAux_of_geq := @getLsbD_rotateRightAux_of_ge
/-- `rotateRight` equals the bit fiddling definition of `rotateRightAux` when the rotation amount is
smaller than the bitwidth. -/
theorem rotateRight_eq_rotateRightAux_of_lt {x : BitVec w} {r : Nat} (hr : r < w) :
@@ -5257,7 +5267,7 @@ theorem replicate_succ' {x : BitVec w} :
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
simp [replicate_append_self]
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
theorem setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
apply BitVec.eq_of_toNat_eq
rw [toNat_setWidth]
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
@@ -5266,6 +5276,14 @@ theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y)
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
theorem setWidth_setWidth_eq_self {a : BitVec w} {w' : Nat} (h : a < BitVec.twoPow w w') : (a.setWidth w').setWidth w = a := by
by_cases hw : w' < w
· simp only [toNat_eq, toNat_setWidth]
rw [Nat.mod_mod_of_dvd' (Nat.pow_dvd_pow _ (Nat.le_of_lt hw)), Nat.mod_eq_of_lt]
rwa [BitVec.lt_def, BitVec.toNat_twoPow_of_lt hw] at h
· rw [BitVec.lt_def, BitVec.toNat_twoPow_of_le (by omega)] at h
simp at h
/-! ### intMin -/
@[grind =]
@@ -5779,6 +5797,25 @@ theorem msb_replicate {n w : Nat} {x : BitVec w} :
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
cases n <;> cases w <;> simp
@[simp]
theorem reverse_eq_zero_iff {x : BitVec w} :
x.reverse = 0#w x = 0#w := by
constructor
· intro hrev
ext i hi
rw [ getLsbD_eq_getElem, getLsbD_eq_getMsbD, getLsbD_reverse]
simp [hrev]
· intro hzero
ext i hi
rw [ getLsbD_eq_getElem, getLsbD_eq_getMsbD, getMsbD_reverse]
simp [hi, hzero]
@[simp]
theorem reverse_reverse_eq {x : BitVec w} :
x.reverse.reverse = x := by
ext k hk
rw [getElem_reverse, getMsbD_reverse, getLsbD_eq_getElem]
/-! ### Inequalities (le / lt) -/
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
@@ -5909,6 +5946,12 @@ theorem getElem_eq_true_of_lt_of_le {x : BitVec w} (hk' : k < w) (hlt: x.toNat <
omega
· simp [show w k + k' by omega] at hk'
theorem not_lt_iff {b : BitVec w} : ~~~b < b 0 < w b.msb = true := by
refine fun h => ?_, fun hw, hb => ?_
· have := length_pos_of_lt h
exact this, by rwa [ ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)] at h
· rwa [ ult_iff_lt, ult_eq_msb_of_msb_neq (by simp_all)]
/-! ### Count leading zeros -/
theorem clzAuxRec_zero (x : BitVec w) :
@@ -6182,16 +6225,70 @@ theorem toNat_lt_two_pow_sub_clz {x : BitVec w} :
· simp [show w + 1 i by omega]
· simp; omega
theorem clz_eq_reverse_ctz {x : BitVec w} :
x.clz = (x.reverse).ctz := by
simp [ctz]
/-! ### Deprecations -/
/-! ### Count trailing zeros -/
set_option linter.missingDocs false
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
abbrev toFin_uShiftRight := @toFin_ushiftRight
theorem ctz_eq_reverse_clz {x : BitVec w} :
x.ctz = (x.reverse).clz := by
simp [ctz]
/-- The number of trailing zeroes is strictly less than the bitwidth iff the bitvector is nonzero. -/
@[simp]
theorem ctz_lt_iff_ne_zero {x : BitVec w} :
ctz x < w x 0#w := by
simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq]
rw [show BitVec.ofNat w w = w by simp, reverse_eq_zero_iff (x := x)]
apply clz_lt_iff_ne_zero (x := x.reverse)
/-- If a bitvec is different than zero the bits at indexes lower than `ctz x` are false. -/
theorem getLsbD_false_of_lt_ctz {x : BitVec w} (hi : i < x.ctz.toNat) :
x.getLsbD i = false := by
rw [getLsbD_eq_getMsbD, getLsbD_reverse]
have hiff := ctz_lt_iff_ne_zero (x := x)
by_cases hzero : x = 0#w
· simp [hzero, getLsbD_reverse]
· simp only [ctz_eq_reverse_clz, natCast_eq_ofNat, ne_eq, hzero, not_false_eq_true,
iff_true] at hiff
simp only [ctz] at hi
have hi' : i < w := by simp [BitVec.lt_def] at hiff; omega
simp only [hi', decide_true, Bool.true_and]
have : (x.reverse.clzAuxRec (w - 1)).toNat w := by
rw [show ((x.reverse.clzAuxRec (w - 1)).toNat w) =
((x.reverse.clzAuxRec (w - 1)).toNat (BitVec.ofNat w w).toNat) by simp, le_def]
apply clzAuxRec_le (x := x.reverse) (n := w - 1)
let j := (x.reverse.clzAuxRec (w - 1)).toNat - 1 - i
rw [show w - 1 - i = w - (x.reverse.clzAuxRec (w - 1)).toNat + j by
subst j
rw [Nat.sub_sub (n := (x.reverse.clzAuxRec (w - 1)).toNat),
Nat.add_sub_assoc (by exact Nat.one_add_le_iff.mpr hi)]
omega]
have hfalse : (i : Nat), w - 1 < i x.reverse.getLsbD i = false := by
intros i hj
simp [show w i by omega]
exact getLsbD_false_of_clzAuxRec (x := x.reverse) (n := w - 1) hfalse (j := j)
/-- If a bitvec is different than zero, the bit at index `ctz x`, i.e., the first bit after the
trailing zeros, is true. -/
theorem getLsbD_true_ctz_of_ne_zero {x : BitVec w} (hx : x 0#w) :
x.getLsbD (ctz x).toNat = true := by
simp only [ctz_eq_reverse_clz, clz]
rw [getLsbD_eq_getMsbD, getLsbD_reverse]
have := ctz_lt_iff_ne_zero (x := x)
simp only [ctz_eq_reverse_clz, clz, natCast_eq_ofNat, lt_def, toNat_ofNat, Nat.mod_two_pow_self,
ne_eq] at this
simp only [this, hx, not_false_eq_true, decide_true, Bool.true_and]
have hnotrev : ¬x.reverse = 0#w := by simp [reverse_eq_zero_iff, hx]
apply getLsbD_true_of_eq_clzAuxRec_of_ne_zero (x := x.reverse) (n := w - 1) hnotrev
intro i hi
simp [show w i by omega]
/-- A nonzero bitvector is lower-bounded by its trailing zeroes. -/
theorem two_pow_ctz_le_toNat_of_ne_zero {x : BitVec w} (hx : x 0#w) :
2 ^ (ctz x).toNat x.toNat := by
have hclz := getLsbD_true_ctz_of_ne_zero (x := x) hx
exact Nat.ge_two_pow_of_testBit hclz
end BitVec

View File

@@ -111,35 +111,11 @@ Needed for confluence of term `(a && b) ↔ a` which reduces to `(a && b) = a` v
@[simp] theorem eq_self_and : {a b : Bool}, (a = (a && b)) (a b) := by decide
@[simp] theorem eq_and_self : {a b : Bool}, (b = (a && b)) (b a) := by decide
@[deprecated and_eq_left_iff_imp (since := "2025-04-04")]
abbrev and_iff_left_iff_imp := @and_eq_left_iff_imp
@[deprecated and_eq_right_iff_imp (since := "2025-04-04")]
abbrev and_iff_right_iff_imp := @and_eq_right_iff_imp
@[deprecated eq_self_and (since := "2025-04-04")]
abbrev iff_self_and := @eq_self_and
@[deprecated eq_and_self (since := "2025-04-04")]
abbrev iff_and_self := @eq_and_self
@[simp] theorem not_and_eq_left_iff_and : {a b : Bool}, ((!a && b) = a) !a !b := by decide
@[simp] theorem and_not_eq_right_iff_and : {a b : Bool}, ((a && !b) = b) !a !b := by decide
@[simp] theorem eq_not_self_and : {a b : Bool}, (a = (!a && b)) !a !b := by decide
@[simp] theorem eq_and_not_self : {a b : Bool}, (b = (a && !b)) !a !b := by decide
@[deprecated not_and_eq_left_iff_and (since := "2025-04-04")]
abbrev not_and_iff_left_iff_imp := @not_and_eq_left_iff_and
@[deprecated and_not_eq_right_iff_and (since := "2025-04-04")]
abbrev and_not_iff_right_iff_imp := @and_not_eq_right_iff_and
@[deprecated eq_not_self_and (since := "2025-04-04")]
abbrev iff_not_self_and := @eq_not_self_and
@[deprecated eq_and_not_self (since := "2025-04-04")]
abbrev iff_and_not_self := @eq_and_not_self
/-! ### or -/
@[simp] theorem or_self_left : (a b : Bool), (a || (a || b)) = (a || b) := by decide
@@ -169,35 +145,11 @@ Needed for confluence of term `(a || b) ↔ a` which reduces to `(a || b) = a` v
@[simp] theorem eq_self_or : {a b : Bool}, (a = (a || b)) (b a) := by decide
@[simp] theorem eq_or_self : {a b : Bool}, (b = (a || b)) (a b) := by decide
@[deprecated or_eq_left_iff_imp (since := "2025-04-04")]
abbrev or_iff_left_iff_imp := @or_eq_left_iff_imp
@[deprecated or_eq_right_iff_imp (since := "2025-04-04")]
abbrev or_iff_right_iff_imp := @or_eq_right_iff_imp
@[deprecated eq_self_or (since := "2025-04-04")]
abbrev iff_self_or := @eq_self_or
@[deprecated eq_or_self (since := "2025-04-04")]
abbrev iff_or_self := @eq_or_self
@[simp] theorem not_or_eq_left_iff_and : {a b : Bool}, ((!a || b) = a) a b := by decide
@[simp] theorem or_not_eq_right_iff_and : {a b : Bool}, ((a || !b) = b) a b := by decide
@[simp] theorem eq_not_self_or : {a b : Bool}, (a = (!a || b)) a b := by decide
@[simp] theorem eq_or_not_self : {a b : Bool}, (b = (a || !b)) a b := by decide
@[deprecated not_or_eq_left_iff_and (since := "2025-04-04")]
abbrev not_or_iff_left_iff_imp := @not_or_eq_left_iff_and
@[deprecated or_not_eq_right_iff_and (since := "2025-04-04")]
abbrev or_not_iff_right_iff_imp := @or_not_eq_right_iff_and
@[deprecated eq_not_self_or (since := "2025-04-04")]
abbrev iff_not_self_or := @eq_not_self_or
@[deprecated eq_or_not_self (since := "2025-04-04")]
abbrev iff_or_not_self := @eq_or_not_self
theorem or_comm : (x y : Bool), (x || y) = (y || x) := by decide
instance : Std.Commutative (· || ·) := or_comm
@@ -562,6 +514,7 @@ theorem exists_bool {p : Bool → Prop} : (∃ b, p b) ↔ p false p true :=
theorem cond_eq_ite {α} (b : Bool) (t e : α) : cond b t e = if b then t else e := by
cases b <;> simp
@[deprecated cond_eq_ite (since := "2025-10-29")]
theorem cond_eq_if : (bif b then x else y) = (if b then x else y) := cond_eq_ite b x y
@[simp] theorem cond_not (b : Bool) (t e : α) : cond (!b) t e = cond b e t := by
@@ -621,11 +574,6 @@ protected theorem cond_false {α : Sort u} {a b : α} : cond false a b = b := co
@[simp] theorem cond_then_self : (c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_else_self : (c b : Bool), cond c b c = (c && b) := by decide
@[deprecated cond_then_not_self (since := "2025-04-04")] abbrev cond_true_not_same := @cond_then_not_self
@[deprecated cond_else_not_self (since := "2025-04-04")] abbrev cond_false_not_same := @cond_else_not_self
@[deprecated cond_then_self (since := "2025-04-04")] abbrev cond_true_same := @cond_then_self
@[deprecated cond_else_self (since := "2025-04-04")] abbrev cond_false_same := @cond_else_self
theorem cond_pos {b : Bool} {a a' : α} (h : b = true) : (bif b then a else a') = a := by
rw [h, cond_true]
@@ -665,7 +613,7 @@ theorem decide_beq_decide (p q : Prop) [dpq : Decidable (p ↔ q)] [dp : Decidab
end Bool
export Bool (cond_eq_if xor and or not)
export Bool (cond_eq_if cond_eq_ite xor and or not)
/-! ### decide -/

View File

@@ -7,6 +7,6 @@ module
prelude
public import Init.Data.ByteArray.Basic
public import Init.Data.ByteArray.Bootstrap
public import Init.Data.ByteArray.Extra
public section
public import Init.Data.ByteArray.Lemmas

View File

@@ -6,21 +6,15 @@ Author: Leonardo de Moura
module
prelude
public import Init.Data.Array.DecidableEq
public import Init.Data.UInt.Basic
public import Init.Data.UInt.BasicAux
import all Init.Data.UInt.BasicAux
public import Init.Data.Option.Basic
public import Init.Data.Array.Extract
set_option doc.verso true
@[expose] public section
universe u
structure ByteArray where
data : Array UInt8
attribute [extern "lean_byte_array_mk"] ByteArray.mk
attribute [extern "lean_byte_array_data"] ByteArray.data
namespace ByteArray
deriving instance BEq for ByteArray
@@ -30,41 +24,46 @@ attribute [ext] ByteArray
instance : DecidableEq ByteArray :=
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
@[extern "lean_mk_empty_byte_array"]
def emptyWithCapacity (c : @& Nat) : ByteArray :=
{ data := #[] }
@[deprecated emptyWithCapacity (since := "2025-03-12")]
abbrev mkEmpty := emptyWithCapacity
def empty : ByteArray := emptyWithCapacity 0
instance : Inhabited ByteArray where
default := empty
instance : EmptyCollection ByteArray where
emptyCollection := ByteArray.empty
@[extern "lean_byte_array_push"]
def push : ByteArray UInt8 ByteArray
| bs, b => bs.push b
@[extern "lean_byte_array_size"]
def size : (@& ByteArray) Nat
| bs => bs.size
/--
Retrieves the size of the array as a platform-specific fixed-width integer.
Because {name}`USize` is big enough to address all memory on every platform that Lean supports,
there are in practice no {name}`ByteArray`s that have more elements that {name}`USize` can count.
-/
@[extern "lean_sarray_size", simp]
def usize (a : @& ByteArray) : USize :=
a.size.toUSize
/--
Retrieves the byte at the indicated index. Callers must prove that the index is in bounds. The index
is represented by a platform-specific fixed-width integer (either 32 or 64 bits).
Because {name}`USize` is big enough to address all memory on every platform that Lean supports, there are
in practice no {name}`ByteArray`s for which {name}`uget` cannot retrieve all elements.
-/
@[extern "lean_byte_array_uget"]
def uget : (a : @& ByteArray) (i : USize) (h : i.toNat < a.size := by get_elem_tactic) UInt8
| bs, i, h => bs[i]
/--
Retrieves the byte at the indicated index. Panics if the index is out of bounds.
-/
@[extern "lean_byte_array_get"]
def get! : (@& ByteArray) (@& Nat) UInt8
| bs, i => bs[i]!
/--
Retrieves the byte at the indicated index. Callers must prove that the index is in bounds.
Use {name}`uget` for a more efficient alternative or {name}`get!` for a variant that panics if the
index is out of bounds.
-/
@[extern "lean_byte_array_fget"]
def get : (a : @& ByteArray) (i : @& Nat) (h : i < a.size := by get_elem_tactic) UInt8
| bs, i, _ => bs[i]
@@ -75,43 +74,94 @@ instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
instance : GetElem ByteArray USize UInt8 fun xs i => i.toFin < xs.size where
getElem xs i h := xs.uget i h
/--
Replaces the byte at the given index.
The array is modified in-place if there are no other references to it.
If the index is out of bounds, the array is returned unmodified.
-/
@[extern "lean_byte_array_set"]
def set! : ByteArray (@& Nat) UInt8 ByteArray
| bs, i, b => bs.set! i b
/--
Replaces the byte at the given index.
No bounds check is performed, but the function requires a proof that the index is in bounds. This
proof can usually be omitted, and will be synthesized automatically.
The array is modified in-place if there are no other references to it.
-/
@[extern "lean_byte_array_fset"]
def set : (a : ByteArray) (i : @& Nat) UInt8 (h : i < a.size := by get_elem_tactic) ByteArray
| bs, i, b, h => bs.set i b h
@[extern "lean_byte_array_uset"]
@[extern "lean_byte_array_uset", inherit_doc ByteArray.set]
def uset : (a : ByteArray) (i : USize) UInt8 (h : i.toNat < a.size := by get_elem_tactic) ByteArray
| bs, i, v, h => bs.uset i v h
/--
Computes a hash for a {name}`ByteArray`.
-/
@[extern "lean_byte_array_hash"]
protected opaque hash (a : @& ByteArray) : UInt64
instance : Hashable ByteArray where
hash := ByteArray.hash
/--
Returns {name}`true` when {name}`s` contains zero bytes.
-/
def isEmpty (s : ByteArray) : Bool :=
s.size == 0
/--
Copy the slice at `[srcOff, srcOff + len)` in `src` to `[destOff, destOff + len)` in `dest`, growing `dest` if necessary.
If `exact` is `false`, the capacity will be doubled when grown. -/
Copies the slice at `[srcOff, srcOff + len)` in {name}`src` to `[destOff, destOff + len)` in
{name}`dest`, growing {name}`dest` if necessary. If {name}`exact` is {name}`false`, the capacity
will be doubled when grown.
-/
@[extern "lean_byte_array_copy_slice"]
def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray :=
dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + min len (src.data.size - srcOff)) dest.data.size
/--
Copies the bytes with indices {name}`b` (inclusive) to {name}`e` (exclusive) to a new
{name}`ByteArray`.
-/
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
a.copySlice b empty 0 (e - b)
protected def append (a : ByteArray) (b : ByteArray) : ByteArray :=
@[inline]
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
b.copySlice 0 a a.size b.size false
instance : Append ByteArray := ByteArray.append
@[simp]
theorem size_data {a : ByteArray} :
a.data.size = a.size := rfl
@[csimp]
theorem append_eq_fastAppend : @ByteArray.append = @ByteArray.fastAppend := by
funext a b
ext1
apply Array.ext'
simp [ByteArray.fastAppend, copySlice, size_data, - Array.append_assoc]
-- Needs to come after the `csimp` lemma
instance : Append ByteArray where
append := ByteArray.append
@[simp]
theorem append_eq {a b : ByteArray} : a.append b = a ++ b := rfl
@[simp]
theorem fastAppend_eq {a b : ByteArray} : a.fastAppend b = a ++ b := by
simp [ append_eq_fastAppend]
/--
Converts a packed array of bytes to a linked list.
-/
def toList (bs : ByteArray) : List UInt8 :=
let rec loop (i : Nat) (r : List UInt8) :=
if i < bs.size then
@@ -122,16 +172,12 @@ def toList (bs : ByteArray) : List UInt8 :=
decreasing_by decreasing_trivial_pre_omega
loop 0 []
@[inline] def findIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option Nat :=
let rec @[specialize] loop (i : Nat) :=
if h : i < a.size then
if p a[i] then some i else loop (i+1)
else
none
termination_by a.size - i
decreasing_by decreasing_trivial_pre_omega
loop start
/--
Finds the index of the first byte in {name}`a` for which {name}`p` returns {name}`true`. If no byte
in {name}`a` satisfies {name}`p`, then the result is {name}`none`.
The index is returned along with a proof that it is a valid index in the array.
-/
@[inline] def findFinIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option (Fin a.size) :=
let rec @[specialize] loop (i : Nat) :=
if h : i < a.size then
@@ -143,11 +189,29 @@ def toList (bs : ByteArray) : List UInt8 :=
loop start
/--
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
This is similar to the `Array` version.
Finds the index of the first byte in {name}`a` for which {name}`p` returns {name}`true`. If no byte
in {name}`a` satisfies {name}`p`, then the result is {name}`none`.
TODO: avoid code duplication in the future after we improve the compiler.
The variant {name}`findFinIdx?` additionally returns a proof that the found index is in bounds.
-/
@[inline] def findIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option Nat :=
let rec @[specialize] loop (i : Nat) :=
if h : i < a.size then
if p a[i] then some i else loop (i+1)
else
none
termination_by a.size - i
decreasing_by decreasing_trivial_pre_omega
loop start
/--
An efficient implementation of {name}`ForIn.forIn` for {name}`ByteArray` that uses {name}`USize`
rather than {name}`Nat` for indices.
We claim this unsafe implementation is correct because an array cannot have more than
{name}`USize.size` elements in our runtime. This is similar to the {name}`Array` version.
-/
-- TODO: avoid code duplication in the future after we improve the compiler.
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 β m (ForInStep β)) : m β :=
let sz := as.usize
let rec @[specialize] loop (i : USize) (b : β) : m β := do
@@ -160,7 +224,11 @@ def toList (bs : ByteArray) : List UInt8 :=
pure b
loop 0 b
/-- Reference implementation for `forIn` -/
/--
The reference implementation of {name}`ForIn.forIn` for {name}`ByteArray`.
In compiled code, this is replaced by the more efficient {name}`ByteArray.forInUnsafe`.
-/
@[implemented_by ByteArray.forInUnsafe]
protected def forIn {β : Type v} {m : Type v Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 β m (ForInStep β)) : m β :=
let rec loop (i : Nat) (h : i as.size) (b : β) : m β := do
@@ -178,7 +246,13 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
instance : ForIn m ByteArray UInt8 where
forIn := ByteArray.forIn
/-- See comment at `forInUnsafe` -/
/--
An efficient implementation of a monadic left fold on for {name}`ByteArray` that uses {name}`USize`
rather than {name}`Nat` for indices.
We claim this unsafe implementation is correct because an array cannot have more than
{name}`USize.size` elements in our runtime. This is similar to the {name}`Array` version.
-/
-- TODO: avoid code duplication.
@[inline]
unsafe def foldlMUnsafe {β : Type v} {m : Type v Type w} [Monad m] (f : β UInt8 m β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : m β :=
@@ -195,7 +269,14 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
else
pure init
/-- Reference implementation for `foldlM` -/
/--
A monadic left fold on {name}`ByteArray` that iterates over an array from low to high indices,
computing a running value.
Each element of the array is combined with the value from the prior elements using a monadic
function {name}`f`. The initial value {name}`init` is the starting value before any elements have
been processed.
-/
@[implemented_by foldlMUnsafe]
def foldlM {β : Type v} {m : Type v Type w} [Monad m] (f : β UInt8 m β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : m β :=
let fold (stop : Nat) (h : stop as.size) :=
@@ -213,11 +294,23 @@ def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 →
else
fold as.size (Nat.le_refl _)
/--
A left fold on {name}`ByteArray` that iterates over an array from low to high indices, computing a
running value.
Each element of the array is combined with the value from the prior elements using a function
{name}`f`. The initial value {name}`init` is the starting value before any elements have been
processed.
{name}`ByteArray.foldlM` is a monadic variant of this function.
-/
@[inline]
def foldl {β : Type v} (f : β UInt8 β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : β :=
Id.run <| as.foldlM (pure <| f · ·) init start stop
/-- Iterator over the bytes (`UInt8`) of a `ByteArray`.
set_option doc.verso false -- Awaiting intra-module forward reference support
/--
Iterator over the bytes (`UInt8`) of a `ByteArray`.
Typically created by `arr.iter`, where `arr` is a `ByteArray`.
@@ -241,6 +334,7 @@ structure Iterator where
current byte is `(default : UInt8)`. -/
idx : Nat
deriving Inhabited
set_option doc.verso true
/-- Creates an iterator at the beginning of an array. -/
def mkIterator (arr : ByteArray) : Iterator :=
@@ -258,16 +352,25 @@ theorem Iterator.sizeOf_eq (i : Iterator) : sizeOf i = i.array.size - i.idx :=
namespace Iterator
/-- Number of bytes remaining in the iterator. -/
/--
The number of bytes remaining in the iterator.
-/
def remainingBytes : Iterator Nat
| arr, i => arr.size - i
@[inherit_doc Iterator.idx]
def pos := Iterator.idx
/-- The byte at the current position.
/-- True if the iterator is past the array's last byte. -/
@[inline]
def atEnd : Iterator Bool
| arr, i => i arr.size
On an invalid position, returns `(default : UInt8)`. -/
/--
The byte at the current position.
On an invalid position, returns {lean}`(default : UInt8)`.
-/
@[inline]
def curr : Iterator UInt8
| arr, i =>
@@ -276,27 +379,28 @@ def curr : Iterator → UInt8
else
default
/-- Moves the iterator's position forward by one byte, unconditionally.
/--
Moves the iterator's position forward by one byte, unconditionally.
It is only valid to call this function if the iterator is not at the end of the array, *i.e.*
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
{name}`Iterator.atEnd` is {name}`false`; otherwise, the resulting iterator will be invalid.
-/
@[inline]
def next : Iterator Iterator
| arr, i => arr, i + 1
/-- Decreases the iterator's position.
/--
Decreases the iterator's position.
If the position is zero, this function is the identity. -/
If the position is zero, this function is the identity.
-/
@[inline]
def prev : Iterator Iterator
| arr, i => arr, i - 1
/-- True if the iterator is past the array's last byte. -/
@[inline]
def atEnd : Iterator Bool
| arr, i => i arr.size
/-- True if the iterator is not past the array's last byte. -/
/--
True if the iterator is valid; that is, it is not past the array's last byte.
-/
@[inline]
def hasNext : Iterator Bool
| arr, i => i < arr.size
@@ -322,17 +426,21 @@ def next' (it : Iterator) (_h : it.hasNext) : Iterator :=
def hasPrev : Iterator Bool
| _, i => i > 0
/-- Moves the iterator's position to the end of the array.
/--
Moves the iterator's position to the end of the array.
Note that `i.toEnd.atEnd` is always `true`. -/
Given {given}`i : ByteArray.Iterator`, note that {lean}`i.toEnd.atEnd` is always {name}`true`.
-/
@[inline]
def toEnd : Iterator Iterator
| arr, _ => arr, arr.size
/-- Moves the iterator's position several bytes forward.
/--
Moves the iterator's position several bytes forward.
The resulting iterator is only valid if the number of bytes to skip is less than or equal to
the number of bytes left in the iterator. -/
the number of bytes left in the iterator.
-/
@[inline]
def forward : Iterator Nat Iterator
| arr, i, f => arr, i + f
@@ -340,9 +448,11 @@ def forward : Iterator → Nat → Iterator
@[inherit_doc forward, inline]
def nextn : Iterator Nat Iterator := forward
/-- Moves the iterator's position several bytes back.
/--
Moves the iterator's position several bytes back.
If asked to go back more bytes than available, stops at the beginning of the array. -/
If asked to go back more bytes than available, stops at the beginning of the array.
-/
@[inline]
def prevn : Iterator Nat Iterator
| arr, i, f => arr, i - f
@@ -350,13 +460,4 @@ def prevn : Iterator → Nat → Iterator
end Iterator
end ByteArray
/--
Converts a list of bytes into a `ByteArray`.
-/
def List.toByteArray (bs : List UInt8) : ByteArray :=
let rec loop
| [], r => r
| b::bs, r => loop bs (r.push b)
loop bs ByteArray.empty
instance : ToString ByteArray := fun bs => bs.toList.toString

View File

@@ -0,0 +1,59 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.List.Basic
public section
set_option doc.verso true
namespace ByteArray
@[simp]
theorem data_push {a : ByteArray} {b : UInt8} : (a.push b).data = a.data.push b := rfl
/--
Appends two byte arrays.
In compiled code, calls to {name}`ByteArray.append` are replaced with the much more efficient
{name (scope:="Init.Data.ByteArray.Basic")}`ByteArray.fastAppend`.
-/
@[expose]
protected def append (a b : ByteArray) : ByteArray :=
a.data.toList ++ b.data.toList
@[simp]
theorem toList_data_append' {a b : ByteArray} :
(a.append b).data.toList = a.data.toList ++ b.data.toList := by
have a := a
have b := b
rfl
theorem ext : {x y : ByteArray} x.data = y.data x = y
| _, _, rfl => rfl
end ByteArray
@[simp]
theorem List.toList_data_toByteArray {l : List UInt8} :
l.toByteArray.data.toList = l := by
rw [List.toByteArray]
suffices a b, (List.toByteArray.loop a b).data.toList = b.data.toList ++ a by
simpa using this l ByteArray.empty
intro a b
fun_induction List.toByteArray.loop a b with simp_all [toList_push]
where
toList_push {xs : Array UInt8} {x : UInt8} : (xs.push x).toList = xs.toList ++ [x] := by
have xs := xs
simp [Array.push, List.concat_eq_append]
theorem List.toByteArray_append' {l l' : List UInt8} :
(l ++ l').toByteArray = l.toByteArray.append l'.toByteArray :=
ByteArray.ext (ext (by simp))
where
ext : {x y : Array UInt8} x.toList = y.toList x = y
| _, _, rfl => rfl

View File

@@ -9,7 +9,13 @@ prelude
public import Init.Data.ByteArray.Basic
import Init.Data.String.Basic
/-- Interpret a `ByteArray` of size 8 as a little-endian `UInt64`. -/
set_option doc.verso true
/--
Interprets a {name}`ByteArray` of size 8 as a little-endian {name}`UInt64`.
Panics if the array's size is not 8.
-/
public def ByteArray.toUInt64LE! (bs : ByteArray) : UInt64 :=
assert! bs.size == 8
(bs.get! 7).toUInt64 <<< 0x38 |||
@@ -21,7 +27,11 @@ public def ByteArray.toUInt64LE! (bs : ByteArray) : UInt64 :=
(bs.get! 1).toUInt64 <<< 0x8 |||
(bs.get! 0).toUInt64
/-- Interpret a `ByteArray` of size 8 as a big-endian `UInt64`. -/
/--
Interprets a {name}`ByteArray` of size 8 as a big-endian {name}`UInt64`.
Panics if the array's size is not 8.
-/
public def ByteArray.toUInt64BE! (bs : ByteArray) : UInt64 :=
assert! bs.size == 8
(bs.get! 0).toUInt64 <<< 0x38 |||

View File

@@ -0,0 +1,289 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.ByteArray.Basic
public section
namespace ByteArray
-- At present the preferred normal form for empty byte arrays is `ByteArray.empty`
@[simp]
theorem emptyc_eq_empty : ( : ByteArray) = ByteArray.empty := rfl
@[simp]
theorem emptyWithCapacity_eq_empty : ByteArray.emptyWithCapacity 0 = ByteArray.empty := rfl
@[simp]
theorem data_empty : ByteArray.empty.data = #[] := rfl
@[simp]
theorem data_extract {a : ByteArray} {b e : Nat} :
(a.extract b e).data = a.data.extract b e := by
simp [extract, copySlice]
by_cases b e
· rw [(by omega : b + (e - b) = e)]
· rw [Array.extract_eq_empty_of_le (by omega), Array.extract_eq_empty_of_le (by omega)]
@[simp]
theorem extract_zero_size {b : ByteArray} : b.extract 0 b.size = b := by
ext1
simp
@[simp]
theorem extract_same {b : ByteArray} {i : Nat} : b.extract i i = ByteArray.empty := by
ext1
simp [Nat.min_le_left]
theorem fastAppend_eq_copySlice {a b : ByteArray} :
a.fastAppend b = b.copySlice 0 a a.size b.size false := rfl
@[simp]
theorem _root_.List.toByteArray_append {l l' : List UInt8} :
(l ++ l').toByteArray = l.toByteArray ++ l'.toByteArray := by
simp [List.toByteArray_append']
@[simp]
theorem toList_data_append {l l' : ByteArray} :
(l ++ l').data.toList = l.data.toList ++ l'.data.toList := by
simp [ append_eq]
@[simp]
theorem data_append {l l' : ByteArray} :
(l ++ l').data = l.data ++ l'.data := by
simp [ Array.toList_inj]
@[simp]
theorem size_empty : ByteArray.empty.size = 0 := by
simp [ ByteArray.size_data]
@[simp]
theorem _root_.List.data_toByteArray {l : List UInt8} :
l.toByteArray.data = l.toArray := by
rw [List.toByteArray]
suffices a b, (List.toByteArray.loop a b).data = b.data ++ a.toArray by
simpa using this l ByteArray.empty
intro a b
fun_induction List.toByteArray.loop a b with simp_all
@[simp]
theorem _root_.List.size_toByteArray {l : List UInt8} :
l.toByteArray.size = l.length := by
simp [ ByteArray.size_data]
@[simp]
theorem _root_.List.toByteArray_nil : List.toByteArray [] = ByteArray.empty := rfl
@[simp]
theorem empty_append {b : ByteArray} : ByteArray.empty ++ b = b := by
ext1
simp
@[simp]
theorem append_empty {b : ByteArray} : b ++ ByteArray.empty = b := by
ext1
simp
@[simp, grind =]
theorem size_append {a b : ByteArray} : (a ++ b).size = a.size + b.size := by
simp [ size_data]
@[simp]
theorem size_eq_zero_iff {a : ByteArray} : a.size = 0 a = ByteArray.empty := by
refine fun h => ?_, fun h => h ByteArray.size_empty
ext1
simp [ Array.size_eq_zero_iff, h]
theorem getElem_eq_getElem_data {a : ByteArray} {i : Nat} {h : i < a.size} :
a[i] = a.data[i]'(by simpa [ size_data]) := rfl
@[simp]
theorem getElem_append_left {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
(hlt : i < a.size) : (a ++ b)[i] = a[i] := by
simp only [getElem_eq_getElem_data, data_append]
rw [Array.getElem_append_left (by simpa)]
theorem getElem_append_right {i : Nat} {a b : ByteArray} {h : i < (a ++ b).size}
(hle : a.size i) : (a ++ b)[i] = b[i - a.size]'(by simp_all; omega) := by
simp only [getElem_eq_getElem_data, data_append]
rw [Array.getElem_append_right (by simpa)]
simp
@[simp]
theorem _root_.List.getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.toByteArray.size} :
l.toByteArray[i]'h = l[i]'(by simp_all) := by
simp [ByteArray.getElem_eq_getElem_data]
theorem _root_.List.getElem_eq_getElem_toByteArray {l : List UInt8} {i : Nat} {h : i < l.length} :
l[i]'h = l.toByteArray[i]'(by simp_all) := by
simp
@[simp]
theorem size_extract {a : ByteArray} {b e : Nat} :
(a.extract b e).size = min e a.size - b := by
simp [ size_data]
@[simp]
theorem extract_eq_empty_iff {b : ByteArray} {i j : Nat} : b.extract i j = ByteArray.empty min j b.size i := by
rw [ size_eq_zero_iff, size_extract]
omega
@[simp]
theorem extract_add_left {b : ByteArray} {i j : Nat} : b.extract (i + j) i = ByteArray.empty := by
simp only [extract_eq_empty_iff]
exact Nat.le_trans (Nat.min_le_left _ _) (by simp)
@[simp]
theorem append_eq_empty_iff {a b : ByteArray} :
a ++ b = ByteArray.empty a = ByteArray.empty b = ByteArray.empty := by
simp [ size_eq_zero_iff, size_append]
@[simp]
theorem toByteArray_eq_empty {l : List UInt8} :
l.toByteArray = ByteArray.empty l = [] := by
simp [ ByteArray.size_eq_zero_iff]
@[simp]
theorem append_right_inj {ys₁ ys₂ : ByteArray} (xs : ByteArray) :
xs ++ ys₁ = xs ++ ys₂ ys₁ = ys₂ := by
simp [ByteArray.ext_iff, Array.append_right_inj]
@[simp]
theorem append_left_inj {xs₁ xs₂ : ByteArray} (ys : ByteArray) :
xs₁ ++ ys = xs₂ ++ ys xs₁ = xs₂ := by
simp [ByteArray.ext_iff, Array.append_left_inj]
@[simp]
theorem extract_append_extract {a : ByteArray} {i j k : Nat} :
a.extract i j ++ a.extract j k = a.extract (min i j) (max j k) := by
ext1
simp
theorem extract_eq_extract_append_extract {a : ByteArray} {i k : Nat} (j : Nat)
(hi : i j) (hk : j k) :
a.extract i k = a.extract i j ++ a.extract j k := by
simp
rw [Nat.min_eq_left hi, Nat.max_eq_right hk]
theorem append_inj_left {xs₁ xs₂ ys₁ ys₂ : ByteArray} (h : xs₁ ++ ys₁ = xs₂ ++ ys₂) (hl : xs₁.size = xs₂.size) : xs₁ = xs₂ := by
simp only [ByteArray.ext_iff, ByteArray.size_data, ByteArray.data_append] at *
exact Array.append_inj_left h hl
theorem extract_append_eq_right {a b : ByteArray} {i j : Nat} (hi : i = a.size) (hj : j = a.size + b.size) :
(a ++ b).extract i j = b := by
subst hi hj
ext1
simp [ size_data]
theorem extract_append_eq_left {a b : ByteArray} {i : Nat} (hi : i = a.size) :
(a ++ b).extract 0 i = a := by
subst hi
ext1
simp
theorem extract_append_size_left {a b : ByteArray} {i : Nat} :
(a ++ b).extract i a.size = a.extract i a.size := by
ext1
simp
theorem extract_append_size_add {a b : ByteArray} {i j : Nat} :
(a ++ b).extract (a.size + i) (a.size + j) = b.extract i j := by
ext1
simp
theorem extract_append {as bs : ByteArray} {i j : Nat} :
(as ++ bs).extract i j = as.extract i j ++ bs.extract (i - as.size) (j - as.size) := by
ext1
simp
theorem extract_append_size_add' {a b : ByteArray} {i j k : Nat} (h : k = a.size) :
(a ++ b).extract (k + i) (k + j) = b.extract i j := by
cases h
rw [extract_append_size_add]
theorem extract_extract {a : ByteArray} {i j k l : Nat} :
(a.extract i j).extract k l = a.extract (i + k) (min (i + l) j) := by
ext1
simp
theorem getElem_extract_aux {xs : ByteArray} {start stop : Nat} (h : i < (xs.extract start stop).size) :
start + i < xs.size := by
rw [size_extract] at h; apply Nat.add_lt_of_lt_sub'; apply Nat.lt_of_lt_of_le h
apply Nat.sub_le_sub_right; apply Nat.min_le_right
theorem getElem_extract {i : Nat} {b : ByteArray} {start stop : Nat}
(h) : (b.extract start stop)[i]'h = b[start + i]'(getElem_extract_aux h) := by
simp [getElem_eq_getElem_data]
theorem extract_eq_extract_left {a : ByteArray} {i i' j : Nat} :
a.extract i j = a.extract i' j min j a.size - i = min j a.size - i' := by
simp [ByteArray.ext_iff, Array.extract_eq_extract_left]
theorem extract_add_one {a : ByteArray} {i : Nat} (ha : i + 1 a.size) :
a.extract i (i + 1) = [a[i]].toByteArray := by
ext
· simp
omega
· rename_i j hj hj'
obtain rfl : j = 0 := by simpa using hj'
simp [ByteArray.getElem_eq_getElem_data]
theorem extract_add_two {a : ByteArray} {i : Nat} (ha : i + 2 a.size) :
a.extract i (i + 2) = [a[i], a[i + 1]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_one (by omega)]
simp [ List.toByteArray_append]
theorem extract_add_three {a : ByteArray} {i : Nat} (ha : i + 3 a.size) :
a.extract i (i + 3) = [a[i], a[i + 1], a[i + 2]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_two (by omega)]
simp [ List.toByteArray_append]
theorem extract_add_four {a : ByteArray} {i : Nat} (ha : i + 4 a.size) :
a.extract i (i + 4) = [a[i], a[i + 1], a[i + 2], a[i + 3]].toByteArray := by
rw [extract_eq_extract_append_extract (i + 1) (by simp) (by omega),
extract_add_one (by omega), extract_add_three (by omega)]
simp [ List.toByteArray_append]
theorem append_assoc {a b c : ByteArray} : a ++ b ++ c = a ++ (b ++ c) := by
ext1
simp
@[simp]
theorem toList_empty : ByteArray.empty.toList = [] := by
simp [ByteArray.toList, ByteArray.toList.loop]
theorem copySlice_eq_append {src : ByteArray} {srcOff : Nat} {dest : ByteArray} {destOff len : Nat} {exact : Bool} :
ByteArray.copySlice src srcOff dest destOff len exact =
dest.extract 0 destOff ++ src.extract srcOff (srcOff +len) ++ dest.extract (destOff + min len (src.data.size - srcOff)) dest.data.size := by
ext1
simp [copySlice]
@[simp]
theorem data_set {as : ByteArray} {i : Nat} {h : i < as.size} {a : UInt8} :
(as.set i a h).data = as.data.set i a (by simpa) := by
simp [set]
theorem set_eq_push_extract_append_extract {as : ByteArray} {i : Nat} (h : i < as.size) {a : UInt8} :
as.set i a h = (as.extract 0 i).push a ++ as.extract (i + 1) as.size := by
ext1
simpa using Array.set_eq_push_extract_append_extract _
@[simp]
theorem append_toByteArray_singleton {as : ByteArray} {a : UInt8} :
as ++ [a].toByteArray = as.push a := by
ext1
simp
@[simp]
theorem extract_zero_max_size {a : ByteArray} {i : Nat} : a.extract 0 (max i a.size) = a := by
ext1
simp [Nat.le_max_right]
end ByteArray

View File

@@ -9,5 +9,3 @@ prelude
public import Init.Data.Char.Basic
public import Init.Data.Char.Lemmas
public import Init.Data.Char.Order
public section

View File

@@ -6,7 +6,6 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Char.Basic
import all Init.Data.Char.Basic
public import Init.Data.UInt.Lemmas
@@ -14,12 +13,16 @@ public section
namespace Char
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
@[deprecated Char.ext (since := "2025-10-26")]
protected theorem eq_of_val_eq : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
theorem le_def {a b : Char} : a b a.1 b.1 := .rfl
theorem lt_def {a b : Char} : a < b a.1 < b.1 := .rfl
@[deprecated lt_def (since := "2025-10-26")]
theorem lt_iff_val_lt_val {a b : Char} : a < b a.val < b.val := Iff.rfl
@[simp] protected theorem not_le {a b : Char} : ¬ a b b < a := UInt32.not_le
@[simp] protected theorem not_lt {a b : Char} : ¬ a < b b a := UInt32.not_lt
@[simp] protected theorem le_refl (a : Char) : a a := by simp [le_def]
@@ -52,8 +55,12 @@ instance leAntisymm : Std.Antisymm (· ≤ · : Char → Char → Prop) where
antisymm _ _ := Char.le_antisymm
-- This instance is useful while setting up instances for `String`.
instance ltTrichotomous : Std.Trichotomous (· < · : Char Char Prop) where
trichotomous _ _ h₁ h₂ := Char.le_antisymm (by simpa using h₂) (by simpa using h₁)
@[deprecated ltTrichotomous (since := "2025-10-27")]
def notLTAntisymm : Std.Antisymm (¬ · < · : Char Char Prop) where
antisymm _ _ h₁ h₂ := Char.le_antisymm (by simpa using h₂) (by simpa using h₁)
antisymm := Char.ltTrichotomous.trichotomous
instance ltAsymm : Std.Asymm (· < · : Char Char Prop) where
asymm _ _ := Char.lt_asymm
@@ -70,4 +77,9 @@ def notLTTotal : Std.Total (¬ · < · : Char → Char → Prop) where
rw [Char.ofNat, dif_pos]
rfl
@[simp]
theorem toUInt8_val {c : Char} : c.val.toUInt8 = c.toUInt8 := rfl
theorem toString_eq_singleton {c : Char} : c.toString = String.singleton c := rfl
end Char

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