Compare commits

...

67 Commits

Author SHA1 Message Date
Leonardo de Moura
6c6b776d87 chore: move example 2025-04-11 12:06:03 -07:00
Leonardo de Moura
5e8caa562f chore: example is working now 2025-04-11 12:06:03 -07:00
Leonardo de Moura
c6d179143e fix: bug at isCongrToPrevSplit 2025-04-11 12:06:03 -07:00
Leonardo de Moura
6e3ebdca91 fix: missing "Exists .. = False` propagator 2025-04-11 12:06:03 -07:00
Leonardo de Moura
f57016ee3a chore: move test
This test is easy for `grind`, we just need to annotate `Nat.min_def`.
2025-04-11 12:06:03 -07:00
Sebastian Ullrich
0669a04704 chore: CI: limit CCACHE_SIZE to 400MB (#7922) 2025-04-11 17:09:16 +00:00
Sebastian Ullrich
5cd352588c perf: use mimalloc with important C++ hash maps (#7868)
`unordered_map`/`unordered_set` does an allocation per insert, use
mimalloc for them for important hash maps
2025-04-11 16:23:33 +00:00
Henrik Böving
e9cc776f22 perf: bv_decide DecidableEq fast path using hash comparison (#7920)
This PR introduces a fast path based on comparing the (cached) hash
value to the `DecidableEq` instance of the core expression data type in
`bv_decide`'s bitblaster.

As we use a good hash function ™️ this should allow us to short
circuit to "not equal" quicker (if appropriate) than currently as we
will often not have to traverse all the way down to the actual conflict.
This in turn should speed up traversing of bucket chains during hash
collisions.
2025-04-11 15:00:41 +00:00
Lean stage0 autoupdater
e79fef15df chore: update stage0 2025-04-11 14:12:34 +00:00
Sebastian Ullrich
c672934f11 chore: add "Init size" benchmark (#7918) 2025-04-11 13:15:27 +00:00
Sebastian Ullrich
582877d2d3 feat: environment extension data can be split into .olean.server (#7914)
This PR adds a function hook `PersistentEnvExtension.saveEntriesFn` that
can be used to store server-only metadata such as position information
and docstrings that should not affect (re)builds.
2025-04-11 13:06:19 +00:00
Marc Huisinga
39ce3d14f4 test: make test deterministic (#7916) 2025-04-11 11:16:16 +00:00
Kim Morrison
32758aa712 feat: lemmas about permutations (#7912)
This PR adds `List.Perm.take/drop`, and `Array.Perm.extract`,
restricting permutations to sublist / subarrays when they are constant
elsewhere.
2025-04-11 08:13:58 +00:00
Kim Morrison
0f6e35dc63 feat: missing List/Array/Vector lemmas about isSome_idxOf? and relatives (#7913)
This PR adds some missing `List/Array/Vector lemmas` about
`isSome_idxOf?`, `isSome_finIdxOf?`, `isSome_findFinIdx?,
`isSome_findIdx?` and the corresponding `isNone` versions.
2025-04-11 07:45:46 +00:00
Kim Morrison
2528188dde chore: add failing grind test (#7910)
Adds a currently failing test, for a `grind` improvement.
2025-04-11 03:22:56 +00:00
Leonardo de Moura
1cdadfd47a chore: cleanup grind cutsat trace messages (#7908) 2025-04-11 00:52:18 +00:00
Kyle Miller
e07c59c831 fix: eliminate panic when inductive has autoparam parameter with underdetermined type (#7905)
This PR fixes an issue introduced bug #6125 where an `inductive` or
`structure` with an autoimplicit parameter with a type that has a
metavariable would lead to a panic. Closes #7788.

This was due to switching from `Term.addAutoBoundImplicits'` to
`Term.addAutoBoundImplicits` and not properly handling metavariables in
the parameters list. To fix this, now the inductive type headers record
the abstracted type and the number of parameters, rather than record the
parameters, the type, the local context, and the local instances. A
benefit to this over `Term.addAutoBoundImplicits'` is that the type's
parameters do not appear twice in the local context.
2025-04-11 00:19:53 +00:00
Leonardo de Moura
cbd38ceadd fix: mbtc and cast issue in grind (#7907)
This PR fixes two bugs in `grind`. 
1. Model-based theory combination was creating type incorrect terms.
2. `Nat.cast` vs `NatCast.natCast` issue during normalization.
2025-04-10 22:46:56 +00:00
Kyle Miller
c46f1e941c fix: sorry in Infoview shouldn't show module name (#7813)
This PR fixes an issue where `let n : Nat := sorry` in the Infoview
pretty prints as ``n : ℕ := sorry `«Foo:17:17»``. This was caused by
top-level expressions being pretty printed with the same rules as
Infoview hovers. Closes #6715. Refactors `Lean.Widget.ppExprTagged`; now
it takes a delaborator, and downstream users should configure their own
pretty printer option overrides if necessary if they used the `explicit`
argument (see `Lean.Widget.makePopup.ppExprForPopup` for an example).
Breaking change: `ppExprTagged` does not set `pp.proofs` on the root
expression.
2025-04-10 21:47:07 +00:00
Markus Himmel
cf3b257ccd chore: Option cleanup (#7897)
This PR cleans up the `Option` development, upstreaming some results
from mathlib in the process.

Notable changes:
- the name `<op>_eq_some_iff` is preferred over `<op>_eq_some`
- the `simp` normal form for `<$>` is `Option.map`, for `>>=` is
`Option.bind` and for `<|>` is `Option.orElse` (for the former two, this
was already true before this PR). All further lemmas about these
operations are now stated only in terms of
`Option.map`/`Option.bind`/`Option.orElse`. Previously, in some cases
both versions were available, with a prime used to disambiguate (the
primed version was usually the "non-ascii-art" version). Now, there are
no lemmas about the ascii-art versions besides the ones turning them
into the non-ascii-art operations, and there is only one version of
every lemma, about the non-ascii-art operation, and named without a
prime.
2025-04-10 18:53:30 +00:00
Kyle Miller
09ab15dc6d fix: remove infinite loop in withFnRefWhenTagAppFns (#7904)
This PR fixes an oversight in `withFnRefWhenTagAppFns` that causes an
infinite loop when the expression is a constant. This affected pretty
printing of zero-field structures when `pp.tagAppFns` was true (used by
docgen and verso). Closes #7898.
2025-04-10 17:16:29 +00:00
Sebastian Ullrich
e631efd817 feat: introduce Elab.inServer option (#7902)
This PR introduces a dedicated option for checking whether elaborators
are running in the language server.
2025-04-10 14:51:37 +00:00
Sebastian Graf
d2f4ce0158 fix: Add Inhabited instance for OptionT (#7901)
This PR adds `instance [Pure f] : Inhabited (OptionT f α)`, so that
`Inhabited (OptionT Id Empty)` synthesizes.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-04-10 14:49:03 +00:00
Sebastian Ullrich
69536808ca feat: read/writeModuleDataParts API for serialization with cross-file sharing (#7854)
This PR introduces fundamental API to distribute module data across
multiple files in preparation for the module system.
2025-04-10 13:32:24 +00:00
Markus Himmel
3d5dd15de4 chore: move bmod results from LemmasAux.lean to DivMod/Lemmas.lean (#7899)
This PR shuffles some results about integers around to make sure that
all material that currently exists about `Int.bmod` is located in
`DivMod/Lemmas.lean` and not downstream of that.
2025-04-10 12:07:11 +00:00
Lean stage0 autoupdater
91c245663b chore: update stage0 2025-04-10 12:26:07 +00:00
Sebastian Ullrich
1421b6145e fix: cancellation of synchronous part of previous elaboration (#7882)
This PR fixes a regression where elaboration of a previous document
version is not cancelled on changes to the document.

Done by removing the default from `SnapshotTask.cancelTk?` and
consistently passing the current thread's token for synchronous
elaboration steps.
2025-04-10 11:43:41 +00:00
Kim Morrison
bffa642ad6 feat: Lean.Grind.IsCharP (#7870)
This PR adds a mixin typeclass for `Lean.Grind.CommRing` recording the
characteristic of the ring, and constructs instances for `Int`, `IntX`,
`UIntX`, and `BitVec`.
2025-04-10 08:36:42 +00:00
Kim Morrison
deef1c2739 feat: BitVec.pow and Pow (BitVec w) Nat (#7893)
This PR adds `BitVec.pow` and `Pow (BitVec w) Nat`. The implementation
is the naive one, and should later be replaced by an `@[extern]`. This
is tracked at https://github.com/leanprover/lean4/issues/7887.
2025-04-10 05:21:30 +00:00
Kim Morrison
acf42bd30b chore: add simp lemma Int.cast x = x for x : Int (#7891)
This PR adds the rfl simp lemma `Int.cast x = x` for `x : Int`.
2025-04-10 02:35:06 +00:00
Leonardo de Moura
4947215325 feat: improve funext support in grind (#7892)
This PR improves the support for `funext` in `grind`. We will push
another PR to minimize the number of case-splits later.
2025-04-10 01:57:27 +00:00
Kim Morrison
6e7209dfa3 chore: add Int.dvd_iff_bmod_eq_zero (#7890)
This PR adds missing lemmas about `Int.bmod`, parallel to lemmas about
the other `mod` variants.
2025-04-10 01:36:42 +00:00
Kim Morrison
97a00b3881 chore: variant of Int.toNat_sub (#7889)
This PR adds `Int.toNat_sub''` a variant of `Int.toNat_sub` taking
inequality hypotheses, rather than expecting the arguments to be casts
of natural numbers. This is parallel to the existing `toNat_add` and
`toNat_mul`.
2025-04-10 01:34:48 +00:00
Kim Morrison
d758b4c862 chore: Fin.ofNat'_mul, analogous to existing add lemmas (#7888)
This PR adds `Fin.ofNat'_mul` and `Fin.mul_ofNat'`, parallel to the
existing lemmas about `add`.
2025-04-10 01:32:47 +00:00
Kim Morrison
61d7716ad8 feat: UIntX.pow and Pow UIntX Nat instances (#7886)
This PR adds `UIntX.pow` and `Pow UIntX Nat` instances, and similarly
for signed fixed-width integers. These are currently only the naive
implementation, and will need to be subsequently replaced via
`@[extern]` with fast implementations (tracked at #7887).
2025-04-10 00:27:48 +00:00
Kim Morrison
05f16ed279 feat: UIntX.ofInt (#7880)
This PR adds the functions `UIntX.ofInt`, and basic lemmas.
2025-04-09 23:50:29 +00:00
Leonardo de Moura
985cd71f23 fix: Nat counterexamples in grind (#7885)
This PR fixes the counterexamples produced by the cutsat procedure in
`grind` for examples containing `Nat` terms.
2025-04-09 18:30:58 +00:00
Marc Huisinga
2ede81fe10 fix: search path related bugs (#7873)
This PR fixes a number of bugs related to the handling of the source
search path in the language server, where deleting files could cause
several features to stop functioning and both untitled files and files
that don't exist on disc could have conflicting module names.

In detail, it makes the following adjustments:
- The URI <-> module name conversion was adjusted to produce no name
collisions.
- File URIs in the search path yield a module name relative to the
search path, as before.
- File URIs not in the search path, non-file URIs and non-`.lean` files
yield a `«external:<full uri>»` module name.
- To avoid the issue of the URI -> module name conversion failing when a
file is deleted from disc, we now cache the result of this conversion in
the watchdog and the file worker when the file is first opened.
- All of the URI <-> module name conversions now consistently go through
`Server.documentUriFromModule?` and `moduleFromDocumentUri` to ensure
that we don't have minor deviations for this conversion all over the
place.
- The threading of the source search path through the file worker (from
`lake setup-file`) is removed. It turns out that `lake serve` already
sets the correct source search path in the environment, so we can just
always use the search path from the environment.
- Since we can now answer more requests that need the .ileans in
untitled files, a lot of the tests that test 'Go to definition' needed
to be adjusted so that they use the information from the watchdog, not
the file worker. As we load references asynchronously, this PR adds an
internal `$/lean/waitForILeans` request that tests can use to wait for
all .ilean files to be loaded and for the ilean references from the file
worker for the current document version to be finalized.
- As part of this PR, we noticed that the .ileans aren't available in
the NixOS setup, so @Kha adjusted the Nix CI to fix this.

### Breaking changes
- `Server.documentUriFromModule` has been renamed to
`Server.documentUriFromModule?` and doesn't take a `SearchPath` argument
anymore, as the `SearchPath` is now computed from the `LEAN_SRC_PATH`
environment variable. It has also been moved from `Lean.Server.GoTo` to
`Lean.Server.Utils`.
- `Server.moduleFromDocumentUri` does not take a `SearchPath` argument
anymore and won't return an `Option` anymore. It has also been moved
from `Lean.Server.GoTo` to `Lean.Server.Utils`.
- The `System.SearchPath.searchModuleNameOfUri` function has been
removed. It is recommended to use `Server.moduleFromDocumentUri`
instead.
- The `initSrcSearchPath` function has been renamed to
`getSrcSearchPath` and has been moved from `Lean.Util.Paths` to
`Lean.Util.Path`. It also doesn't need to take a `pkgSearchPath`
argument anymore.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-04-09 15:37:49 +00:00
Sebastian Ullrich
4d6ad8b0fb chore: remove stray test output file (#7881) 2025-04-09 14:46:31 +00:00
Kim Morrison
07e7a43668 chore: add Int.toNat_emod (#7879)
This PR adds `Int.toNat_emod`, analogous to `Int.toNat_add/mul`.
2025-04-09 13:42:15 +00:00
Leonardo de Moura
388b6f045b chore: avoid unnecessary quotations in cutsat traces and counterexamples (#7877)
cc @kim-em
2025-04-08 21:01:07 +00:00
Leonardo de Moura
5a6f45a324 feat: improve cutsat Nat support (#7876)
This PR eliminates another source of facts of the form `-1 *
NatCast.natCast x <= 0` for each `x : Nat` in the local context. These
facts are now stored internally in the cutsat state.

cc @kim-em
2025-04-08 19:40:45 +00:00
Wojciech Nawrocki
e6ce55ffd4 feat: make TryThis work in widget messages (#7610)
This PR adjusts the `TryThis` widget to also work in widget messages
rather than only as a panel widget. It also adds additional
documentation explaining why this change was needed.
2025-04-08 16:01:03 +00:00
Sebastian Ullrich
1b40c46ab1 chore: panic on blocking waits in sync tasks (#7853) 2025-04-08 14:49:26 +00:00
Markus Himmel
0b54a76e32 chore: cleanup of monadic Option functions (#7871)
This PR generalizes the typeclass assumptions on monadic `Option`
functions.

`Option.mapA` is now an alias for `Option.mapM`, which now works for
applicative functors. The changed definition is exactly equivalent for
monads which use the default implementation of `map`, and those who
change it will hopefully choose a definition for `map` that is more
efficient and not less efficient. `Option.mapA` is not deprecated in
order to keep the API aligned with `List` (`List.mapA` and `List.mapM`
cannot be unified because the monadic version is much more efficient
than the applicative version).
2025-04-08 14:27:24 +00:00
dependabot[bot]
4bb8d37e37 chore: CI: bump dcarbone/install-jq-action from 3.0.1 to 3.1.1 (#7780)
Bumps
[dcarbone/install-jq-action](https://github.com/dcarbone/install-jq-action)
from 3.0.1 to 3.1.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dcarbone/install-jq-action/releases">dcarbone/install-jq-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.1.1</h2>
<h2>What's Changed</h2>
<ul>
<li>1.7.1 for windows and some small cleanup by <a
href="https://github.com/dcarbone"><code>@​dcarbone</code></a> in <a
href="https://redirect.github.com/dcarbone/install-jq-action/pull/17">dcarbone/install-jq-action#17</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dcarbone/install-jq-action/compare/v3...v3.1.1">https://github.com/dcarbone/install-jq-action/compare/v3...v3.1.1</a></p>
<h2>v3.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>bumping default to jq 1.7.1 by <a
href="https://github.com/dcarbone"><code>@​dcarbone</code></a> in <a
href="https://redirect.github.com/dcarbone/install-jq-action/pull/16">dcarbone/install-jq-action#16</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dcarbone/install-jq-action/compare/v3...v3.1.0">https://github.com/dcarbone/install-jq-action/compare/v3...v3.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f0e10f46ff"><code>f0e10f4</code></a>
1.7.1 for windows and some small cleanup (<a
href="https://redirect.github.com/dcarbone/install-jq-action/issues/17">#17</a>)</li>
<li><a
href="8f16b8ad5b"><code>8f16b8a</code></a>
remove ubuntu 20.04 from tests.</li>
<li><a
href="26514abd65"><code>26514ab</code></a>
always forget the dang readme.</li>
<li><a
href="4e6d52de30"><code>4e6d52d</code></a>
bumping default to jq 1.7.1 (<a
href="https://redirect.github.com/dcarbone/install-jq-action/issues/16">#16</a>)</li>
<li><a
href="8fd607321d"><code>8fd6073</code></a>
Update README.md</li>
<li>See full diff in <a
href="https://github.com/dcarbone/install-jq-action/compare/v3.0.1...v3.1.1">compare
view</a></li>
</ul>
</details>
<br />


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

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

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

---

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

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


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-08 12:12:52 +00:00
Henrik Böving
e76eb6fbd2 fix: make Array.emptyWithCapacity actually allocate (#7869)
This PR fixes a regression introduced in #7445 where the new
`Array.emptyWithCapacity` was accidentally not tagged with the correct
function to actually allocate the capacity.
2025-04-08 09:37:33 +00:00
Markus Himmel
ca3f43907b chore: drop Option.zipWith, use Option.merge instead (#7851)
This PR partially reverts #7818, because the function called
`Option.zipWith` in that PR does not actually correspond to
`List.zipWith`. We choose `Option.merge` as the name instead.
2025-04-08 08:44:52 +00:00
Markus Himmel
106b772659 chore: remove membership instance on Option from most theorem statements (#7856)
This PR changes definitions and theorems not to use the membership
instance on `Option` unless the theorem is specifically about the
membership instance.

The reasoning for this change is that the lemma `a ∈ o ↔ o = some a` is
a `simp` lemma, and we generally want theorem statements to use `simp`
normal forms.

One notable exception is the `ForIn'` instance, which must use
`Membership` because unlike `GetElem`, `ForIn'` requires the validity
predicate to be expressed via `Membership`.
2025-04-08 08:06:50 +00:00
Lean stage0 autoupdater
e8446c81c8 chore: update stage0 2025-04-08 07:50:47 +00:00
JovanGerb
bfed223306 perf: use Array.emptyWithCapacity in toArray for HashMap and TreeMap (#7863)
This PR improves the `toArray` functions of `HashMap` and `TreeMap` to
use the known size for the initial capacity of the `Array`.
2025-04-08 05:59:53 +00:00
Mac Malone
a35c62d0ad chore: lake: builtins for DSL & plugin for server (#7860)
This PR restores the use of builtins (e.g., initializer, elaborators,
and macros) for DSL features and the use of the Lake plugin in the
server.

The motivation is to avoid elaboration breakages in Lake when core types
need changing (e.g., `Environment`).

This reverts #7399 and partially reverts #7608. The use of the plugin is
more narrow -- it is now just used for elaboration of Lake configuration
files in the server. This should hopefully avoid the reappearance of
#7388.
2025-04-08 03:45:33 +00:00
Leonardo de Moura
e86644f329 chore: remove not very useful reportIssue (#7866)
cc @kim-em
2025-04-08 03:00:48 +00:00
Leonardo de Moura
d1dad44227 fix: missing propagation rule for implication in grind (#7865)
This PR adds a missing propagation rule for implication in `grind`. It
also avoids unnecessary case-splits on implications.
2025-04-08 02:13:13 +00:00
Leonardo de Moura
ba1c1258d7 feat: case split on implications in grind (#7864)
This PR adds support to `grind` for case splitting on implications of
the form `p -> q` and `(h : p) -> q h`. See the new option `(splitImp :=
true)`.
2025-04-08 00:10:43 +00:00
George Rennie
2d8c642711 feat: allow empty clause anywhere in LRAT proof (#7859)
This PR allows the LRAT parser to accept any proof that derives the
empty clause at somepoint, not necessarily in the last line. Some tools
like lrat-trim occasionally include deletions after the derivation of
the empty clause but the proof is sound as long as it soundly derives
the empty clause somewhere.
2025-04-07 22:36:55 +00:00
Leonardo de Moura
a3b83f7ca9 feat: improve Bool normalization in grind (#7862)
This PR improves the normalization of `Bool` terms in `grind`. Recall
that `grind` currently does not case split on Boolean terms to reduce
the size of the search space.
2025-04-07 22:15:32 +00:00
Leonardo de Moura
5a849dee9b fix: grind ematch theorem activation issue (#7861)
This PR fixes an issue that prevented theorems from being activated in
`grind`.
2025-04-07 21:09:26 +00:00
Lean stage0 autoupdater
a6f4802d66 chore: update stage0 2025-04-07 15:22:09 +00:00
Sebastian Ullrich
acd6b13d76 fix: avoid blocking wait in sync task (#7852)
This PR fixes an issue where editing a Lean file may lead to a server
deadlock from threadpool starvation, especially on machines with a low
number of cores.
2025-04-07 11:46:09 +00:00
Kim Morrison
b0acdef433 chore: a failing grind test about Bool equality (#7850) 2025-04-07 07:28:28 +00:00
Kim Morrison
0f2ede45d5 chore: another failing grind test (#7848)
This PR adds another failing test case for `grind`.
2025-04-07 06:43:45 +00:00
Tobias Grosser
ab4febd1df feat: add BitVec.[toInt_append|toFin_append] (#7835)
This PR adds `BitVec.[toInt_append|toFin_append]`.

`toInt_append` states:

```lean
(x ++ y).toInt = if n == 0 then y.toInt else (2 ^ m) * x.toInt + y.toNat
```

We also add the following `Nat` theorem (derived from a corresponding
theorem `two_pow_add_eq_or_of_lt`) as it faciliates the `append` proofs:

```lean
theorem shiftLeft_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
  a <<< i + b = a <<< i ||| b
```
2025-04-07 05:50:12 +00:00
Kim Morrison
f8691bcb62 chore: remove @[simp] from @[deprecated] theorems (#7847)
This PR removes `@[simp]` from all deprecated theorems. `simp` will
still use such lemmas, without any warning message.
2025-04-07 05:49:11 +00:00
Kim Morrison
9c7f50a42c chore: deprecate Option.mem_iff (#7846)
This PR deprecates `Option.mem_iff` in favour of the identical
`Option.mem_def`.
2025-04-07 05:30:25 +00:00
Kyle Miller
cd0b54ce5d feat: tag structure instances when pp.tagAppFn is set (#7840)
This PR causes structure instance notation to be tagged with the
constructor when `pp.tagAppFns` is true. This will make docgen will have
`{` and `}` be links to the structure constructor.
2025-04-07 05:07:05 +00:00
Kim Morrison
8a373cbebe chore: add failing grind tests about decide (#7845) 2025-04-07 04:05:20 +00:00
684 changed files with 3657 additions and 1642 deletions

View File

@@ -46,7 +46,7 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
# current cache limit
CCACHE_MAXSIZE: 600M
CCACHE_MAXSIZE: 400M
# squelch error message about missing nixpkgs channel
NIX_BUILD_SHELL: bash
LSAN_OPTIONS: max_leaks=10

View File

@@ -111,7 +111,7 @@ jobs:
- name: 'Setup jq'
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: dcarbone/install-jq-action@v3.0.1
uses: dcarbone/install-jq-action@v3.1.1
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
- name: Check merge-base and nightly-testing-YYYY-MM-DD

View File

@@ -159,7 +159,7 @@ with builtins; let
dir=$(dirname $relpath)
mkdir -p $dir $out/$dir $ilean/$dir $c/$dir
if [ -d $src ]; then cp -r $src/. .; else cp $src $leanPath; fi
lean -o $out/$oleanPath -i $ilean/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
lean -o $out/$oleanPath -i $out/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
'';
}) // {
inherit deps;

View File

@@ -59,6 +59,9 @@ instance : Monad (OptionT m) where
pure := OptionT.pure
bind := OptionT.bind
instance {m : Type u Type v} [Pure m] : Inhabited (OptionT m α) where
default := pure (f:=m) default
/--
Recovers from failures. Typically used via the `<|>` operator.
-/

View File

@@ -597,7 +597,10 @@ structure Task (α : Type u) : Type u where
many tasks have finished.
`Task.map` and `Task.bind` should be preferred over `Task.get` for setting up task dependencies
where possible as they do not require temporarily growing the threadpool in this way.
where possible as they do not require temporarily growing the threadpool in this way. In
particular, calling `Task.get` in a task continuation with `(sync := true)` will panic as the
continuation is decidedly not "cheap" in this case and deadlocks may otherwise occur. The
waited-upon task should instead be returned and unwrapped using `Task.bind/IO.bindTask`.
-/
get : α
deriving Inhabited, Nonempty

View File

@@ -446,11 +446,13 @@ theorem findIdx?_eq_none_iff {xs : Array α} {p : α → Bool} :
rcases xs with xs
simp
@[simp]
theorem findIdx?_isSome {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
rcases xs with xs
simp [List.findIdx?_isSome]
@[simp]
theorem findIdx?_isNone {xs : Array α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
rcases xs with xs
@@ -584,13 +586,25 @@ theorem findFinIdx?_eq_some_iff {xs : Array α} {p : α → Bool} {i : Fin xs.si
xs.findFinIdx? p = some i
p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
Bool.not_eq_true, exists_and_left, and_exists_self, Fin.getElem_fin]
constructor
· rintro a, h, w₁, w₂, rfl
exact w₁, fun j hji => by simpa using w₂ j hji
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[simp]
theorem isSome_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by
rcases xs with xs
simp
@[simp]
theorem isNone_findFinIdx? {xs : Array α} {p : α Bool} :
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
rcases xs with xs
simp
@[simp] theorem findFinIdx?_subtype {p : α Prop} {xs : Array { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
xs.findFinIdx? f = (xs.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
@@ -636,6 +650,20 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
rcases xs with xs
simp [List.idxOf?_eq_none_iff]
@[simp]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isSome a xs := by
rcases xs with xs
simp
@[simp]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.idxOf? a).isNone = ¬ a xs := by
rcases xs with xs
simp
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
@@ -658,4 +686,16 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
rcases xs with xs
simp [List.finIdxOf?_eq_some_iff]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isSome a xs := by
rcases xs with xs
simp
@[simp]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
(xs.finIdxOf? a).isNone = ¬ a xs := by
rcases xs with xs
simp
end Array

View File

@@ -65,4 +65,20 @@ theorem swap_perm {xs : Array α} {i j : Nat} (h₁ : i < xs.size) (h₂ : j < x
simp only [swap, perm_iff_toList_perm, toList_set]
apply set_set_perm
namespace Perm
set_option linter.indexVariables false in
theorem extract {xs ys : Array α} (h : xs ~ ys) (lo hi : Nat)
(wlo : i, i < lo xs[i]? = ys[i]?) (whi : i, hi i xs[i]? = ys[i]?) :
(xs.extract lo (hi + 1)) ~ (ys.extract lo (hi + 1)) := by
rcases xs with xs
rcases ys with ys
simp_all only [perm_toArray, List.getElem?_toArray, List.extract_toArray,
List.extract_eq_drop_take]
apply List.Perm.take' (w := fun i h => by simpa using whi (lo + i) (by omega))
apply List.Perm.drop' (w := wlo)
exact h
end Perm
end Array

View File

@@ -227,6 +227,20 @@ SMT-LIB name: `bvmul`.
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
instance : Mul (BitVec n) := .mul
/--
Raises a bitvector to a natural number power. Usually accessed via the `^` operator.
Note that this is currently an inefficient implementation,
and should be replaced via an `@[extern]` with a native implementation.
See https://github.com/leanprover/lean4/issues/7887.
-/
protected def pow (x : BitVec n) (y : Nat) : BitVec n :=
match y with
| 0 => 1
| y + 1 => x.pow y * x
instance : Pow (BitVec n) Nat where
pow x y := x.pow y
/--
Unsigned division of bitvectors using the Lean convention where division by zero returns zero.
Usually accessed via the `/` operator.

View File

@@ -2660,6 +2660,63 @@ theorem msb_append {x : BitVec w} {y : BitVec v} :
rw [getElem_append] -- Why does this not work with `simp [getElem_append]`?
simp [show i < w by omega]
theorem toInt_append {x : BitVec n} {y : BitVec m} :
(x ++ y).toInt = if n == 0 then y.toInt else (2 ^ m) * x.toInt + y.toNat := by
by_cases n0 : n = 0
· subst n0
simp [BitVec.eq_nil x, BitVec.toInt]
· by_cases m0 : m = 0
· subst m0
simp [BitVec.eq_nil y, n0]
· simp only [beq_iff_eq, n0, reduceIte]
by_cases x.msb
case pos h =>
rw [toInt_eq_msb_cond]
simp only [show ((x ++ y).msb = true) by simp [msb_append, n0, h], reduceIte, toNat_append,
Nat.pow_add, Nat.shiftLeft_eq, toInt_eq_msb_cond, h]
rw_mod_cast [ Nat.shiftLeft_add_eq_or_of_lt (by omega), Nat.shiftLeft_eq, Nat.shiftLeft_eq,
Nat.mul_comm, Int.mul_sub]
norm_cast
rw [Int.natCast_add, Nat.mul_comm (n := 2 ^ n)]
omega
case neg h =>
rw [Bool.not_eq_true] at h
rw [toInt_eq_toNat_of_msb h, toInt_eq_toNat_of_msb (by simp [msb_append, n0, h])]
rw_mod_cast [toNat_append, Nat.shiftLeft_add_eq_or_of_lt (by omega), Nat.shiftLeft_eq,
Nat.mul_comm]
@[simp] theorem toInt_append_zero {n m : Nat} {x : BitVec n} :
(x ++ 0#m).toInt = (2 ^ m) * x.toInt := by
simp only [toInt_append, beq_iff_eq, toInt_zero, toNat_ofNat, Nat.zero_mod, Int.cast_ofNat_Int, Int.add_zero,
ite_eq_right_iff]
intros h
subst h
simp [BitVec.eq_nil x]
@[simp] theorem toInt_zero_append {n m : Nat} {x : BitVec n} :
(0#m ++ x).toInt = if m = 0 then x.toInt else x.toNat := by
simp [toInt_append]
/--
Show that `(x.toNat <<< n) ||| y.toNat` is within bounds of `BitVec (m + n)`.
-/
theorem toNat_shiftLeft_or_toNat_lt_two_pow_add {m n : Nat} (x : BitVec m) (y : BitVec n) :
x.toNat <<< n ||| y.toNat < 2 ^ (m + n) := by
have hnLe : 2^n 2 ^(m + n) := by
rw [Nat.pow_add]
exact Nat.le_mul_of_pos_left (2 ^ n) (Nat.two_pow_pos m)
apply Nat.or_lt_two_pow
· have := Nat.two_pow_pos n
rw [Nat.shiftLeft_eq, Nat.pow_add, Nat.mul_lt_mul_right]
<;> omega
· omega
@[simp] theorem toFin_append {x : BitVec m} {y : BitVec n} :
(x ++ y).toFin =
@Fin.mk (2^(m+n)) (x.toNat <<< n ||| y.toNat) (toNat_shiftLeft_or_toNat_lt_two_pow_add x y) := by
ext
simp
@[simp] theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = y.cast (by omega) := by
ext i ih
simp [getElem_append, show i < v by omega]
@@ -3596,6 +3653,13 @@ theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := b
@[simp, bitvec_to_nat] theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n := rfl
@[simp] theorem toFin_mul (x y : BitVec n) : (x * y).toFin = (x.toFin * y.toFin) := rfl
theorem ofNat_mul {n} (x y : Nat) : BitVec.ofNat n (x * y) = BitVec.ofNat n x * BitVec.ofNat n y := by
apply eq_of_toNat_eq
simp [BitVec.ofNat, Fin.ofNat'_mul]
theorem ofNat_mul_ofNat {n} (x y : Nat) : BitVec.ofNat n x * BitVec.ofNat n y = BitVec.ofNat n (x * y) :=
(ofNat_mul x y).symm
protected theorem mul_comm (x y : BitVec w) : x * y = y * x := by
apply eq_of_toFin_eq; simpa using Fin.mul_comm ..
instance : Std.Commutative (fun (x y : BitVec w) => x * y) := BitVec.mul_comm
@@ -3689,6 +3753,22 @@ theorem setWidth_mul (x y : BitVec w) (h : i ≤ w) :
have dvd : 2^i 2^w := Nat.pow_dvd_pow _ h
simp [bitvec_to_nat, h, Nat.mod_mod_of_dvd _ dvd]
/-! ### pow -/
@[simp]
protected theorem pow_zero {x : BitVec w} : x ^ 0 = 1#w := rfl
protected theorem pow_succ {x : BitVec w} : x ^ (n + 1) = x ^ n * x := rfl
@[simp]
protected theorem pow_one {x : BitVec w} : x ^ 1 = x := by simp [BitVec.pow_succ]
protected theorem pow_add {x : BitVec w} {n m : Nat}: x ^ (n + m) = (x ^ n) * (x ^ m):= by
induction m with
| zero => simp
| succ m ih =>
rw [ Nat.add_assoc, BitVec.pow_succ, ih, BitVec.mul_assoc, BitVec.pow_succ]
/-! ### le and lt -/
@[bitvec_to_nat] theorem le_def {x y : BitVec n} :

View File

@@ -976,6 +976,16 @@ theorem coe_sub_iff_lt {a b : Fin n} : (↑(a - b) : Nat) = n + a - b ↔ a < b
/-! ### mul -/
theorem ofNat'_mul [NeZero n] (x : Nat) (y : Fin n) :
Fin.ofNat' n x * y = Fin.ofNat' n (x * y.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.mul_def]
theorem mul_ofNat' [NeZero n] (x : Fin n) (y : Nat) :
x * Fin.ofNat' n y = Fin.ofNat' n (x.val * y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat', Fin.mul_def]
theorem val_mul {n : Nat} : a b : Fin n, (a * b).val = a.val * b.val % n
| _, _, _, _ => rfl

View File

@@ -420,6 +420,8 @@ instance : IntCast Int where intCast n := n
protected def Int.cast {R : Type u} [IntCast R] : Int R :=
IntCast.intCast
@[simp] theorem Int.cast_eq (x : Int) : Int.cast x = x := rfl
-- see the notes about coercions into arbitrary types in the module doc-string
instance [IntCast R] : CoeTail Int R where coe := Int.cast

View File

@@ -24,6 +24,11 @@ namespace Int
protected theorem exists_add_of_le {a b : Int} (h : a b) : (c : Nat), b = a + c :=
(b - a).toNat, by omega
theorem toNat_emod {x y : Int} (hx : 0 x) (hy : 0 y) :
(x % y).toNat = x.toNat % y.toNat :=
match x, y, eq_ofNat_of_zero_le hx, eq_ofNat_of_zero_le hy with
| _, _, _, rfl, _, rfl => rfl
/-! ### dvd -/
theorem dvd_antisymm {a b : Int} (H1 : 0 a) (H2 : 0 b) : a b b a a = b := by
@@ -2140,6 +2145,11 @@ theorem bmod_pos (x : Int) (m : Nat) (p : x % m < (m + 1) / 2) : bmod x m = x %
theorem bmod_neg (x : Int) (m : Nat) (p : x % m (m + 1) / 2) : bmod x m = (x % m) - m := by
simp [bmod_def, Int.not_lt.mpr p]
theorem bmod_eq_emod (x : Int) (m : Nat) : bmod x m = x % m - if x % m (m + 1) / 2 then m else 0 := by
split
· rwa [bmod_neg]
· rw [bmod_pos] <;> simp_all
@[simp]
theorem bmod_one_is_zero (x : Int) : Int.bmod x 1 = 0 := by
simp [Int.bmod]
@@ -2368,6 +2378,43 @@ theorem bmod_neg_bmod : bmod (-(bmod x n)) n = bmod (-x) n := by
apply (bmod_add_cancel_right x).mp
rw [Int.add_left_neg, add_bmod_bmod, Int.add_left_neg]
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m x) (h1 : x < m) :
(x.bmod m) < 0 (-(m / 2) x x < 0) ((m + 1) / 2 x) := by
simp only [Int.bmod_def]
by_cases xpos : 0 x
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) n) (hn : n < (m + 1) / 2) :
n.bmod m = n := by
rw [ Int.sub_eq_zero]
have := le_bmod (x := n) (m := m) (by omega)
have := bmod_lt (x := n) (m := m) (by omega)
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
omega
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n m) :
(a.bmod m).bmod n = a.bmod n := by
rw [ Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
obtain k, rfl := hnm
simp [Int.mul_assoc]
theorem bmod_eq_self_of_le_mul_two {x : Int} {y : Nat} (hle : -y x * 2) (hlt : x * 2 < y) :
x.bmod y = x := by
apply bmod_eq_self_of_le (by omega) (by omega)
theorem dvd_iff_bmod_eq_zero {a : Nat} {b : Int} : (a : Int) b b.bmod a = 0 := by
rw [dvd_iff_emod_eq_zero, bmod]
split <;> rename_i h
· rfl
· simp only [Int.not_lt] at h
match a with
| 0 => omega
| a + 1 =>
have : b % (a+1) < a + 1 := emod_lt b (by omega)
simp_all
omega
/-! Helper theorems for `dvd` simproc -/
protected theorem dvd_eq_true_of_mod_eq_zero {a b : Int} (h : b % a == 0) : (a b) = True := by

View File

@@ -377,6 +377,11 @@ theorem toNat_of_nonpos : ∀ {z : Int}, z ≤ 0 → z.toNat = 0
@[simp] theorem negSucc_add_one_eq_neg_ofNat_iff {a b : Nat} : -[a+1] + 1 = - (b : Int) a = b := by
rw [eq_comm, neg_ofNat_eq_negSucc_add_one_iff, eq_comm]
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c a = c + b := by
refine fun h => ?_, fun h => ?_ <;> subst h <;> simp
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c a = b + c := by
rw [Int.sub_eq_iff_eq_add, Int.add_comm]
/- ## add/sub injectivity -/
@[simp] protected theorem add_left_inj {i j : Int} (k : Int) : (i + k = j + k) i = j := by

View File

@@ -19,9 +19,6 @@ namespace Int
@[simp] theorem natCast_le_zero : {n : Nat} (n : Int) 0 n = 0 := by omega
protected theorem sub_eq_iff_eq_add {b a c : Int} : a - b = c a = c + b := by omega
protected theorem sub_eq_iff_eq_add' {b a c : Int} : a - b = c a = b + c := by omega
@[simp] protected theorem neg_nonpos_iff (i : Int) : -i 0 0 i := by omega
@[simp] theorem zero_le_ofNat (n : Nat) : 0 ((no_index (OfNat.ofNat n)) : Int) :=
@@ -81,15 +78,6 @@ theorem eq_ofNat_toNat {a : Int} : a = a.toNat ↔ 0 ≤ a := by omega
theorem toNat_le_toNat {n m : Int} (h : n m) : n.toNat m.toNat := by omega
theorem toNat_lt_toNat {n m : Int} (hn : 0 < m) : n.toNat < m.toNat n < m := by omega
/-! ### natAbs -/
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d n) (h₁ : n.natAbs < d.natAbs) :
n = 0 := by
obtain a, rfl := h
rw [natAbs_mul] at h₁
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
exact fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt (Nat.le_mul_of_pos_right d.natAbs h) h₁)
/-! ### min and max -/
@[simp] protected theorem min_assoc : (a b c : Int), min (min a b) c = min a (min b c) := by omega
@@ -128,33 +116,6 @@ protected theorem sub_min_sub_left (a b c : Int) : min (a - b) (a - c) = a - max
protected theorem sub_max_sub_left (a b c : Int) : max (a - b) (a - c) = a - min b c := by omega
/-! ### bmod -/
theorem bmod_neg_iff {m : Nat} {x : Int} (h2 : -m x) (h1 : x < m) :
(x.bmod m) < 0 (-(m / 2) x x < 0) ((m + 1) / 2 x) := by
simp only [Int.bmod_def]
by_cases xpos : 0 x
· rw [Int.emod_eq_of_lt xpos (by omega)]; omega
· rw [Int.add_emod_self.symm, Int.emod_eq_of_lt (by omega) (by omega)]; omega
theorem bmod_eq_self_of_le {n : Int} {m : Nat} (hn' : -(m / 2) n) (hn : n < (m + 1) / 2) :
n.bmod m = n := by
rw [ Int.sub_eq_zero]
have := le_bmod (x := n) (m := m) (by omega)
have := bmod_lt (x := n) (m := m) (by omega)
apply eq_zero_of_dvd_of_natAbs_lt_natAbs Int.dvd_bmod_sub_self
omega
theorem bmod_bmod_of_dvd {a : Int} {n m : Nat} (hnm : n m) :
(a.bmod m).bmod n = a.bmod n := by
rw [ Int.sub_eq_iff_eq_add.2 (bmod_add_bdiv a m).symm]
obtain k, rfl := hnm
simp [Int.mul_assoc]
theorem bmod_eq_self_of_le_mul_two {x : Int} {y : Nat} (hle : -y x * 2) (hlt : x * 2 < y) :
x.bmod y = x := by
apply bmod_eq_self_of_le (by omega) (by omega)
theorem mul_le_mul_of_natAbs_le {x y : Int} {s t : Nat} (hx : x.natAbs s) (hy : y.natAbs t) :
x * y s * t := by
by_cases 0 < s 0 < t

View File

@@ -1827,6 +1827,14 @@ theorem eq_def (ctx : Context) (x : Var) (xPoly : Poly) (p : Poly)
simp [eq_def_cert]; intro _ h; subst p; simp [h]
rw [ Int.sub_eq_add_neg, Int.sub_self]
def eq_def'_cert (x : Var) (e : Expr) (p : Poly) : Bool :=
p == .add (-1) x e.norm
theorem eq_def' (ctx : Context) (x : Var) (e : Expr) (p : Poly)
: eq_def'_cert x e p x.denote ctx = e.denote ctx p.denote' ctx = 0 := by
simp [eq_def'_cert]; intro _ h; subst p; simp [h]
rw [ Int.sub_eq_add_neg, Int.sub_self]
end Int.Linear
theorem Int.not_le_eq (a b : Int) : (¬a b) = (b + 1 a) := by

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
prelude
import Init.Data.Int.Lemmas
import Init.Data.Int.DivMod
import Init.Data.Int.Linear
import Init.Data.RArray
namespace Int.OfNat
@@ -47,6 +48,9 @@ def Expr.denoteAsInt (ctx : Context) : Expr → Int
theorem Expr.denoteAsInt_eq (ctx : Context) (e : Expr) : e.denoteAsInt ctx = e.denote ctx := by
induction e <;> simp [denote, denoteAsInt, Int.ofNat_ediv, *] <;> rfl
theorem Expr.eq_denoteAsInt (ctx : Context) (e : Expr) : e.denote ctx = e.denoteAsInt ctx := by
apply Eq.symm; apply denoteAsInt_eq
theorem Expr.eq (ctx : Context) (lhs rhs : Expr)
: (lhs.denote ctx = rhs.denote ctx) = (lhs.denoteAsInt ctx = rhs.denoteAsInt ctx) := by
simp [denoteAsInt_eq, Int.ofNat_inj]

View File

@@ -562,6 +562,16 @@ theorem natAbs_sub_of_nonneg_of_le {a b : Int} (h₁ : 0 ≤ b) (h₂ : b ≤ a)
· rwa [ Int.ofNat_le, natAbs_of_nonneg h₁, natAbs_of_nonneg (Int.le_trans h₁ h₂)]
· exact Int.sub_nonneg_of_le h₂
theorem eq_zero_of_dvd_of_natAbs_lt_natAbs {d n : Int} (h : d n) (h₁ : n.natAbs < d.natAbs) :
n = 0 := by
let a, ha := h
subst ha
rw [natAbs_mul] at h₁
suffices ¬ 0 < a.natAbs by simp [Int.natAbs_eq_zero.1 (Nat.eq_zero_of_not_pos this)]
refine fun h => Nat.lt_irrefl _ (Nat.lt_of_le_of_lt ?_ h₁)
rw (occs := [1]) [ Nat.mul_one d.natAbs]
exact Nat.mul_le_mul (Nat.le_refl _) h
/-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0
@@ -599,6 +609,18 @@ theorem toNat_add {a b : Int} (ha : 0 ≤ a) (hb : 0 ≤ b) : (a + b).toNat = a.
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
| _, _, _, rfl, _, rfl => rfl
theorem toNat_mul {a b : Int} (ha : 0 a) (hb : 0 b) : (a * b).toNat = a.toNat * b.toNat :=
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
| _, _, _, rfl, _, rfl => rfl
/--
Variant of `Int.toNat_sub` taking non-negativity hypotheses,
rather than expecting the arguments to be casts of natural numbers.
-/
theorem toNat_sub'' {a b : Int} (ha : 0 a) (hb : 0 b) : (a - b).toNat = a.toNat - b.toNat :=
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
| _, _, _, rfl, _, rfl => toNat_sub _ _
theorem toNat_add_nat {a : Int} (ha : 0 a) (n : Nat) : (a + n).toNat = a.toNat + n :=
match a, eq_ofNat_of_zero_le ha with | _, _, rfl => rfl

View File

@@ -334,7 +334,7 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
@[simp] theorem head?_attachWith {P : α Prop} {xs : List α}
(H : (a : α), a xs P a) :
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some a, H _ (mem_of_mem_head? h)) := by
(xs.attachWith P H).head? = xs.head?.pbind (fun a h => some a, H _ (mem_of_head? h)) := by
cases xs <;> simp_all
@[simp] theorem head_attachWith {P : α Prop} {xs : List α}
@@ -345,7 +345,7 @@ theorem getElem_attach {xs : List α} {i : Nat} (h : i < xs.attach.length) :
| cons x xs => simp [head_attachWith, h]
@[simp] theorem head?_attach {xs : List α} :
xs.attach.head? = xs.head?.pbind (fun a h => some a, mem_of_mem_head? h) := by
xs.attach.head? = xs.head?.pbind (fun a h => some a, mem_of_head? h) := by
cases xs <;> simp_all
@[simp] theorem head_attach {xs : List α} (h) :
@@ -470,20 +470,19 @@ theorem attach_filterMap {l : List α} {f : α → Option β} :
| cons x xs ih =>
simp only [filterMap_cons, attach_cons, ih, filterMap_map]
split <;> rename_i h
· simp only [Option.pbind_eq_none_iff, reduceCtorEq, Option.mem_def, exists_false,
· simp only [Option.pbind_eq_none_iff, reduceCtorEq, exists_false,
or_false] at h
rw [attach_congr]
rotate_left
· simp only [h]
rfl
rw [ih]
simp only [map_filterMap, Option.map_pbind, Option.map_some']
simp only [map_filterMap, Option.map_pbind, Option.map_some]
rfl
· simp only [Option.pbind_eq_some_iff] at h
obtain a, h, w := h
simp only [Option.some.injEq] at w
subst w
simp only [Option.mem_def] at h
rw [attach_congr]
rotate_left
· simp only [h]

View File

@@ -2048,10 +2048,10 @@ def sum {α} [Add α] [Zero α] : List αα :=
protected def _root_.Nat.sum (l : List Nat) : Nat := l.foldr (·+·) 0
set_option linter.deprecated false in
@[simp, deprecated sum_nil (since := "2024-10-17")]
@[deprecated sum_nil (since := "2024-10-17")]
theorem _root_.Nat.sum_nil : Nat.sum ([] : List Nat) = 0 := rfl
set_option linter.deprecated false in
@[simp, deprecated sum_cons (since := "2024-10-17")]
@[deprecated sum_cons (since := "2024-10-17")]
theorem _root_.Nat.sum_cons (a : Nat) (l : List Nat) :
Nat.sum (a::l) = a + Nat.sum l := rfl

View File

@@ -95,10 +95,10 @@ theorem findSome?_eq_some_iff {f : α → Option β} {l : List α} {b : β} :
| cons x xs ih =>
simp [guard, findSome?, find?]
split <;> rename_i h
· simp only [Option.guard_eq_some] at h
· simp only [Option.guard_eq_some_iff] at h
obtain rfl, h := h
simp [h]
· simp only [Option.guard_eq_none] at h
· simp only [Option.guard_eq_none_iff] at h
simp [ih, h]
theorem find?_eq_findSome?_guard {l : List α} : find? p l = findSome? (Option.guard fun x => p x) l :=
@@ -700,6 +700,7 @@ theorem findIdx?_eq_none_iff {xs : List α} {p : α → Bool} :
simp only [findIdx?_cons]
split <;> simp_all [cond_eq_if]
@[simp]
theorem findIdx?_isSome {xs : List α} {p : α Bool} :
(xs.findIdx? p).isSome = xs.any p := by
induction xs with
@@ -708,6 +709,7 @@ theorem findIdx?_isSome {xs : List α} {p : α → Bool} :
simp only [findIdx?_cons]
split <;> simp_all
@[simp]
theorem findIdx?_isNone {xs : List α} {p : α Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
induction xs with
@@ -768,7 +770,7 @@ theorem findIdx?_eq_some_iff_getElem {xs : List α} {p : α → Bool} {i : Nat}
not_and, Classical.not_forall, Bool.not_eq_false]
intros
refine 0, zero_lt_succ i, _
· simp only [Option.map_eq_some', ih, Bool.not_eq_true, length_cons]
· simp only [Option.map_eq_some_iff, ih, Bool.not_eq_true, length_cons]
constructor
· rintro a, h, h₁, h₂, rfl
refine Nat.succ_lt_succ_iff.mpr h, by simpa, fun j hj => ?_
@@ -824,7 +826,7 @@ abbrev findIdx?_of_eq_none := @of_findIdx?_eq_none
(xs ++ ys : List α).findIdx? p =
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.length) := by
induction xs with simp
| cons _ _ _ => split <;> simp_all [Option.map_or', Option.map_map]; rfl
| cons _ _ _ => split <;> simp_all [Option.map_or, Option.map_map]; rfl
theorem findIdx?_flatten {l : List (List α)} {p : α Bool} :
l.flatten.findIdx? p =
@@ -977,13 +979,31 @@ theorem findFinIdx?_eq_some_iff {xs : List α} {p : α → Bool} {i : Fin xs.len
xs.findFinIdx? p = some i
p xs[i] j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
Bool.not_eq_true, Option.mem_def, exists_and_left, and_exists_self, Fin.getElem_fin]
Bool.not_eq_true, exists_and_left, and_exists_self, Fin.getElem_fin]
constructor
· rintro a, h, w₁, w₂, rfl
exact w₁, fun j hji => by simpa using w₂ j hji
· rintro h, w
exact i, i.2, h, fun j hji => w j, by omega hji, rfl
@[simp]
theorem isSome_findFinIdx? {l : List α} {p : α Bool} :
(l.findFinIdx? p).isSome = l.any p := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findFinIdx?_cons]
split <;> simp_all
@[simp]
theorem isNone_findFinIdx? {l : List α} {p : α Bool} :
(l.findFinIdx? p).isNone = l.all (fun x => ¬ p x) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findFinIdx?_cons]
split <;> simp_all
@[simp] theorem findFinIdx?_subtype {p : α Prop} {l : List { x // p x }}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
@@ -1084,6 +1104,24 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
l.finIdxOf? a = some i l[i] = a j (_ : j < i), ¬l[j] = a := by
simp only [finIdxOf?, findFinIdx?_eq_some_iff, beq_iff_eq]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isSome a l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [finIdxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
@[simp]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isNone = ¬ a l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [finIdxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
/-! ### idxOf?
The verification API for `idxOf?` is still incomplete.
@@ -1109,6 +1147,25 @@ theorem idxOf?_cons [BEq α] {a : α} {xs : List α} {b : α} :
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
@[simp]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isSome a l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [idxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
@[simp]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isNone = ¬ a l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [idxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
/-! ### lookup -/
section lookup

View File

@@ -848,7 +848,9 @@ theorem getLast!_cons_eq_getLastD [Inhabited α] : @getLast! α _ (a::l) = getLa
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
theorem getLast_mem_getLast? : {l : List α} (h : l []), getLast l h getLast? l
| [], h => by contradiction
| _ :: _, _ => rfl
theorem getLast?_eq_some_getLast : {l : List α} (h : l []), getLast? l = some (getLast l h)
| _ :: _, _ => rfl
theorem getLastD_mem_cons : {l : List α} {a : α}, getLastD l a a::l
@@ -964,17 +966,16 @@ abbrev head?_isSome := @isSome_head?
| [], h => absurd rfl h
| _::_, _ => .head ..
theorem mem_of_mem_head? : {l : List α} {a : α}, a l.head? a l := by
intro l a h
cases l with
| nil => simp at h
| cons b l =>
simp at h
cases h
exact mem_cons_self
theorem mem_of_head? : {l : List α} {a : α} l.head? = some a a l
| _::_, _, h => Option.some.inj h mem_cons_self
theorem mem_of_mem_head? : {l : List α} {a : α}, a l.head? a l :=
mem_of_head?
theorem head_mem_head? : {l : List α} (h : l []), head l h head? l
| [], h => by contradiction
| _ :: _, _ => rfl
theorem head?_eq_some_head : {l : List α} (h : l []), head? l = some (head l h)
| _ :: _, _ => rfl
theorem head?_concat {a : α} : (l ++ [a]).head? = l.head?.getD a := by
@@ -983,11 +984,13 @@ theorem head?_concat {a : α} : (l ++ [a]).head? = l.head?.getD a := by
theorem head?_concat_concat : (l ++ [a, b]).head? = (l ++ [a]).head? := by
cases l <;> simp
theorem head_of_head?_eq_some {l : List α} {x} (hx : l.head? = some x) :
l.head (ne_nil_of_mem (mem_of_head? hx)) = x := by
rw [ Option.some_inj, head?_eq_some_head, hx]
theorem head_of_mem_head? {l : List α} {x} (hx : x l.head?) :
l.head (ne_nil_of_mem (mem_of_mem_head? hx)) = x := by
cases l
· contradiction
· simpa using hx
l.head (ne_nil_of_mem (mem_of_mem_head? hx)) = x :=
head_of_head?_eq_some hx
/-! ### headD -/
@@ -2151,7 +2154,7 @@ theorem replicate_succ' : replicate (n + 1) a = replicate n a ++ [a] := by
| 0 => by simp
| n+1 => by simp [replicate_succ, mem_replicate, Nat.succ_ne_zero]
@[simp, deprecated mem_replicate (since := "2024-09-05")]
@[deprecated mem_replicate (since := "2024-09-05")]
theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
(replicate n b).contains a = (a == b && !n == 0) := by
induction n with
@@ -2160,7 +2163,7 @@ theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
simp only [replicate_succ, elem_cons]
split <;> simp_all
@[simp, deprecated mem_replicate (since := "2024-09-05")]
@[deprecated mem_replicate (since := "2024-09-05")]
theorem decide_mem_replicate [BEq α] [LawfulBEq α] {a b : α} :
{n}, decide (b replicate n a) = ((¬ n == 0) && b == a)
| 0 => by simp
@@ -2469,18 +2472,19 @@ theorem getLast?_eq_head?_reverse {xs : List α} : xs.getLast? = xs.reverse.head
theorem head?_eq_getLast?_reverse {xs : List α} : xs.head? = xs.reverse.getLast? := by
simp
theorem mem_of_mem_getLast? {l : List α} {a : α} (h : a getLast? l) : a l := by
rw [getLast?_eq_head?_reverse] at h
rw [ mem_reverse]
exact mem_of_mem_head? h
theorem mem_of_getLast? {l : List α} {a : α} (h : getLast? l = some a) : a l :=
mem_reverse.1 (mem_of_head? (getLast?_eq_head?_reverse h))
theorem mem_of_mem_getLast? {l : List α} {a : α} (h : a getLast? l) : a l :=
mem_of_getLast? h
theorem getLast_of_getLast?_eq_some {l : List α} (hx : l.getLast? = some x) :
l.getLast (ne_nil_of_mem (mem_of_getLast? hx)) = x := by
rw [ Option.some_inj, getLast?_eq_some_getLast, hx]
theorem getLast_of_mem_getLast? {l : List α} (hx : x l.getLast?) :
l.getLast (ne_nil_of_mem (mem_of_mem_getLast? hx)) = x := by
rw [Option.mem_def] at hx
cases l
· contradiction
· rw [ Option.some_inj, hx]
rfl
l.getLast (ne_nil_of_mem (mem_of_mem_getLast? hx)) = x :=
getLast_of_getLast?_eq_some hx
@[simp] theorem map_reverse {f : α β} {l : List α} : l.reverse.map f = (l.map f).reverse := by
induction l <;> simp [*]
@@ -2860,10 +2864,6 @@ theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔
rw [getLast?_eq_head?_reverse, isSome_head?]
simp
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a xs := by
obtain ys, rfl := getLast?_eq_some_iff.1 h
exact mem_concat_self
@[deprecated mem_of_getLast? (since := "2024-10-21")] abbrev mem_of_getLast?_eq_some := @mem_of_getLast?
@[simp] theorem getLast_reverse {l : List α} (h : l.reverse []) :

View File

@@ -339,7 +339,7 @@ theorem getElem?_mapIdx_go : ∀ {l : List α} {acc : Array β} {i : Nat},
if h : i < acc.size then some acc[i] else Option.map (f i) l[i - acc.size]?
| [], acc, i => by
simp only [mapIdx.go, Array.toListImpl_eq, getElem?_def, Array.length_toList,
Array.getElem_toList, length_nil, Nat.not_lt_zero, reduceDIte, Option.map_none']
Array.getElem_toList, length_nil, Nat.not_lt_zero, reduceDIte, Option.map_none]
| a :: l, acc, i => by
rw [mapIdx.go, getElem?_mapIdx_go]
simp only [Array.size_push]

View File

@@ -54,4 +54,23 @@ theorem set_set_perm {as : List α} {i j : Nat} (h₁ : i < as.length) (h₂ : j
subst t
apply set_set_perm' _ _ (by omega)
namespace Perm
/-- Variant of `List.Perm.take` specifying the the permutation is constant after `i` elementwise. -/
theorem take' {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} (w : j, i j l₁[j]? = l₂[j]?) :
(l₁.take i) ~ (l₂.take i) := by
apply h.take
ext1 j
simpa using w (i + j) (by omega)
/-- Variant of `List.Perm.drop` specifying the the permutation is constant before `i` elementwise. -/
theorem drop' {l₁ l₂ : List α} (h : l₁ ~ l₂) {i : Nat} (w : j, j < i l₁[j]? = l₂[j]?) :
(l₁.drop i) ~ (l₂.drop i) := by
apply h.drop
ext1
simp only [getElem?_take]
split <;> simp_all
end Perm
end List

View File

@@ -239,7 +239,7 @@ dropping the first `i` elements. Version designed to rewrite from the small list
@[simp]
theorem getElem?_drop {xs : List α} {i j : Nat} : (xs.drop i)[j]? = xs[i + j]? := by
ext
simp only [getElem?_eq_some_iff, getElem_drop, Option.mem_def]
simp only [getElem?_eq_some_iff, getElem_drop]
constructor <;> intro h, ha
· exact _, ha
· refine ?_, ha

View File

@@ -118,9 +118,8 @@ theorem Pairwise.map {S : β → β → Prop} (f : α → β) (H : ∀ a b : α,
pairwise_map.2 <| p.imp (H _ _)
theorem pairwise_filterMap {f : β Option α} {l : List β} :
Pairwise R (filterMap f l) Pairwise (fun a a' : β => b f a, b' f a', R b b') l := by
let _S (a a' : β) := b f a, b' f a', R b b'
simp only [Option.mem_def]
Pairwise R (filterMap f l) Pairwise (fun a a' : β => b, f a = some b b', f a' = some b' R b b') l := by
let _S (a a' : β) := b, f a = some b b', f a' = some b' R b b'
induction l with
| nil => simp only [filterMap, Pairwise.nil]
| cons a l IH => ?_
@@ -134,7 +133,7 @@ theorem pairwise_filterMap {f : β → Option α} {l : List β} :
fun h a ha b hab => h _ _ ha hab, fun h a b ha hab => h _ ha _ hab
theorem Pairwise.filterMap {S : β β Prop} (f : α Option β)
(H : a a' : α, R a a' b f a, b' f a', S b b') {l : List α} (p : Pairwise R l) :
(H : a a' : α, R a a' b, f a = some b b', f a' = some b' S b b') {l : List α} (p : Pairwise R l) :
Pairwise S (filterMap f l) :=
pairwise_filterMap.2 <| p.imp (H _ _)

View File

@@ -536,4 +536,16 @@ theorem perm_insertIdx {α} (x : α) (l : List α) {i} (h : i ≤ l.length) :
simp only [insertIdx, modifyTailIdx]
refine .trans (.cons _ (ih (Nat.le_of_succ_le_succ h))) (.swap ..)
namespace Perm
theorem take {l₁ l₂ : List α} (h : l₁ ~ l₂) {n : Nat} (w : l₁.drop n = l₂.drop n) :
(l₁.take n) ~ (l₂.take n) := by
rwa [ List.take_append_drop n l₁, List.take_append_drop n l₂, w, perm_append_right_iff] at h
theorem drop {l₁ l₂ : List α} (h : l₁ ~ l₂) {n : Nat} (w : l₂.take n = l₁.take n) :
(l₁.drop n) ~ (l₂.drop n) := by
rwa [ List.take_append_drop n l₁, List.take_append_drop n l₂, w, perm_append_left_iff] at h
end Perm
end List

View File

@@ -816,6 +816,11 @@ theorem le_shiftLeft {a b : Nat} : a ≤ a <<< b :=
theorem lt_of_shiftLeft_lt {a b c : Nat} (h : a <<< b < c) : a < c :=
Nat.lt_of_le_of_lt le_shiftLeft h
theorem shiftLeft_add_eq_or_of_lt {b : Nat} (b_lt : b < 2^i) (a : Nat) :
a <<< i + b = a <<< i ||| b := by
rw [shiftLeft_eq, Nat.mul_comm]
rw [two_pow_add_eq_or_of_lt b_lt]
/-! ### le -/
theorem le_of_testBit {n m : Nat} (h : i, n.testBit i = true m.testBit i = true) : n m := by

View File

@@ -16,7 +16,7 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
`Option {x // P x}` is the same as the input `Option α`.
-/
@[inline] private unsafe def attachWithImpl
(o : Option α) (P : α Prop) (_ : x o, P x) : Option {x // P x} := unsafeCast o
(o : Option α) (P : α Prop) (_ : x, o = some x P x) : Option {x // P x} := unsafeCast o
/--
“Attaches” a proof that some predicate holds for an optional value, if present, returning a subtype
@@ -28,10 +28,10 @@ value drawn from a parameter is smaller than the parameter. This allows the well
mechanism to prove that the function terminates.
-/
@[implemented_by attachWithImpl] def attachWith
(xs : Option α) (P : α Prop) (H : x xs, P x) : Option {x // P x} :=
(xs : Option α) (P : α Prop) (H : x, xs = some x P x) : Option {x // P x} :=
match xs with
| none => none
| some x => some x, H x (mem_some_self x)
| some x => some x, H x rfl
/--
“Attaches” a proof that an optional value, if present, is indeed this value, returning a subtype
@@ -42,14 +42,14 @@ operators (such as `Option.map`) to prove that an optional value drawn from a pa
than the parameter. This allows the well-founded recursion mechanism to prove that the function
terminates.
-/
@[inline] def attach (xs : Option α) : Option {x // x xs} := xs.attachWith _ fun _ => id
@[inline] def attach (xs : Option α) : Option {x // xs = some x} := xs.attachWith _ fun _ => id
@[simp] theorem attach_none : (none : Option α).attach = none := rfl
@[simp] theorem attachWith_none : (none : Option α).attachWith P H = none := rfl
@[simp] theorem attach_some {x : α} :
(some x).attach = some x, rfl := rfl
@[simp] theorem attachWith_some {x : α} {P : α Prop} (h : (b : α), b some x P b) :
@[simp] theorem attachWith_some {x : α} {P : α Prop} (h : (b : α), some x = some b P b) :
(some x).attachWith P h = some x, by simpa using h := rfl
theorem attach_congr {o₁ o₂ : Option α} (h : o₁ = o₂) :
@@ -57,13 +57,13 @@ theorem attach_congr {o₁ o₂ : Option α} (h : o₁ = o₂) :
subst h
simp
theorem attachWith_congr {o₁ o₂ : Option α} (w : o₁ = o₂) {P : α Prop} {H : x o₁, P x} :
theorem attachWith_congr {o₁ o₂ : Option α} (w : o₁ = o₂) {P : α Prop} {H : x, o₁ = some x P x} :
o₁.attachWith P H = o₂.attachWith P fun _ h => H _ (w h) := by
subst w
simp
theorem attach_map_val (o : Option α) (f : α β) :
(o.attach.map fun (i : {i // i o}) => f i) = o.map f := by
(o.attach.map fun (i : {i // o = some i}) => f i) = o.map f := by
cases o <;> simp
@[deprecated attach_map_val (since := "2025-02-17")]
@@ -73,47 +73,50 @@ theorem attach_map_subtype_val (o : Option α) :
o.attach.map Subtype.val = o :=
(attach_map_val _ _).trans (congrFun Option.map_id _)
theorem attachWith_map_val {p : α Prop} (f : α β) (o : Option α) (H : a o, p a) :
theorem attachWith_map_val {p : α Prop} (f : α β) (o : Option α) (H : a, o = some a p a) :
((o.attachWith p H).map fun (i : { i // p i}) => f i.val) = o.map f := by
cases o <;> simp [H]
@[deprecated attachWith_map_val (since := "2025-02-17")]
abbrev attachWith_map_coe := @attachWith_map_val
theorem attachWith_map_subtype_val {p : α Prop} (o : Option α) (H : a o, p a) :
theorem attachWith_map_subtype_val {p : α Prop} (o : Option α) (H : a, o = some a p a) :
(o.attachWith p H).map Subtype.val = o :=
(attachWith_map_val _ _ _).trans (congrFun Option.map_id _)
theorem mem_attach : (o : Option α) (x : {x // x o}), x o.attach
theorem attach_eq_some : (o : Option a) (x : {x // o = some x}), o.attach = some x
| none, x, h => by simp at h
| some a, x, h => by simpa using h
theorem mem_attach : (o : Option α) (x : {x // o = some x}), x o.attach :=
attach_eq_some
@[simp] theorem isNone_attach (o : Option α) : o.attach.isNone = o.isNone := by
cases o <;> simp
@[simp] theorem isNone_attachWith {p : α Prop} (o : Option α) (H : a o, p a) :
@[simp] theorem isNone_attachWith {p : α Prop} (o : Option α) (H : a, o = some a p a) :
(o.attachWith p H).isNone = o.isNone := by
cases o <;> simp
@[simp] theorem isSome_attach (o : Option α) : o.attach.isSome = o.isSome := by
cases o <;> simp
@[simp] theorem isSome_attachWith {p : α Prop} (o : Option α) (H : a o, p a) :
@[simp] theorem isSome_attachWith {p : α Prop} (o : Option α) (H : a, o = some a p a) :
(o.attachWith p H).isSome = o.isSome := by
cases o <;> simp
@[simp] theorem attach_eq_none_iff {o : Option α} : o.attach = none o = none := by
cases o <;> simp
@[simp] theorem attach_eq_some_iff {o : Option α} {x : {x // x o}} :
@[simp] theorem attach_eq_some_iff {o : Option α} {x : {x // o = some x}} :
o.attach = some x o = some x.val := by
cases o <;> cases x <;> simp
@[simp] theorem attachWith_eq_none_iff {p : α Prop} {o : Option α} (H : a o, p a) :
@[simp] theorem attachWith_eq_none_iff {p : α Prop} {o : Option α} (H : a, o = some a p a) :
o.attachWith p H = none o = none := by
cases o <;> simp
@[simp] theorem attachWith_eq_some_iff {p : α Prop} {o : Option α} (H : a o, p a) {x : {x // p x}} :
@[simp] theorem attachWith_eq_some_iff {p : α Prop} {o : Option α} (H : a, o = some a p a) {x : {x // p x}} :
o.attachWith p H = some x o = some x.val := by
cases o <;> cases x <;> simp
@@ -123,7 +126,7 @@ theorem mem_attach : ∀ (o : Option α) (x : {x // x ∈ o}), x ∈ o.attach
· simp at h
· simp [get_some]
@[simp] theorem get_attachWith {p : α Prop} {o : Option α} (H : a o, p a) (h : (o.attachWith p H).isSome) :
@[simp] theorem get_attachWith {p : α Prop} {o : Option α} (H : a, o = some a p a) (h : (o.attachWith p H).isSome) :
(o.attachWith p H).get h = o.get (by simpa using h), H _ (by simp) := by
cases o
· simp at h
@@ -138,47 +141,47 @@ theorem toList_attach (o : Option α) :
cases o <;> simp
theorem attach_map {o : Option α} (f : α β) :
(o.map f).attach = o.attach.map (fun x, h => f x, mem_map_of_mem f h) := by
(o.map f).attach = o.attach.map (fun x, h => f x, map_eq_some_iff.2 _, h, rfl) := by
cases o <;> simp
theorem attachWith_map {o : Option α} (f : α β) {P : β Prop} {H : (b : β), b o.map f P b} :
(o.map f).attachWith P H = (o.attachWith (P f) (fun _ h => H _ (mem_map_of_mem f h))).map
theorem attachWith_map {o : Option α} (f : α β) {P : β Prop} {H : (b : β), o.map f = some b P b} :
(o.map f).attachWith P H = (o.attachWith (P f) (fun _ h => H _ (map_eq_some_iff.2 _, h, rfl))).map
fun x, h => f x, h := by
cases o <;> simp
theorem map_attach_eq_pmap {o : Option α} (f : { x // x o } β) :
o.attach.map f = o.pmap (fun a (h : a o) => f a, h) (fun _ h => h) := by
theorem map_attach_eq_pmap {o : Option α} (f : { x // o = some x } β) :
o.attach.map f = o.pmap (fun a (h : o = some a) => f a, h) (fun _ h => h) := by
cases o <;> simp
@[deprecated map_attach_eq_pmap (since := "2025-02-09")]
abbrev map_attach := @map_attach_eq_pmap
@[simp] theorem map_attachWith {l : Option α} {P : α Prop} {H : (a : α), a l P a}
@[simp] theorem map_attachWith {l : Option α} {P : α Prop} {H : (a : α), l = some a P a}
(f : { x // P x } β) :
(l.attachWith P H).map f = l.attach.map fun x, h => f x, H _ h := by
cases l <;> simp_all
theorem map_attachWith_eq_pmap {o : Option α} {P : α Prop} {H : (a : α), a o P a}
theorem map_attachWith_eq_pmap {o : Option α} {P : α Prop} {H : (a : α), o = some a P a}
(f : { x // P x } β) :
(o.attachWith P H).map f =
o.pmap (fun a (h : a o P a) => f a, h.2) (fun a h => h, H a h) := by
o.pmap (fun a (h : o = some a P a) => f a, h.2) (fun a h => h, H a h) := by
cases o <;> simp
@[simp]
theorem map_attach_eq_attachWith {o : Option α} {p : α Prop} (f : a, a o p a) :
theorem map_attach_eq_attachWith {o : Option α} {p : α Prop} (f : a, o = some a p a) :
o.attach.map (fun x => x.1, f x.1 x.2) = o.attachWith p f := by
cases o <;> simp_all [Function.comp_def]
theorem attach_bind {o : Option α} {f : α Option β} :
(o.bind f).attach =
o.attach.bind fun x, h => (f x).attach.map fun y, h' => y, mem_bind_iff.mpr x, h, h' := by
o.attach.bind fun x, h => (f x).attach.map fun y, h' => y, bind_eq_some_iff.2 _, h, h' := by
cases o <;> simp
theorem bind_attach {o : Option α} {f : {x // x o} Option β} :
theorem bind_attach {o : Option α} {f : {x // o = some x} Option β} :
o.attach.bind f = o.pbind fun a h => f a, h := by
cases o <;> simp
theorem pbind_eq_bind_attach {o : Option α} {f : (a : α) a o Option β} :
theorem pbind_eq_bind_attach {o : Option α} {f : (a : α) o = some a Option β} :
o.pbind f = o.attach.bind fun x, h => f x h := by
cases o <;> simp
@@ -190,7 +193,7 @@ theorem attach_filter {o : Option α} {p : α → Bool} :
| some a =>
simp only [filter_some, attach_some]
ext
simp only [mem_def, attach_eq_some_iff, ite_none_right_eq_some, some.injEq, some_bind,
simp only [attach_eq_some_iff, ite_none_right_eq_some, some.injEq, some_bind,
dite_none_right_eq_some]
constructor
· rintro h, w
@@ -198,7 +201,7 @@ theorem attach_filter {o : Option α} {p : α → Bool} :
· rintro h, rfl
simp [h]
theorem filter_attach {o : Option α} {p : {x // x o} Bool} :
theorem filter_attach {o : Option α} {p : {x // o = some x} Bool} :
o.attach.filter p = o.pbind fun a h => if p a, h then some a, h else none := by
cases o <;> simp [filter_some]
@@ -242,7 +245,7 @@ def unattach {α : Type _} {p : α → Prop} (o : Option { x // p x }) := o.map
cases o <;> simp
@[simp] theorem unattach_attachWith {p : α Prop} {o : Option α}
{H : a o, p a} :
{H : a, o = some a p a} :
(o.attachWith p H).unattach = o := by
cases o <;> simp

View File

@@ -94,13 +94,20 @@ Runs a monadic function `f` on an optional value, returning the result. If the o
From the perspective of `Option` as a container with at most one element, this is analogous to
`List.mapM`, returning the result of running the monadic function on all elements of the container.
`Option.mapA` is the corresponding operation for applicative functors.
This function only requires `m` to be an applicative functor. An alias `Option.mapA` is provided.
-/
@[inline] protected def mapM [Monad m] (f : α m β) (o : Option α) : m (Option β) := do
if let some a := o then
return some ( f a)
else
return none
@[inline] protected def mapM [Applicative m] (f : α m β) : Option α m (Option β)
| none => pure none
| some x => some <$> f x
/--
Applies a function in some applicative functor to an optional value, returning `none` with no
effects if the value is missing.
This is an alias for `Option.mapM`, which already works for applicative functors.
-/
@[inline] protected def mapA [Applicative m] (f : α m β) : Option α m (Option β) :=
Option.mapM f
theorem map_id : (Option.map id : Option α Option α) = id :=
funext (fun o => match o with | none => rfl | some _ => rfl)
@@ -209,33 +216,12 @@ The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise,
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
both are `none`, then the value is `none`.
Examples:
* `Option.zipWith (· + ·) none (some 3) = some 3`
* `Option.zipWith (· + ·) (some 2) (some 3) = some 5`
* `Option.zipWith (· + ·) (some 2) none = some 2`
* `Option.zipWith (· + ·) none none = none`
-/
def zipWith (fn : α α α) : Option α Option α Option α
| none , none => none
| some x, none => some x
| none , some y => some y
| some x, some y => some <| fn x y
/--
Applies a function to a two optional values if both are present. Otherwise, if one value is present,
it is returned and the function is not used.
The value is `some (fn a b)` if the inputs are `some a` and `some b`. Otherwise, the behavior is
equivalent to `Option.orElse`: if only one input is `some x`, then the value is `some x`, and if
both are `none`, then the value is `none`.
Examples:
* `Option.merge (· + ·) none (some 3) = some 3`
* `Option.merge (· + ·) (some 2) (some 3) = some 5`
* `Option.merge (· + ·) (some 2) none = some 2`
* `Option.merge (· + ·) none none = none`
-/
@[deprecated zipWith (since := "2025-04-04")]
def merge (fn : α α α) : Option α Option α Option α
| none , none => none
| some x, none => some x
@@ -245,13 +231,12 @@ def merge (fn : ααα) : Option α → Option α → Option α
@[simp] theorem getD_none : getD none a = a := rfl
@[simp] theorem getD_some : getD (some a) b = a := rfl
@[simp] theorem map_none' (f : α β) : none.map f = none := rfl
@[simp] theorem map_some' (a) (f : α β) : (some a).map f = some (f a) := rfl
@[simp] theorem map_none (f : α β) : none.map f = none := rfl
@[simp] theorem map_some (a) (f : α β) : (some a).map f = some (f a) := rfl
@[simp] theorem none_bind (f : α Option β) : none.bind f = none := rfl
@[simp] theorem some_bind (a) (f : α Option β) : (some a).bind f = f a := rfl
/--
A case analysis function for `Option`.
@@ -328,7 +313,7 @@ Examples:
* `Option.liftOrGet (· + ·) (some 2) none = some 2`
* `Option.liftOrGet (· + ·) none none = none`
-/
@[deprecated zipWith (since := "2025-04-04")]
@[deprecated merge (since := "2025-04-04")]
def liftOrGet (f : α α α) : Option α Option α Option α
| none, none => none
| some a, none => some a
@@ -355,19 +340,11 @@ Examples:
-/
@[simp, inline] def join (x : Option (Option α)) : Option α := x.bind id
/--
Applies a function in some applicative functor to an optional value, returning `none` with no
effects if the value is missing.
This is analogous to `Option.mapM` for monads.
-/
@[inline] protected def mapA [Applicative m] {α β} (f : α m β) : Option α m (Option β)
| none => pure none
| some x => some <$> f x
/--
Converts an optional monadic computation into a monadic computation of an optional value.
This function only requires `m` to be an applicative functor.
Example:
```lean example
#eval show IO (Option String) from
@@ -382,11 +359,10 @@ hello
some "world"
```
-/
@[inline] def sequence [Monad m] {α : Type u} : Option (m α) m (Option α)
@[inline] def sequence [Applicative m] {α : Type u} : Option (m α) m (Option α)
| none => pure none
| some fn => some <$> fn
/--
A monadic case analysis function for `Option`.
@@ -405,8 +381,7 @@ Gets the value in an option, monadically computing a default value on `none`.
This is the monadic analogue of `Option.getD`.
-/
@[inline] def getDM [Monad m] (x : Option α) (y : m α) : m α :=
@[inline] def getDM [Pure m] (x : Option α) (y : m α) : m α :=
match x with
| some a => pure a
| none => y

View File

@@ -39,31 +39,37 @@ This is not an instance because it is not definitionally equal to the standard i
Try to use the Boolean comparisons `Option.isNone` or `Option.isSome` instead.
-/
@[inline] def decidable_eq_none {o : Option α} : Decidable (o = none) :=
@[inline] def decidableEqNone {o : Option α} : Decidable (o = none) :=
decidable_of_decidable_of_iff isNone_iff_eq_none
instance {p : α Prop} [DecidablePred p] : o : Option α, Decidable ( a, a o p a)
| none => isTrue nofun
| some a =>
if h : p a then isTrue fun _ e => some_inj.1 e h
else isFalse <| mt (· _ rfl) h
@[deprecated decidableEqNone (since := "2025-04-10"), inline]
def decidable_eq_none {o : Option α} : Decidable (o = none) :=
decidableEqNone
instance {p : α Prop} [DecidablePred p] : o : Option α, Decidable (Exists fun a => a o p a)
| none => isFalse nofun
| some a => if h : p a then isTrue _, rfl, h else isFalse fun _, rfl, hn => h hn
instance decidableForallMem {p : α Prop} [DecidablePred p] :
o : Option α, Decidable ( a, a o p a)
| none => isTrue nofun
| some a =>
if h : p a then isTrue fun _ e => some_inj.1 e h
else isFalse <| mt (· _ rfl) h
instance decidableExistsMem {p : α Prop} [DecidablePred p] :
o : Option α, Decidable (Exists fun a => a o p a)
| none => isFalse nofun
| some a => if h : p a then isTrue _, rfl, h else isFalse fun _, rfl, hn => h hn
/--
Given an optional value and a function that can be applied when the value is `some`, returns the
result of applying the function if this is possible.
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
behavior is equivalent to that of `Option.bind`.
The function `f` is _partial_ because it is only defined for the values `a : α` such that
`o = some a`. This restriction allows the function to use the fact that it can only be called when
`o` is not `none`: it can relate its argument to the optional value `o`. Its runtime behavior is
equivalent to that of `Option.bind`.
Examples:
```lean example
def attach (v : Option α) : Option { y : α // y ∈ v } :=
def attach (v : Option α) : Option { y : α // v = some y } :=
v.pbind fun x h => some ⟨x, h⟩
```
```lean example
@@ -80,7 +86,7 @@ none
```
-/
@[inline]
def pbind : (o : Option α) (f : (a : α) a o Option β) Option β
def pbind : (o : Option α) (f : (a : α) o = some a Option β) Option β
| none, _ => none
| some a, f => f a rfl
@@ -90,7 +96,7 @@ satisfies `p` if it's present, applies the function to the value.
Examples:
```lean example
def attach (v : Option α) : Option { y : α // y ∈ v } :=
def attach (v : Option α) : Option { y : α // v = some y } :=
v.pmap (fun a (h : a ∈ v) => ⟨_, h⟩) (fun _ h => h)
```
```lean example
@@ -108,7 +114,7 @@ none
-/
@[inline] def pmap {p : α Prop}
(f : a : α, p a β) :
(o : Option α) ( a, a o p a) Option β
(o : Option α) ( a, o = some a p a) Option β
| none, _ => none
| some a, H => f a (H a rfl)
@@ -116,14 +122,14 @@ none
Given an optional value and a function that can be applied when the value is `some`, returns the
result of applying the function if this is possible, or a fallback value otherwise.
The function `f` is _partial_ because it is only defined for the values `a : α` such `a ∈ o`, which
is equivalent to `o = some a`. This restriction allows the function to use the fact that it can only
be called when `o` is not `none`: it can relate its argument to the optional value `o`. Its runtime
behavior is equivalent to that of `Option.elim`.
The function `f` is _partial_ because it is only defined for the values `a : α` such that
`o = some a`. This restriction allows the function to use the fact that it can only be called when
`o` is not `none`: it can relate its argument to the optional value `o`. Its runtime behavior is
equivalent to that of `Option.elim`.
Examples:
```lean example
def attach (v : Option α) : Option { y : α // y ∈ v } :=
def attach (v : Option α) : Option { y : α // v = some y } :=
v.pelim none fun x h => some ⟨x, h⟩
```
```lean example
@@ -139,7 +145,7 @@ some ⟨3, ⋯⟩
none
```
-/
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) a o β) : β :=
@[inline] def pelim (o : Option α) (b : β) (f : (a : α) o = some a β) : β :=
match o with
| none => b
| some a => f a rfl

View File

@@ -12,11 +12,14 @@ import Init.Ext
namespace Option
@[deprecated mem_def (since := "2025-04-07")]
theorem mem_iff {a : α} {b : Option α} : a b b = some a := .rfl
theorem mem_some {a b : α} : a some b b = a := by simp
theorem mem_some_self (a : α) : a some a := mem_some.2 rfl
theorem mem_some_iff {a b : α} : a some b b = a := mem_some
theorem mem_some_self (a : α) : a some a := rfl
theorem some_ne_none (x : α) : some x none := nofun
@@ -28,12 +31,18 @@ protected theorem «exists» {p : Option α → Prop} :
fun | none, hx => .inl hx | some x, hx => .inr x, hx,
fun | .inl h => _, h | .inr _, hx => _, hx
theorem eq_none_or_eq_some (a : Option α) : a = none x, a = some x :=
Option.exists.mp exists_eq'
theorem get_mem : {o : Option α} (h : isSome o), o.get h o
| some _, _ => rfl
theorem get_of_mem : {o : Option α} (h : isSome o), a o o.get h = a
| _, _, rfl => rfl
theorem get_of_eq_some : {o : Option α} (h : isSome o), o = some a o.get h = a
| _, _, rfl => rfl
@[simp] theorem not_mem_none (a : α) : a (none : Option α) := nofun
theorem getD_of_ne_none {x : Option α} (hx : x none) (y : α) : some (x.getD y) = x := by
@@ -71,13 +80,24 @@ theorem get_inj {o1 o2 : Option α} {h1} {h2} :
theorem mem_unique {o : Option α} {a b : α} (ha : a o) (hb : b o) : a = b :=
some.inj <| ha hb
@[ext] theorem ext : {o₁ o₂ : Option α}, ( a, a o₁ a o₂) o₁ = o₂
theorem eq_some_unique {o : Option α} {a b : α} (ha : o = some a) (hb : o = some b) : a = b :=
some.inj <| ha hb
@[ext] theorem ext : {o₁ o₂ : Option α}, ( a, o₁ = some a o₂ = some a) o₁ = o₂
| none, none, _ => rfl
| some _, _, H => ((H _).1 rfl).symm
| _, some _, H => (H _).2 rfl
set_option Elab.async false
theorem eq_none_iff_forall_ne_some : o = none a, o some a := by
cases o <;> simp
theorem eq_none_iff_forall_some_ne : o = none a, some a o := by
cases o <;> simp
theorem eq_none_iff_forall_not_mem : o = none a, a o :=
fun e a h => by rw [e] at h; (cases h), fun h => ext <| by simp; exact h
eq_none_iff_forall_ne_some
theorem isSome_iff_exists : isSome x a, x = some a := by cases x <;> simp [isSome]
@@ -90,9 +110,30 @@ theorem isSome_of_mem {x : Option α} {y : α} (h : y ∈ x) : x.isSome := by
theorem isSome_of_eq_some {x : Option α} {y : α} (h : x = some y) : x.isSome := by
cases x <;> trivial
@[simp] theorem not_isSome : isSome a = false a.isNone = true := by
@[simp] theorem isSome_eq_false_iff : isSome a = false a.isNone = true := by
cases a <;> simp
@[simp] theorem isNone_eq_false_iff : isNone a = false a.isSome = true := by
cases a <;> simp
@[simp]
theorem not_isSome (a : Option α) : (!a.isSome) = a.isNone := by
cases a <;> simp
@[simp]
theorem not_comp_isSome : (! ·) @Option.isSome α = Option.isNone := by
funext
simp
@[simp]
theorem not_isNone (a : Option α) : (!a.isNone) = a.isSome := by
cases a <;> simp
@[simp]
theorem not_comp_isNone : (!·) @Option.isNone α = Option.isSome := by
funext x
simp
theorem eq_some_iff_get_eq : o = some a h : o.isSome, o.get h = a := by
cases o <;> simp
@@ -134,20 +175,28 @@ abbrev ball_ne_none := @forall_ne_none
@[simp] theorem bind_eq_bind : bind = @Option.bind α β := rfl
@[simp] theorem orElse_eq_orElse : HOrElse.hOrElse = @Option.orElse α := rfl
@[simp] theorem bind_some (x : Option α) : x.bind some = x := by cases x <;> rfl
@[simp] theorem bind_none (x : Option α) : x.bind (fun _ => none (α := β)) = none := by
cases x <;> rfl
theorem bind_eq_some : x.bind f = some b a, x = some a f a = some b := by
theorem bind_eq_some_iff : x.bind f = some b a, x = some a f a = some b := by
cases x <;> simp
@[simp] theorem bind_eq_none {o : Option α} {f : α Option β} :
@[deprecated bind_eq_some_iff (since := "2025-04-10")]
abbrev bind_eq_some := @bind_eq_some_iff
@[simp] theorem bind_eq_none_iff {o : Option α} {f : α Option β} :
o.bind f = none a, o = some a f a = none := by cases o <;> simp
@[deprecated bind_eq_none_iff (since := "2025-04-10")]
abbrev bind_eq_none := @bind_eq_none_iff
theorem bind_eq_none' {o : Option α} {f : α Option β} :
o.bind f = none b a, a o b f a := by
simp only [eq_none_iff_forall_not_mem, not_exists, not_and, mem_def, bind_eq_some]
o.bind f = none b a, o = some a f a some b := by
cases o <;> simp [eq_none_iff_forall_ne_some]
theorem mem_bind_iff {o : Option α} {f : α Option β} :
b o.bind f a, a o b f a := by
@@ -181,49 +230,67 @@ theorem isSome_apply_of_isSome_bind {α β : Type _} {x : Option α} {f : α
(isSome_apply_of_isSome_bind h) := by
cases x <;> trivial
theorem join_eq_some : x.join = some a x = some (some a) := by
simp [bind_eq_some]
theorem join_eq_some_iff : x.join = some a x = some (some a) := by
simp [bind_eq_some_iff]
@[deprecated join_eq_some_iff (since := "2025-04-10")]
abbrev join_eq_some := @join_eq_some_iff
theorem join_ne_none : x.join none z, x = some (some z) := by
simp only [ne_none_iff_exists', join_eq_some, iff_self]
simp only [ne_none_iff_exists', join_eq_some_iff, iff_self]
theorem join_ne_none' : ¬x.join = none z, x = some (some z) :=
join_ne_none
theorem join_eq_none : o.join = none o = none o = some none :=
theorem join_eq_none_iff : o.join = none o = none o = some none :=
match o with | none | some none | some (some _) => by simp
@[deprecated join_eq_none_iff (since := "2025-04-10")]
abbrev join_eq_none := @join_eq_none_iff
theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl
@[simp] theorem map_eq_map : Functor.map f = Option.map f := rfl
theorem map_none : f <$> none = none := rfl
@[deprecated map_none (since := "2025-04-10")]
abbrev map_none' := @map_none
theorem map_some : f <$> some a = some (f a) := rfl
@[deprecated map_some (since := "2025-04-10")]
abbrev map_some' := @map_some
@[simp] theorem map_eq_some' : x.map f = some b a, x = some a f a = b := by cases x <;> simp
theorem map_eq_some : f <$> x = some b a, x = some a f a = b := map_eq_some'
@[simp] theorem map_eq_none' : x.map f = none x = none := by
cases x <;> simp [map_none', map_some', eq_self_iff_true]
theorem isSome_map {x : Option α} : (f <$> x).isSome = x.isSome := by
@[simp] theorem map_eq_some_iff : x.map f = some b a, x = some a f a = b := by
cases x <;> simp
@[simp] theorem isSome_map' {x : Option α} : (x.map f).isSome = x.isSome := by
@[deprecated map_eq_some_iff (since := "2025-04-10")]
abbrev map_eq_some := @map_eq_some_iff
@[deprecated map_eq_some_iff (since := "2025-04-10")]
abbrev map_eq_some' := @map_eq_some_iff
@[simp] theorem map_eq_none_iff : x.map f = none x = none := by
cases x <;> simp [map_none, map_some, eq_self_iff_true]
@[deprecated map_eq_none_iff (since := "2025-04-10")]
abbrev map_eq_none := @map_eq_none_iff
@[deprecated map_eq_none_iff (since := "2025-04-10")]
abbrev map_eq_none' := @map_eq_none_iff
@[simp] theorem isSome_map {x : Option α} : (x.map f).isSome = x.isSome := by
cases x <;> simp
@[simp] theorem isNone_map' {x : Option α} : (x.map f).isNone = x.isNone := by
cases x <;> simp
@[deprecated isSome_map (since := "2025-04-10")]
abbrev isSome_map' := @isSome_map
theorem map_eq_none : f <$> x = none x = none := map_eq_none'
@[simp] theorem isNone_map {x : Option α} : (x.map f).isNone = x.isNone := by
cases x <;> simp
theorem map_eq_bind {x : Option α} : x.map f = x.bind (some f) := by
cases x <;> simp [Option.bind]
theorem map_congr {x : Option α} (h : a, a x f a = g a) : x.map f = x.map g := by
cases x <;> simp only [map_none', map_some', h, mem_def]
theorem map_congr {x : Option α} (h : a, x = some a f a = g a) :
x.map f = x.map g := by
cases x <;> simp only [map_none, map_some, h]
@[simp] theorem map_id_fun {α : Type u} : Option.map (id : α α) = id := by
funext; simp [map_id]
@@ -241,7 +308,7 @@ theorem get_map {f : α → β} {o : Option α} {h : (o.map f).isSome} :
@[simp] theorem map_map (h : β γ) (g : α β) (x : Option α) :
(x.map g).map h = x.map (h g) := by
cases x <;> simp only [map_none', map_some', ··]
cases x <;> simp only [map_none, map_some, ··]
theorem comp_map (h : β γ) (g : α β) (x : Option α) : x.map (h g) = (x.map g).map h :=
(map_map ..).symm
@@ -249,7 +316,7 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
@[simp] theorem map_comp_map (f : α β) (g : β γ) :
Option.map g Option.map f = Option.map (g f) := by funext x; simp
theorem mem_map_of_mem (g : α β) (h : a x) : g a Option.map g x := h.symm map_some' ..
theorem mem_map_of_mem (g : α β) (h : a x) : g a Option.map g x := h.symm map_some ..
theorem map_inj_right {f : α β} {o o' : Option α} (w : x y, f x = f y x = y) :
o.map f = o'.map f o = o' := by
@@ -279,12 +346,15 @@ theorem isSome_of_isSome_filter (p : α → Bool) (o : Option α) (h : (o.filter
@[deprecated isSome_of_isSome_filter (since := "2025-03-18")]
abbrev isSome_filter_of_isSome := @isSome_of_isSome_filter
@[simp] theorem filter_eq_none {o : Option α} {p : α Bool} :
o.filter p = none a, a o ¬ p a := by
@[simp] theorem filter_eq_none_iff {o : Option α} {p : α Bool} :
o.filter p = none a, o = some a ¬ p a := by
cases o <;> simp [filter_some]
@[simp] theorem filter_eq_some {o : Option α} {p : α Bool} :
o.filter p = some a a o p a := by
@[deprecated filter_eq_none_iff (since := "2025-04-10")]
abbrev filter_eq_none := @filter_eq_none_iff
@[simp] theorem filter_eq_some_iff {o : Option α} {p : α Bool} :
o.filter p = some a o = some a p a := by
cases o with
| none => simp
| some a =>
@@ -297,6 +367,9 @@ abbrev isSome_filter_of_isSome := @isSome_of_isSome_filter
rintro rfl
simpa using h
@[deprecated filter_eq_some_iff (since := "2025-04-10")]
abbrev filter_eq_some := @filter_eq_some_iff
theorem mem_filter_iff {p : α Bool} {a : α} {o : Option α} :
a o.filter p a o p a := by
simp
@@ -370,29 +443,43 @@ theorem join_join {x : Option (Option (Option α))} : x.join.join = (x.map join)
cases x <;> simp
theorem mem_of_mem_join {a : α} {x : Option (Option α)} (h : a x.join) : some a x :=
h.symm join_eq_some.1 h
h.symm join_eq_some_iff.1 h
@[simp] theorem some_orElse (a : α) (x : Option α) : (some a <|> x) = some a := rfl
@[simp] theorem some_orElse (a : α) (f) : (some a).orElse f = some a := rfl
@[simp] theorem none_orElse (x : Option α) : (none <|> x) = x := rfl
@[simp] theorem none_orElse (f : Unit Option α) : none.orElse f = f () := rfl
@[simp] theorem orElse_none (x : Option α) : (x <|> none) = x := by cases x <;> rfl
@[simp] theorem orElse_none (x : Option α) : x.orElse (fun _ => none) = x := by cases x <;> rfl
theorem map_orElse {x y : Option α} : (x <|> y).map f = (x.map f <|> y.map f) := by
theorem orElse_eq_some_iff (o : Option α) (f) (x : α) :
(o.orElse f) = some x o = some x o = none f () = some x := by
cases o <;> simp
theorem orElse_eq_none_iff (o : Option α) (f) : (o.orElse f) = none o = none f () = none := by
cases o <;> simp
theorem map_orElse {x : Option α} {y} :
(x.orElse y).map f = (x.map f).orElse (fun _ => (y ()).map f) := by
cases x <;> simp
@[simp] theorem guard_eq_some [DecidablePred p] : guard p a = some b a = b p a :=
@[simp] theorem guard_eq_some_iff [DecidablePred p] : guard p a = some b a = b p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[deprecated guard_eq_some_iff (since := "2025-04-10")]
abbrev guard_eq_some := @guard_eq_some_iff
@[simp] theorem isSome_guard [DecidablePred p] : (Option.guard p a).isSome p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[deprecated isSome_guard (since := "2025-03-18")]
abbrev guard_isSome := @isSome_guard
@[simp] theorem guard_eq_none [DecidablePred p] : Option.guard p a = none ¬ p a :=
@[simp] theorem guard_eq_none_iff [DecidablePred p] : Option.guard p a = none ¬ p a :=
if h : p a then by simp [Option.guard, h] else by simp [Option.guard, h]
@[deprecated guard_eq_none_iff (since := "2025-04-10")]
abbrev guard_eq_none := @guard_eq_none_iff
@[simp] theorem guard_pos [DecidablePred p] (h : p a) : Option.guard p a = some a := by
simp [Option.guard, h]
@@ -429,38 +516,54 @@ theorem guard_eq_map (p : α → Prop) [DecidablePred p] :
theorem guard_def (p : α Prop) {_ : DecidablePred p} :
Option.guard p = fun x => if p x then some x else none := rfl
theorem zipWith_eq_or_eq {f : α α α} (h : a b, f a b = a f a b = b) :
o₁ o₂, zipWith f o₁ o₂ = o₁ zipWith f o₁ o₂ = o₂
theorem merge_eq_or_eq {f : α α α} (h : a b, f a b = a f a b = b) :
o₁ o₂, merge f o₁ o₂ = o₁ merge f o₁ o₂ = o₂
| none, none => .inl rfl
| some _, none => .inl rfl
| none, some _ => .inr rfl
| some a, some b => by have := h a b; simp [zipWith] at this ; exact this
| some a, some b => by have := h a b; simp [merge] at this ; exact this
@[simp] theorem zipWith_none_left {f} {b : Option α} : zipWith f none b = b := by
@[simp] theorem merge_none_left {f} {b : Option α} : merge f none b = b := by
cases b <;> rfl
@[simp] theorem zipWith_none_right {f} {a : Option α} : zipWith f a none = a := by
@[simp] theorem merge_none_right {f} {a : Option α} : merge f a none = a := by
cases a <;> rfl
@[simp] theorem zipWith_some_some {f} {a b : α} :
zipWith f (some a) (some b) = f a b := rfl
@[simp] theorem merge_some_some {f} {a b : α} :
merge f (some a) (some b) = f a b := rfl
@[deprecated zipWith_eq_or_eq (since := "2025-04-04")]
@[deprecated merge_eq_or_eq (since := "2025-04-04")]
theorem liftOrGet_eq_or_eq {f : α α α} (h : a b, f a b = a f a b = b) :
o₁ o₂, zipWith f o₁ o₂ = o₁ zipWith f o₁ o₂ = o₂ :=
zipWith_eq_or_eq h
o₁ o₂, merge f o₁ o₂ = o₁ merge f o₁ o₂ = o₂ :=
merge_eq_or_eq h
@[deprecated zipWith_none_left (since := "2025-04-04")]
theorem liftOrGet_none_left {f} {b : Option α} : zipWith f none b = b :=
zipWith_none_left
@[deprecated merge_none_left (since := "2025-04-04")]
theorem liftOrGet_none_left {f} {b : Option α} : merge f none b = b :=
merge_none_left
@[deprecated zipWith_none_right (since := "2025-04-04")]
theorem liftOrGet_none_right {f} {a : Option α} : zipWith f a none = a :=
zipWith_none_right
@[deprecated merge_none_right (since := "2025-04-04")]
theorem liftOrGet_none_right {f} {a : Option α} : merge f a none = a :=
merge_none_right
@[deprecated zipWith_some_some (since := "2025-04-04")]
theorem liftOrGet_some_some {f} {a b : α} : zipWith f (some a) (some b) = f a b :=
zipWith_some_some
@[deprecated merge_some_some (since := "2025-04-04")]
theorem liftOrGet_some_some {f} {a b : α} : merge f (some a) (some b) = f a b :=
merge_some_some
instance commutative_merge (f : α α α) [Std.Commutative f] :
Std.Commutative (merge f) :=
fun a b by cases a <;> cases b <;> simp [merge, Std.Commutative.comm]
instance associative_merge (f : α α α) [Std.Associative f] :
Std.Associative (merge f) :=
fun a b c by cases a <;> cases b <;> cases c <;> simp [merge, Std.Associative.assoc]
instance idempotentOp_merge (f : α α α) [Std.IdempotentOp f] :
Std.IdempotentOp (merge f) :=
fun a by cases a <;> simp [merge, Std.IdempotentOp.idempotent]
instance lawfulIdentity_merge (f : α α α) : Std.LawfulIdentity (merge f) none where
left_id a := by cases a <;> simp [merge]
right_id a := by cases a <;> simp [merge]
@[simp] theorem elim_none (x : β) (f : α β) : none.elim x f = x := rfl
@@ -522,12 +625,18 @@ theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
@[simp] theorem isNone_or : (or o o').isNone = (o.isNone && o'.isNone) := by
cases o <;> rfl
@[simp] theorem or_eq_none : or o o' = none o = none o' = none := by
@[simp] theorem or_eq_none_iff : or o o' = none o = none o' = none := by
cases o <;> simp
@[simp] theorem or_eq_some : or o o' = some a o = some a (o = none o' = some a) := by
@[deprecated or_eq_none_iff (since := "2025-04-10")]
abbrev or_eq_none := @or_eq_none_iff
@[simp] theorem or_eq_some_iff : or o o' = some a o = some a (o = none o' = some a) := by
cases o <;> simp
@[deprecated or_eq_some_iff (since := "2025-04-10")]
abbrev or_eq_some := @or_eq_some_iff
theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
cases o₁ <;> cases o₂ <;> rfl
instance : Std.Associative (or (α := α)) := @or_assoc _
@@ -551,11 +660,11 @@ instance : Std.IdempotentOp (or (α := α)) := ⟨@or_self _⟩
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
cases o <;> rfl
theorem map_or : f <$> or o o' = (f <$> o).or (f <$> o') := by
theorem map_or : (or o o').map f = (o.map f).or (o'.map f) := by
cases o <;> rfl
theorem map_or' : (or o o').map f = (o.map f).or (o'.map f) := by
cases o <;> rfl
@[deprecated map_or (since := "2025-04-10")]
abbrev map_or' := @map_or
theorem or_of_isSome {o o' : Option α} (h : o.isSome) : o.or o' = o := by
match o, h with
@@ -713,10 +822,10 @@ theorem isSome_filter {α : Type _} {x : Option α} {f : α → Bool} :
/-! ### pbind -/
@[simp] theorem pbind_none : pbind none f = none := rfl
@[simp] theorem pbind_some : pbind (some a) f = f a (mem_some_self a) := rfl
@[simp] theorem pbind_some : pbind (some a) f = f a rfl := rfl
@[simp] theorem map_pbind {o : Option α} {f : (a : α) a o Option β} {g : β γ} :
(o.pbind f).map g = o.pbind (fun a h => (f a h).map g) := by
@[simp] theorem map_pbind {o : Option α} {f : (a : α) o = some a Option β}
{g : β γ} : (o.pbind f).map g = o.pbind (fun a h => (f a h).map g) := by
cases o <;> rfl
@[simp] theorem pbind_map {α β γ : Type _} (o : Option α)
@@ -729,25 +838,25 @@ theorem isSome_filter {α : Type _} {x : Option α} {f : α → Bool} :
cases o <;> rfl
@[congr] theorem pbind_congr {o o' : Option α} (ho : o = o')
{f : (a : α) a o Option β} {g : (a : α) a o' Option β}
{f : (a : α) o = some a Option β} {g : (a : α) o' = some a Option β}
(hf : a h, f a (ho h) = g a h) : o.pbind f = o'.pbind g := by
subst ho
exact (funext fun a => funext fun h => hf a h) Eq.refl (o.pbind f)
theorem pbind_eq_none_iff {o : Option α} {f : (a : α) a o Option β} :
theorem pbind_eq_none_iff {o : Option α} {f : (a : α) o = some a Option β} :
o.pbind f = none o = none a h, f a h = none := by
cases o <;> simp
theorem isSome_pbind_iff {o : Option α} {f : (a : α) a o Option β} :
theorem isSome_pbind_iff {o : Option α} {f : (a : α) o = some a Option β} :
(o.pbind f).isSome a h, (f a h).isSome := by
cases o <;> simp
@[deprecated "isSome_pbind_iff" (since := "2025-04-01")]
theorem pbind_isSome {o : Option α} {f : (a : α) a o Option β} :
theorem pbind_isSome {o : Option α} {f : (a : α) o = some a Option β} :
(o.pbind f).isSome = a h, (f a h).isSome := by
exact propext isSome_pbind_iff
theorem pbind_eq_some_iff {o : Option α} {f : (a : α) a o Option β} {b : β} :
theorem pbind_eq_some_iff {o : Option α} {f : (a : α) o = some a Option β} {b : β} :
o.pbind f = some b a h, f a h = some b := by
cases o <;> simp
@@ -756,8 +865,8 @@ theorem pbind_eq_some_iff {o : Option α} {f : (a : α) → a ∈ o → Option
@[simp] theorem pmap_none {p : α Prop} {f : (a : α), p a β} {h} :
pmap f none h = none := rfl
@[simp] theorem pmap_some {p : α Prop} {f : (a : α), p a β} {h}:
pmap f (some a) h = f a (h a (mem_some_self a)) := rfl
@[simp] theorem pmap_some {p : α Prop} {f : (a : α), p a β} {h} :
pmap f (some a) h = f a (h a rfl) := rfl
@[simp] theorem pmap_eq_none_iff {p : α Prop} {f : (a : α), p a β} {h} :
pmap f o h = none o = none := by
@@ -791,13 +900,13 @@ theorem map_pmap {p : α → Prop} (g : β → γ) (f : ∀ a, p a → β) (o H)
theorem pmap_map (o : Option α) (f : α β) {p : β Prop} (g : b, p b γ) (H) :
pmap g (o.map f) H =
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (mem_map_of_mem f m)) := by
pmap (fun a h => g (f a) h) o (fun a m => H (f a) (map_eq_some_iff.2 _, m, rfl)) := by
cases o <;> simp
theorem pmap_pred_congr {α : Type u}
{p p' : α Prop} (hp : x, p x p' x)
{o o' : Option α} (ho : o = o')
(h : x, x o p x) : x, x o' p' x := by
(h : x, o = some x p x) : x, o' = some x p' x := by
intro y hy
cases ho
exact (hp y).mp (h y hy)
@@ -808,7 +917,7 @@ theorem pmap_congr {α : Type u} {β : Type v}
{f : (x : α) p x β} {f' : (x : α) p' x β}
(hf : x h, f x ((hp x).mpr h) = f' x h)
{o o' : Option α} (ho : o = o')
{h : x, x o p x} :
{h : x, o = some x p x} :
Option.pmap f o h = Option.pmap f' o' (Option.pmap_pred_congr hp ho h) := by
cases ho
cases o
@@ -825,7 +934,7 @@ theorem pmap_congr {α : Type u} {β : Type v}
cases o <;> simp
@[simp] theorem elim_pmap {p : α Prop} (f : (a : α) p a β) (o : Option α)
(H : (a : α), a o p a) (g : γ) (g' : β γ) :
(H : (a : α), o = some a p a) (g : γ) (g' : β γ) :
(o.pmap f H).elim g g' =
o.pelim g (fun a h => g' (f a (H a h))) := by
cases o <;> simp
@@ -854,7 +963,7 @@ theorem isSome_pfilter_iff {α : Type _} {o : Option α} {p : (a : α) → o = s
cases o <;> simp
theorem isSome_pfilter_iff_get {α : Type _} {o : Option α} {p : (a : α) o = some a Bool} :
(o.pfilter p).isSome (h : o.isSome), p (o.get h) (get_mem h) := by
(o.pfilter p).isSome (h : o.isSome), p (o.get h) (some_get _).symm := by
cases o <;> simp
theorem isSome_of_isSome_pfilter {α : Type _} {o : Option α} {p : (a : α) o = some a Bool}

View File

@@ -8,7 +8,7 @@ import Init.Data.List.Lemmas
namespace Option
@[simp] theorem mem_toList {a : α} {o : Option α} : a o.toList a o := by
@[simp] theorem mem_toList {a : α} {o : Option α} : a o.toList o = some a := by
cases o <;> simp [eq_comm]
@[simp] theorem forIn'_none [Monad m] (b : β) (f : (a : α) a none β m (ForInStep β)) :

View File

@@ -92,4 +92,6 @@ theorem forIn_eq_elim [Monad m] [LawfulMonad m]
forIn (o.map g) init f = forIn o init fun a y => f (g a) y := by
cases o <;> simp
@[simp] theorem mapA_eq_mapM : @Option.mapA = @Option.mapM := rfl
end Option

View File

@@ -239,6 +239,17 @@ Examples:
@[extern "lean_int8_div"]
protected def Int8.div (a b : Int8) : Int8 := BitVec.sdiv a.toBitVec b.toBitVec
/--
The power operation, raising an 8-bit signed integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def Int8.pow (x : Int8) (n : Nat) : Int8 :=
match n with
| 0 => 1
| n + 1 => Int8.mul (Int8.pow x n) x
/--
The modulo operator for 8-bit signed integers, which computes the remainder when dividing one
integer by another with the T-rounding convention used by `Int8.div`. Usually accessed via the `%`
operator.
@@ -366,6 +377,7 @@ instance : Inhabited Int8 where
instance : Add Int8 := Int8.add
instance : Sub Int8 := Int8.sub
instance : Mul Int8 := Int8.mul
instance : Pow Int8 Nat := Int8.pow
instance : Mod Int8 := Int8.mod
instance : Div Int8 := Int8.div
instance : LT Int8 := Int8.lt
@@ -598,6 +610,17 @@ Examples:
@[extern "lean_int16_div"]
protected def Int16.div (a b : Int16) : Int16 := BitVec.sdiv a.toBitVec b.toBitVec
/--
The power operation, raising a 16-bit signed integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def Int16.pow (x : Int16) (n : Nat) : Int16 :=
match n with
| 0 => 1
| n + 1 => Int16.mul (Int16.pow x n) x
/--
The modulo operator for 16-bit signed integers, which computes the remainder when dividing one
integer by another with the T-rounding convention used by `Int16.div`. Usually accessed via the `%`
operator.
@@ -725,6 +748,7 @@ instance : Inhabited Int16 where
instance : Add Int16 := Int16.add
instance : Sub Int16 := Int16.sub
instance : Mul Int16 := Int16.mul
instance : Pow Int16 Nat := Int16.pow
instance : Mod Int16 := Int16.mod
instance : Div Int16 := Int16.div
instance : LT Int16 := Int16.lt
@@ -973,6 +997,17 @@ Examples:
@[extern "lean_int32_div"]
protected def Int32.div (a b : Int32) : Int32 := BitVec.sdiv a.toBitVec b.toBitVec
/--
The power operation, raising a 32-bit signed integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def Int32.pow (x : Int32) (n : Nat) : Int32 :=
match n with
| 0 => 1
| n + 1 => Int32.mul (Int32.pow x n) x
/--
The modulo operator for 32-bit signed integers, which computes the remainder when dividing one
integer by another with the T-rounding convention used by `Int32.div`. Usually accessed via the `%`
operator.
@@ -1100,6 +1135,7 @@ instance : Inhabited Int32 where
instance : Add Int32 := Int32.add
instance : Sub Int32 := Int32.sub
instance : Mul Int32 := Int32.mul
instance : Pow Int32 Nat := Int32.pow
instance : Mod Int32 := Int32.mod
instance : Div Int32 := Int32.div
instance : LT Int32 := Int32.lt
@@ -1368,6 +1404,17 @@ Examples:
@[extern "lean_int64_div"]
protected def Int64.div (a b : Int64) : Int64 := BitVec.sdiv a.toBitVec b.toBitVec
/--
The power operation, raising a 64-bit signed integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def Int64.pow (x : Int64) (n : Nat) : Int64 :=
match n with
| 0 => 1
| n + 1 => Int64.mul (Int64.pow x n) x
/--
The modulo operator for 64-bit signed integers, which computes the remainder when dividing one
integer by another with the T-rounding convention used by `Int64.div`. Usually accessed via the `%`
operator.
@@ -1495,6 +1542,7 @@ instance : Inhabited Int64 where
instance : Add Int64 := Int64.add
instance : Sub Int64 := Int64.sub
instance : Mul Int64 := Int64.mul
instance : Pow Int64 Nat := Int64.pow
instance : Mod Int64 := Int64.mod
instance : Div Int64 := Int64.div
instance : LT Int64 := Int64.lt
@@ -1746,6 +1794,17 @@ Examples:
@[extern "lean_isize_div"]
protected def ISize.div (a b : ISize) : ISize := BitVec.sdiv a.toBitVec b.toBitVec
/--
The power operation, raising a word-sized signed integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def ISize.pow (x : ISize) (n : Nat) : ISize :=
match n with
| 0 => 1
| n + 1 => ISize.mul (ISize.pow x n) x
/--
The modulo operator for word-sized signed integers, which computes the remainder when dividing one
integer by another with the T-rounding convention used by `ISize.div`. Usually accessed via the `%`
operator.
@@ -1875,6 +1934,7 @@ instance : Inhabited ISize where
instance : Add ISize := ISize.add
instance : Sub ISize := ISize.sub
instance : Mul ISize := ISize.mul
instance : Pow ISize Nat := ISize.pow
instance : Mod ISize := ISize.mod
instance : Div ISize := ISize.div
instance : LT ISize := ISize.lt

View File

@@ -2625,6 +2625,17 @@ instance : Std.LawfulCommIdentity (α := ISize) (· * ·) 1 where
@[simp] theorem Int64.zero_mul {a : Int64} : 0 * a = 0 := Int64.toBitVec_inj.1 BitVec.zero_mul
@[simp] theorem ISize.zero_mul {a : ISize} : 0 * a = 0 := ISize.toBitVec_inj.1 BitVec.zero_mul
@[simp] protected theorem Int8.pow_zero (x : Int8) : x ^ 0 = 1 := rfl
protected theorem Int8.pow_succ (x : Int8) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem Int16.pow_zero (x : Int16) : x ^ 0 = 1 := rfl
protected theorem Int16.pow_succ (x : Int16) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem Int32.pow_zero (x : Int32) : x ^ 0 = 1 := rfl
protected theorem Int32.pow_succ (x : Int32) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem Int64.pow_zero (x : Int64) : x ^ 0 = 1 := rfl
protected theorem Int64.pow_succ (x : Int64) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem ISize.pow_zero (x : ISize) : x ^ 0 = 1 := rfl
protected theorem ISize.pow_succ (x : ISize) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
protected theorem Int8.mul_add {a b c : Int8} : a * (b + c) = a * b + a * c :=
Int8.toBitVec_inj.1 BitVec.mul_add
protected theorem Int16.mul_add {a b c : Int16} : a * (b + c) = a * b + a * c :=

View File

@@ -20,6 +20,9 @@ def UInt8.mk (bitVec : BitVec 8) : UInt8 :=
def UInt8.ofNatCore (n : Nat) (h : n < UInt8.size) : UInt8 :=
UInt8.ofNatLT n h
/-- Converts an `Int` to a `UInt8` by taking the (non-negative remainder of the division by `2 ^ 8`. -/
def UInt8.ofInt (x : Int) : UInt8 := ofNat (x % 2 ^ 8).toNat
/--
Adds two 8-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
@@ -55,6 +58,17 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_uint8_div"]
protected def UInt8.div (a b : UInt8) : UInt8 := BitVec.udiv a.toBitVec b.toBitVec
/--
The power operation, raising an 8-bit unsigned integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def UInt8.pow (x : UInt8) (n : Nat) : UInt8 :=
match n with
| 0 => 1
| n + 1 => UInt8.mul (UInt8.pow x n) x
/--
The modulo operator for 8-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
@@ -129,6 +143,7 @@ protected def UInt8.le (a b : UInt8) : Prop := a.toBitVec ≤ b.toBitVec
instance : Add UInt8 := UInt8.add
instance : Sub UInt8 := UInt8.sub
instance : Mul UInt8 := UInt8.mul
instance : Pow UInt8 Nat := UInt8.pow
instance : Mod UInt8 := UInt8.mod
set_option linter.deprecated false in
@@ -217,6 +232,9 @@ def UInt16.mk (bitVec : BitVec 16) : UInt16 :=
def UInt16.ofNatCore (n : Nat) (h : n < UInt16.size) : UInt16 :=
UInt16.ofNatLT n h
/-- Converts an `Int` to a `UInt16` by taking the (non-negative remainder of the division by `2 ^ 16`. -/
def UInt16.ofInt (x : Int) : UInt16 := ofNat (x % 2 ^ 16).toNat
/--
Adds two 16-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
@@ -252,6 +270,17 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_uint16_div"]
protected def UInt16.div (a b : UInt16) : UInt16 := BitVec.udiv a.toBitVec b.toBitVec
/--
The power operation, raising a 16-bit unsigned integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def UInt16.pow (x : UInt16) (n : Nat) : UInt16 :=
match n with
| 0 => 1
| n + 1 => UInt16.mul (UInt16.pow x n) x
/--
The modulo operator for 16-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
@@ -289,7 +318,7 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_uint16_lor"]
protected def UInt16.lor (a b : UInt16) : UInt16 := a.toBitVec ||| b.toBitVec
/--
Bitwise exclusive or for 8-bit unsigned integers. Usually accessed via the `^^^` operator.
Bitwise exclusive or for 16-bit unsigned integers. Usually accessed via the `^^^` operator.
Each bit of the resulting integer is set if exactly one of the corresponding bits of both input
integers are set.
@@ -326,6 +355,7 @@ protected def UInt16.le (a b : UInt16) : Prop := a.toBitVec ≤ b.toBitVec
instance : Add UInt16 := UInt16.add
instance : Sub UInt16 := UInt16.sub
instance : Mul UInt16 := UInt16.mul
instance : Pow UInt16 Nat := UInt16.pow
instance : Mod UInt16 := UInt16.mod
set_option linter.deprecated false in
@@ -416,6 +446,9 @@ def UInt32.mk (bitVec : BitVec 32) : UInt32 :=
def UInt32.ofNatCore (n : Nat) (h : n < UInt32.size) : UInt32 :=
UInt32.ofNatLT n h
/-- Converts an `Int` to a `UInt32` by taking the (non-negative remainder of the division by `2 ^ 32`. -/
def UInt32.ofInt (x : Int) : UInt32 := ofNat (x % 2 ^ 32).toNat
/--
Adds two 32-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
@@ -451,6 +484,17 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_uint32_div"]
protected def UInt32.div (a b : UInt32) : UInt32 := BitVec.udiv a.toBitVec b.toBitVec
/--
The power operation, raising a 32-bit unsigned integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def UInt32.pow (x : UInt32) (n : Nat) : UInt32 :=
match n with
| 0 => 1
| n + 1 => UInt32.mul (UInt32.pow x n) x
/--
The modulo operator for 32-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
@@ -525,6 +569,7 @@ protected def UInt32.le (a b : UInt32) : Prop := a.toBitVec ≤ b.toBitVec
instance : Add UInt32 := UInt32.add
instance : Sub UInt32 := UInt32.sub
instance : Mul UInt32 := UInt32.mul
instance : Pow UInt32 Nat := UInt32.pow
instance : Mod UInt32 := UInt32.mod
set_option linter.deprecated false in
@@ -577,6 +622,9 @@ def UInt64.mk (bitVec : BitVec 64) : UInt64 :=
def UInt64.ofNatCore (n : Nat) (h : n < UInt64.size) : UInt64 :=
UInt64.ofNatLT n h
/-- Converts an `Int` to a `UInt64` by taking the (non-negative remainder of the division by `2 ^ 64`. -/
def UInt64.ofInt (x : Int) : UInt64 := ofNat (x % 2 ^ 64).toNat
/--
Adds two 64-bit unsigned integers, wrapping around on overflow. Usually accessed via the `+`
operator.
@@ -612,6 +660,17 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_uint64_div"]
protected def UInt64.div (a b : UInt64) : UInt64 := BitVec.udiv a.toBitVec b.toBitVec
/--
The power operation, raising a 64-bit unsigned integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def UInt64.pow (x : UInt64) (n : Nat) : UInt64 :=
match n with
| 0 => 1
| n + 1 => UInt64.mul (UInt64.pow x n) x
/--
The modulo operator for 64-bit unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
@@ -686,6 +745,7 @@ protected def UInt64.le (a b : UInt64) : Prop := a.toBitVec ≤ b.toBitVec
instance : Add UInt64 := UInt64.add
instance : Sub UInt64 := UInt64.sub
instance : Mul UInt64 := UInt64.mul
instance : Pow UInt64 Nat := UInt64.pow
instance : Mod UInt64 := UInt64.mod
set_option linter.deprecated false in
@@ -706,7 +766,7 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_uint64_complement"]
protected def UInt64.complement (a : UInt64) : UInt64 := ~~~a.toBitVec
/--
Negation of 32-bit unsigned integers, computed modulo `UInt64.size`.
Negation of 64-bit unsigned integers, computed modulo `UInt64.size`.
`UInt64.neg a` is equivalent to `18_446_744_073_709_551_615 - a + 1`.
@@ -774,6 +834,9 @@ def USize.mk (bitVec : BitVec System.Platform.numBits) : USize :=
def USize.ofNatCore (n : Nat) (h : n < USize.size) : USize :=
USize.ofNatLT n h
/-- Converts an `Int` to a `USize` by taking the (non-negative remainder of the division by `2 ^ numBits`. -/
def USize.ofInt (x : Int) : USize := ofNat (x % 2 ^ System.Platform.numBits).toNat
@[simp] theorem USize.le_size : 2 ^ 32 USize.size := by cases USize.size_eq <;> simp_all
@[simp] theorem USize.size_le : USize.size 2 ^ 64 := by cases USize.size_eq <;> simp_all
@@ -804,6 +867,17 @@ This function is overridden at runtime with an efficient implementation.
@[extern "lean_usize_div"]
protected def USize.div (a b : USize) : USize := a.toBitVec / b.toBitVec
/--
The power operation, raising a word-sized unsigned integer to a natural number power,
wrapping around on overflow. Usually accessed via the `^` operator.
This function is currently *not* overridden at runtime with an efficient implementation,
and should be used with caution. See https://github.com/leanprover/lean4/issues/7887.
-/
protected def USize.pow (x : USize) (n : Nat) : USize :=
match n with
| 0 => 1
| n + 1 => USize.mul (USize.pow x n) x
/--
The modulo operator for word-sized unsigned integers, which computes the remainder when dividing one
integer by another. Usually accessed via the `%` operator.
@@ -937,6 +1011,7 @@ def USize.toUInt64 (a : USize) : UInt64 :=
UInt64.ofNatLT a.toBitVec.toNat (Nat.lt_of_lt_of_le a.toBitVec.isLt USize.size_le)
instance : Mul USize := USize.mul
instance : Pow USize Nat := USize.pow
instance : Mod USize := USize.mod
set_option linter.deprecated false in

View File

@@ -286,6 +286,17 @@ declare_uint_theorems USize System.Platform.numBits
theorem USize.toNat_ofNat_of_lt_32 {n : Nat} (h : n < 4294967296) : toNat (ofNat n) = n :=
toNat_ofNat_of_lt (Nat.lt_of_lt_of_le h USize.le_size)
theorem UInt8.ofNat_mod_size : ofNat (x % 2 ^ 8) = ofNat x := by
simp [ofNat, BitVec.ofNat, Fin.ofNat']
theorem UInt16.ofNat_mod_size : ofNat (x % 2 ^ 16) = ofNat x := by
simp [ofNat, BitVec.ofNat, Fin.ofNat']
theorem UInt32.ofNat_mod_size : ofNat (x % 2 ^ 32) = ofNat x := by
simp [ofNat, BitVec.ofNat, Fin.ofNat']
theorem UInt64.ofNat_mod_size : ofNat (x % 2 ^ 64) = ofNat x := by
simp [ofNat, BitVec.ofNat, Fin.ofNat']
theorem USize.ofNat_mod_size : ofNat (x % 2 ^ System.Platform.numBits) = ofNat x := by
simp [ofNat, BitVec.ofNat, Fin.ofNat']
theorem UInt8.lt_ofNat_iff {n : UInt8} {m : Nat} (h : m < size) : n < ofNat m n.toNat < m := by
rw [lt_iff_toNat_lt, toNat_ofNat_of_lt' h]
theorem UInt8.ofNat_lt_iff {n : UInt8} {m : Nat} (h : m < size) : ofNat m < n m < n.toNat := by
@@ -2081,6 +2092,23 @@ theorem USize.ofNat_eq_iff_mod_eq_toNat (a : Nat) (b : USize) : USize.ofNat a =
USize.ofNatLT (a % b) (Nat.mod_lt_of_lt ha) = USize.ofNatLT a ha % USize.ofNatLT b hb := by
simp [USize.ofNatLT_eq_ofNat, USize.ofNat_mod ha hb]
@[simp] theorem UInt8.ofInt_one : ofInt 1 = 1 := rfl
@[simp] theorem UInt8.ofInt_neg_one : ofInt (-1) = -1 := rfl
@[simp] theorem UInt16.ofInt_one : ofInt 1 = 1 := rfl
@[simp] theorem UInt16.ofInt_neg_one : ofInt (-1) = -1 := rfl
@[simp] theorem UInt32.ofInt_one : ofInt 1 = 1 := rfl
@[simp] theorem UInt32.ofInt_neg_one : ofInt (-1) = -1 := rfl
@[simp] theorem UInt64.ofInt_one : ofInt 1 = 1 := rfl
@[simp] theorem UInt64.ofInt_neg_one : ofInt (-1) = -1 := rfl
@[simp] theorem USize.ofInt_one : ofInt 1 = 1 := by
rcases System.Platform.numBits_eq with h | h <;>
· apply USize.toNat_inj.mp
simp_all [USize.ofInt, USize.ofNat, size, toNat]
@[simp] theorem USize.ofInt_neg_one : ofInt (-1) = -1 := by
rcases System.Platform.numBits_eq with h | h <;>
· apply USize.toNat_inj.mp
simp_all [USize.ofInt, USize.ofNat, size, toNat]
@[simp] theorem UInt8.ofNat_add (a b : Nat) : UInt8.ofNat (a + b) = UInt8.ofNat a + UInt8.ofNat b := by
simp [UInt8.ofNat_eq_iff_mod_eq_toNat]
@[simp] theorem UInt16.ofNat_add (a b : Nat) : UInt16.ofNat (a + b) = UInt16.ofNat a + UInt16.ofNat b := by
@@ -2092,6 +2120,70 @@ theorem USize.ofNat_eq_iff_mod_eq_toNat (a : Nat) (b : USize) : USize.ofNat a =
@[simp] theorem USize.ofNat_add (a b : Nat) : USize.ofNat (a + b) = USize.ofNat a + USize.ofNat b := by
simp [USize.ofNat_eq_iff_mod_eq_toNat]
@[simp] theorem UInt8.ofInt_add (x y : Int) : ofInt (x + y) = ofInt x + ofInt y := by
dsimp only [UInt8.ofInt]
rw [Int.add_emod]
have h₁ : 0 x % 2 ^ 8 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 8 := Int.emod_nonneg _ (by decide)
have h₃ : 0 x % 2 ^ 8 + y % 2 ^ 8 := Int.add_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
have : (2 ^ 8 : Int).toNat = 2 ^ 8 := rfl
rw [this, UInt8.ofNat_mod_size, UInt8.ofNat_add]
@[simp] theorem UInt16.ofInt_add (x y : Int) : UInt16.ofInt (x + y) = UInt16.ofInt x + UInt16.ofInt y := by
dsimp only [UInt16.ofInt]
rw [Int.add_emod]
have h₁ : 0 x % 2 ^ 16 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 16 := Int.emod_nonneg _ (by decide)
have h₃ : 0 x % 2 ^ 16 + y % 2 ^ 16 := Int.add_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
have : (2 ^ 16 : Int).toNat = 2 ^ 16 := rfl
rw [this, UInt16.ofNat_mod_size, UInt16.ofNat_add]
@[simp] theorem UInt32.ofInt_add (x y : Int) : UInt32.ofInt (x + y) = UInt32.ofInt x + UInt32.ofInt y := by
dsimp only [UInt32.ofInt]
rw [Int.add_emod]
have h₁ : 0 x % 2 ^ 32 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 32 := Int.emod_nonneg _ (by decide)
have h₃ : 0 x % 2 ^ 32 + y % 2 ^ 32 := Int.add_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
have : (2 ^ 32 : Int).toNat = 2 ^ 32 := rfl
rw [this, UInt32.ofNat_mod_size, UInt32.ofNat_add]
@[simp] theorem UInt64.ofInt_add (x y : Int) : UInt64.ofInt (x + y) = UInt64.ofInt x + UInt64.ofInt y := by
dsimp only [UInt64.ofInt]
rw [Int.add_emod]
have h₁ : 0 x % 2 ^ 64 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 64 := Int.emod_nonneg _ (by decide)
have h₃ : 0 x % 2 ^ 64 + y % 2 ^ 64 := Int.add_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_add h₁ h₂]
have : (2 ^ 64 : Int).toNat = 2 ^ 64 := rfl
rw [this, UInt64.ofNat_mod_size, UInt64.ofNat_add]
namespace System.Platform
theorem two_pow_numBits_nonneg : 0 (2 ^ System.Platform.numBits : Int) := by
rcases System.Platform.numBits_eq with h | h <;>
· rw [h]
decide
theorem two_pow_numBits_ne_zero : (2 ^ System.Platform.numBits : Int) 0 := by
rcases System.Platform.numBits_eq with h | h <;>
· rw [h]
decide
end System.Platform
open System.Platform in
@[simp] theorem USize.ofInt_add (x y : Int) : USize.ofInt (x + y) = USize.ofInt x + USize.ofInt y := by
dsimp only [USize.ofInt]
rw [Int.add_emod]
have h₁ : 0 x % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
have h₂ : 0 y % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
have h₃ : 0 x % 2 ^ numBits + y % 2 ^ numBits := Int.add_nonneg h₁ h₂
rw [Int.toNat_emod h₃ two_pow_numBits_nonneg, Int.toNat_add h₁ h₂]
have : (2 ^ numBits : Int).toNat = 2 ^ numBits := by
rcases System.Platform.numBits_eq with h | h <;>
· rw [h]
decide
rw [this, USize.ofNat_mod_size, USize.ofNat_add]
@[simp] theorem UInt8.ofNatLT_add {a b : Nat} (hab : a + b < 2 ^ 8) :
UInt8.ofNatLT (a + b) hab = UInt8.ofNatLT a (Nat.lt_of_add_right_lt hab) + UInt8.ofNatLT b (Nat.lt_of_add_left_lt hab) := by
simp [UInt8.ofNatLT_eq_ofNat]
@@ -2176,6 +2268,56 @@ theorem USize.ofNatLT_sub {a b : Nat} (ha : a < 2 ^ System.Platform.numBits) (ha
@[simp] theorem USize.ofNat_mul (a b : Nat) : USize.ofNat (a * b) = USize.ofNat a * USize.ofNat b := by
simp [USize.ofNat_eq_iff_mod_eq_toNat]
@[simp] theorem UInt8.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
dsimp only [UInt8.ofInt]
rw [Int.mul_emod]
have h₁ : 0 x % 2 ^ 8 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 8 := Int.emod_nonneg _ (by decide)
have h₃ : 0 (x % 2 ^ 8) * (y % 2 ^ 8) := Int.mul_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
have : (2 ^ 8 : Int).toNat = 2 ^ 8 := rfl
rw [this, UInt8.ofNat_mod_size, UInt8.ofNat_mul]
@[simp] theorem UInt16.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
dsimp only [UInt16.ofInt]
rw [Int.mul_emod]
have h₁ : 0 x % 2 ^ 16 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 16 := Int.emod_nonneg _ (by decide)
have h₃ : 0 (x % 2 ^ 16) * (y % 2 ^ 16) := Int.mul_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
have : (2 ^ 16 : Int).toNat = 2 ^ 16 := rfl
rw [this, UInt16.ofNat_mod_size, UInt16.ofNat_mul]
@[simp] theorem UInt32.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
dsimp only [UInt32.ofInt]
rw [Int.mul_emod]
have h₁ : 0 x % 2 ^ 32 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 32 := Int.emod_nonneg _ (by decide)
have h₃ : 0 (x % 2 ^ 32) * (y % 2 ^ 32) := Int.mul_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
have : (2 ^ 32 : Int).toNat = 2 ^ 32 := rfl
rw [this, UInt32.ofNat_mod_size, UInt32.ofNat_mul]
@[simp] theorem UInt64.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
dsimp only [UInt64.ofInt]
rw [Int.mul_emod]
have h₁ : 0 x % 2 ^ 64 := Int.emod_nonneg _ (by decide)
have h₂ : 0 y % 2 ^ 64 := Int.emod_nonneg _ (by decide)
have h₃ : 0 (x % 2 ^ 64) * (y % 2 ^ 64) := Int.mul_nonneg h₁ h₂
rw [Int.toNat_emod h₃ (by decide), Int.toNat_mul h₁ h₂]
have : (2 ^ 64 : Int).toNat = 2 ^ 64 := rfl
rw [this, UInt64.ofNat_mod_size, UInt64.ofNat_mul]
open System.Platform in
@[simp] theorem USize.ofInt_mul (x y : Int) : ofInt (x * y) = ofInt x * ofInt y := by
dsimp only [USize.ofInt]
rw [Int.mul_emod]
have h₁ : 0 x % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
have h₂ : 0 y % 2 ^ numBits := Int.emod_nonneg _ two_pow_numBits_ne_zero
have h₃ : 0 (x % 2 ^ numBits) * (y % 2 ^ numBits) := Int.mul_nonneg h₁ h₂
rw [Int.toNat_emod h₃ two_pow_numBits_nonneg, Int.toNat_mul h₁ h₂]
have : (2 ^ numBits : Int).toNat = 2 ^ numBits := by
rcases System.Platform.numBits_eq with h | h <;>
· rw [h]
decide
rw [this, USize.ofNat_mod_size, USize.ofNat_mul]
@[simp] theorem UInt8.ofNatLT_mul {a b : Nat} (ha : a < 2 ^ 8) (hb : b < 2 ^ 8) (hab : a * b < 2 ^ 8) :
UInt8.ofNatLT (a * b) hab = UInt8.ofNatLT a ha * UInt8.ofNatLT b hb := by
simp [UInt8.ofNatLT_eq_ofNat]
@@ -2467,6 +2609,17 @@ protected theorem USize.neg_add {a b : USize} : - (a + b) = -a - b := USize.toBi
@[simp] protected theorem USize.neg_sub {a b : USize} : -(a - b) = b - a := by
rw [USize.sub_eq_add_neg, USize.neg_add, USize.sub_neg, USize.add_comm, USize.sub_eq_add_neg]
@[simp] protected theorem UInt8.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, UInt8.neg_eq_neg_one_mul]
@[simp] protected theorem UInt16.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, UInt16.neg_eq_neg_one_mul]
@[simp] protected theorem UInt32.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, UInt32.neg_eq_neg_one_mul]
@[simp] protected theorem UInt64.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, UInt64.neg_eq_neg_one_mul]
@[simp] protected theorem USize.ofInt_neg (x : Int) : ofInt (-x) = -ofInt x := by
rw [Int.neg_eq_neg_one_mul, ofInt_mul, ofInt_neg_one, USize.neg_eq_neg_one_mul]
@[simp] protected theorem UInt8.add_left_inj {a b : UInt8} (c : UInt8) : (a + c = b + c) a = b := by
simp [ UInt8.toBitVec_inj]
@[simp] protected theorem UInt16.add_left_inj {a b : UInt16} (c : UInt16) : (a + c = b + c) a = b := by
@@ -2614,6 +2767,17 @@ instance : Std.LawfulCommIdentity (α := USize) (· * ·) 1 where
@[simp] theorem UInt64.zero_mul {a : UInt64} : 0 * a = 0 := UInt64.toBitVec_inj.1 BitVec.zero_mul
@[simp] theorem USize.zero_mul {a : USize} : 0 * a = 0 := USize.toBitVec_inj.1 BitVec.zero_mul
@[simp] protected theorem UInt8.pow_zero (x : UInt8) : x ^ 0 = 1 := rfl
protected theorem UInt8.pow_succ (x : UInt8) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem UInt16.pow_zero (x : UInt16) : x ^ 0 = 1 := rfl
protected theorem UInt16.pow_succ (x : UInt16) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem UInt32.pow_zero (x : UInt32) : x ^ 0 = 1 := rfl
protected theorem UInt32.pow_succ (x : UInt32) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem UInt64.pow_zero (x : UInt64) : x ^ 0 = 1 := rfl
protected theorem UInt64.pow_succ (x : UInt64) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
@[simp] protected theorem USize.pow_zero (x : USize) : x ^ 0 = 1 := rfl
protected theorem USize.pow_succ (x : USize) (n : Nat) : x ^ (n + 1) = x ^ n * x := rfl
protected theorem UInt8.mul_add {a b c : UInt8} : a * (b + c) = a * b + a * c :=
UInt8.toBitVec_inj.1 BitVec.mul_add
protected theorem UInt16.mul_add {a b c : UInt16} : a * (b + c) = a * b + a * c :=

View File

@@ -294,6 +294,18 @@ theorem find?_eq_some_iff_getElem {xs : Vector α n} {p : α → Bool} {b : α}
subst w
simp
@[simp]
theorem isSome_findFinIdx? {xs : Vector α n} {p : α Bool} :
(xs.findFinIdx? p).isSome = xs.any p := by
rcases xs with xs, rfl
simp
@[simp]
theorem isNone_findFinIdx? {xs : Vector α n} {p : α Bool} :
(xs.findFinIdx? p).isNone = xs.all (fun x => ¬ p x) := by
rcases xs with xs, rfl
simp
@[simp] theorem findFinIdx?_subtype {p : α Prop} {xs : Vector { x // p x } n}
{f : { x // p x } Bool} {g : α Bool} (hf : x h, f x, h = g x) :
xs.findFinIdx? f = xs.unattach.findFinIdx? g := by

View File

@@ -1511,7 +1511,7 @@ theorem map_eq_iff {f : α → β} {as : Vector α n} {bs : Vector β n} :
if h : i < as.size then
simpa [h, h'] using w i h
else
rw [getElem?_neg, getElem?_neg, Option.map_none'] <;> omega
rw [getElem?_neg, getElem?_neg, Option.map_none] <;> omega
@[simp] theorem map_set {f : α β} {xs : Vector α n} {i : Nat} {h : i < n} {a : α} :
(xs.set i a).map f = (xs.map f).set i (f a) (by simpa using h) := by

View File

@@ -5,14 +5,30 @@ Authors: Kim Morrison
-/
prelude
import Init.Data.Zero
import Init.Data.Int.DivMod.Lemmas
import Init.TacticsExtra
/-!
# A monolithic commutative ring typeclass for internal use in `grind`.
The `Lean.Grind.CommRing` class will be used to convert expressions into the internal representation via polynomials,
with coefficients expressed via `OfNat` and `Neg`.
The `IsCharP α p` typeclass expresses that the ring has characteristic `p`,
i.e. that a coefficient `OfNat.ofNat x : α` is zero if and only if `x % p = 0` (in `Nat`).
See
```
theorem ofNat_ext_iff {x y : Nat} : OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y ↔ x % p = y % p
theorem ofNat_emod (x : Nat) : OfNat.ofNat (α := α) (x % p) = OfNat.ofNat x
theorem ofNat_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y ↔ x = y
```
-/
namespace Lean.Grind
class CommRing (α : Type u) extends Add α, Zero α, Mul α, One α, Neg α where
class CommRing (α : Type u) extends Add α, Mul α, Neg α, Sub α, HPow α Nat α where
[ofNat : n, OfNat α n]
add_assoc : a b c : α, a + b + c = a + (b + c)
add_comm : a b : α, a + b = b + a
add_zero : a : α, a + 0 = a
@@ -22,11 +38,31 @@ class CommRing (α : Type u) extends Add α, Zero α, Mul α, One α, Neg α whe
mul_one : a : α, a * 1 = a
left_distrib : a b c : α, a * (b + c) = a * b + a * c
zero_mul : a : α, 0 * a = 0
sub_eq_add_neg : a b : α, a - b = a + -b
pow_zero : a : α, a ^ 0 = 1
pow_succ : a : α, n : Nat, a ^ (n + 1) = (a ^ n) * a
ofNat_succ : a : Nat, OfNat.ofNat (α := α) (a + 1) = OfNat.ofNat a + 1 := by intros; rfl
-- This is a low-priority instance, to avoid conflicts with existing `OfNat` instances.
attribute [instance 100] CommRing.ofNat
namespace CommRing
variable {α : Type u} [CommRing α]
instance : NatCast α where
natCast n := OfNat.ofNat n
theorem natCast_zero : ((0 : Nat) : α) = 0 := rfl
theorem ofNat_eq_natCast (n : Nat) : OfNat.ofNat n = (n : α) := rfl
theorem ofNat_add (a b : Nat) : OfNat.ofNat (α := α) (a + b) = OfNat.ofNat a + OfNat.ofNat b := by
induction b with
| zero => simp [Nat.add_zero, add_zero]
| succ b ih => rw [Nat.add_succ, ofNat_succ, ih, ofNat_succ b, add_assoc]
theorem natCast_succ (n : Nat) : ((n + 1 : Nat) : α) = ((n : α) + 1) := ofNat_add _ _
theorem zero_add (a : α) : 0 + a = a := by
rw [add_comm, add_zero]
@@ -42,6 +78,204 @@ theorem right_distrib (a b c : α) : (a + b) * c = a * c + b * c := by
theorem mul_zero (a : α) : a * 0 = 0 := by
rw [mul_comm, zero_mul]
theorem ofNat_mul (a b : Nat) : OfNat.ofNat (α := α) (a * b) = OfNat.ofNat a * OfNat.ofNat b := by
induction b with
| zero => simp [Nat.mul_zero, mul_zero]
| succ a ih => rw [Nat.mul_succ, ofNat_add, ih, ofNat_add, left_distrib, mul_one]
theorem add_left_inj {a b : α} (c : α) : a + c = b + c a = b :=
fun h => by simpa [add_assoc, add_neg_cancel, add_zero] using (congrArg (· + -c) h),
fun g => congrArg (· + c) g
theorem add_right_inj (a b c : α) : a + b = a + c b = c := by
rw [add_comm a b, add_comm a c, add_left_inj]
theorem neg_zero : (-0 : α) = 0 := by
rw [ add_left_inj 0, neg_add_cancel, add_zero]
theorem neg_neg (a : α) : -(-a) = a := by
rw [ add_left_inj (-a), neg_add_cancel, add_neg_cancel]
theorem neg_eq_zero (a : α) : -a = 0 a = 0 :=
fun h => by
replace h := congrArg (-·) h
simpa [neg_neg, neg_zero] using h,
fun h => by rw [h, neg_zero]
theorem neg_add (a b : α) : -(a + b) = -a + -b := by
rw [ add_left_inj (a + b), neg_add_cancel, add_assoc (-a), add_comm a b, add_assoc (-b),
neg_add_cancel, zero_add, neg_add_cancel]
theorem neg_sub (a b : α) : -(a - b) = b - a := by
rw [sub_eq_add_neg, neg_add, neg_neg, sub_eq_add_neg, add_comm]
theorem sub_self (a : α) : a - a = 0 := by
rw [sub_eq_add_neg, add_neg_cancel]
instance : IntCast α where
intCast n := match n with
| Int.ofNat n => OfNat.ofNat n
| Int.negSucc n => -OfNat.ofNat (n + 1)
theorem intCast_zero : ((0 : Int) : α) = 0 := rfl
theorem intCast_one : ((1 : Int) : α) = 1 := rfl
theorem intCast_neg_one : ((-1 : Int) : α) = -1 := rfl
theorem intCast_ofNat (n : Nat) : ((n : Int) : α) = (n : α) := rfl
theorem intCast_ofNat_add_one (n : Nat) : ((n + 1 : Int) : α) = (n : α) + 1 := ofNat_add _ _
theorem intCast_negSucc (n : Nat) : ((-(n + 1) : Int) : α) = -((n : α) + 1) := congrArg (- ·) (ofNat_add _ _)
theorem intCast_neg (x : Int) : ((-x : Int) : α) = - (x : α) :=
match x with
| (0 : Nat) => neg_zero.symm
| (n + 1 : Nat) => by
rw [Int.natCast_add, Int.cast_ofNat_Int, intCast_negSucc, intCast_ofNat_add_one]
| -((n : Nat) + 1) => by
rw [Int.neg_neg, intCast_ofNat_add_one, intCast_negSucc, neg_neg]
theorem intCast_nat_add {x y : Nat} : ((x + y : Int) : α) = ((x : α) + (y : α)) := ofNat_add _ _
theorem intCast_nat_sub {x y : Nat} (h : x y) : (((x - y : Nat) : Int) : α) = ((x : α) - (y : α)) := by
induction x with
| zero =>
have : y = 0 := by omega
simp [this, intCast_zero, natCast_zero, sub_eq_add_neg, zero_add, neg_zero]
| succ x ih =>
by_cases h : x + 1 = y
· simp [h, intCast_zero, sub_self]
· have : ((x + 1 - y : Nat) : Int) = (x - y : Nat) + 1 := by omega
rw [this, intCast_ofNat_add_one]
specialize ih (by omega)
rw [intCast_ofNat] at ih
rw [ih, natCast_succ, sub_eq_add_neg, sub_eq_add_neg, add_assoc, add_comm _ 1, add_assoc]
theorem intCast_add (x y : Int) : ((x + y : Int) : α) = ((x : α) + (y : α)) :=
match x, y with
| (x : Nat), (y : Nat) => ofNat_add _ _
| (x : Nat), (-(y + 1 : Nat)) => by
by_cases h : x y + 1
· have : (x + -(y+1 : Nat) : Int) = ((x - (y + 1) : Nat) : Int) := by omega
rw [this, intCast_neg, intCast_nat_sub h, intCast_ofNat, intCast_ofNat, sub_eq_add_neg]
· have : (x + -(y+1 : Nat) : Int) = (-(y + 1 - x : Nat) : Int) := by omega
rw [this, intCast_neg, intCast_nat_sub (by omega), intCast_ofNat, intCast_neg, intCast_ofNat,
neg_sub, sub_eq_add_neg]
| (-(x + 1 : Nat)), (y : Nat) => by
by_cases h : y x+ 1
· have : (-(x+1 : Nat) + y : Int) = ((y - (x + 1) : Nat) : Int) := by omega
rw [this, intCast_neg, intCast_nat_sub h, intCast_ofNat, intCast_ofNat, sub_eq_add_neg, add_comm]
· have : (-(x+1 : Nat) + y : Int) = (-(x + 1 - y : Nat) : Int) := by omega
rw [this, intCast_neg, intCast_nat_sub (by omega), intCast_ofNat, intCast_neg, intCast_ofNat,
neg_sub, sub_eq_add_neg, add_comm]
| (-(x + 1 : Nat)), (-(y + 1 : Nat)) => by
rw [ Int.neg_add, intCast_neg, intCast_nat_add, neg_add, intCast_neg, intCast_neg, intCast_ofNat, intCast_ofNat]
theorem intCast_sub (x y : Int) : ((x - y : Int) : α) = ((x : α) - (y : α)) := by
rw [Int.sub_eq_add_neg, intCast_add, intCast_neg, sub_eq_add_neg]
theorem neg_eq_neg_one_mul (a : α) : -a = (-1) * a := by
rw [ add_left_inj a, neg_add_cancel]
conv => rhs; arg 2; rw [ one_mul a]
rw [ right_distrib, intCast_neg_one, intCast_one (α := α)]
simp [ intCast_add, intCast_zero, zero_mul]
theorem neg_mul (a b : α) : (-a) * b = -(a * b) := by
rw [neg_eq_neg_one_mul a, neg_eq_neg_one_mul (a * b), mul_assoc]
theorem mul_neg (a b : α) : a * (-b) = -(a * b) := by
rw [mul_comm, neg_mul, mul_comm]
theorem intCast_nat_mul (x y : Nat) : ((x * y : Int) : α) = ((x : α) * (y : α)) := ofNat_mul _ _
theorem intCast_mul (x y : Int) : ((x * y : Int) : α) = ((x : α) * (y : α)) :=
match x, y with
| (x : Nat), (y : Nat) => ofNat_mul _ _
| (x : Nat), (-(y + 1 : Nat)) => by
rw [Int.mul_neg, intCast_neg, intCast_nat_mul, intCast_neg, mul_neg, intCast_ofNat, intCast_ofNat]
| (-(x + 1 : Nat)), (y : Nat) => by
rw [Int.neg_mul, intCast_neg, intCast_nat_mul, intCast_neg, neg_mul, intCast_ofNat, intCast_ofNat]
| (-(x + 1 : Nat)), (-(y + 1 : Nat)) => by
rw [Int.neg_mul_neg, intCast_neg, intCast_neg, neg_mul, mul_neg, neg_neg, intCast_nat_mul,
intCast_ofNat, intCast_ofNat]
end CommRing
open CommRing
class IsCharP (α : Type u) [CommRing α] (p : Nat) where
ofNat_eq_zero_iff (p) : (x : Nat), OfNat.ofNat (α := α) x = 0 x % p = 0
namespace IsCharP
variable (p) {α : Type u} [CommRing α] [IsCharP α p]
theorem natCast_eq_zero_iff (x : Nat) : (x : α) = 0 x % p = 0 :=
ofNat_eq_zero_iff p x
theorem intCast_eq_zero_iff (x : Int) : (x : α) = 0 x % p = 0 :=
match x with
| (x : Nat) => by
have := ofNat_eq_zero_iff (α := α) p (x := x)
rw [Int.ofNat_mod_ofNat]
norm_cast
| -(x + 1 : Nat) => by
rw [Int.neg_emod, Int.ofNat_mod_ofNat, intCast_neg, intCast_ofNat, neg_eq_zero]
have := ofNat_eq_zero_iff (α := α) p (x := x + 1)
rw [ofNat_eq_natCast] at this
rw [this]
simp only [Int.ofNat_dvd]
simp only [ Nat.dvd_iff_mod_eq_zero, Int.natAbs_ofNat, Int.natCast_add,
Int.cast_ofNat_Int, ite_eq_left_iff]
by_cases h : p x + 1
· simp [h]
· simp only [h, not_false_eq_true, Int.natCast_add, Int.cast_ofNat_Int,
forall_const, false_iff, ne_eq]
by_cases w : p = 0
· simp [w]
omega
· have : ((x + 1) % p) < p := Nat.mod_lt _ (by omega)
omega
theorem intCast_ext_iff {x y : Int} : (x : α) = (y : α) x % p = y % p := by
constructor
· intro h
replace h : ((x - y : Int) : α) = 0 := by rw [intCast_sub, h, sub_self]
exact Int.emod_eq_emod_iff_emod_sub_eq_zero.mpr ((intCast_eq_zero_iff p _).mp h)
· intro h
have : ((x - y : Int) : α) = 0 :=
(intCast_eq_zero_iff p _).mpr (by rw [Int.sub_emod, h, Int.sub_self, Int.zero_emod])
replace this := congrArg (· + (y : α)) this
simpa [intCast_sub, zero_add, sub_eq_add_neg, add_assoc, neg_add_cancel, add_zero] using this
theorem ofNat_ext_iff {x y : Nat} : OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y x % p = y % p := by
have := intCast_ext_iff (α := α) p (x := x) (y := y)
simp only [intCast_ofNat, Int.ofNat_emod] at this
simp only [ofNat_eq_natCast]
norm_cast at this
theorem ofNat_ext {x y : Nat} (h : x % p = y % p) : OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y := (ofNat_ext_iff p).mpr h
theorem natCast_ext {x y : Nat} (h : x % p = y % p) : (x : α) = (y : α) := ofNat_ext _ h
theorem natCast_ext_iff {x y : Nat} : (x : α) = (y : α) x % p = y % p :=
ofNat_ext_iff p
theorem intCast_emod (x : Int) : ((x % p : Int) : α) = (x : α) := by
rw [intCast_ext_iff p, Int.emod_emod]
theorem natCast_emod (x : Nat) : ((x % p : Nat) : α) = (x : α) := by
simp only [ intCast_ofNat]
rw [Int.ofNat_emod, intCast_emod]
theorem ofNat_emod (x : Nat) : OfNat.ofNat (α := α) (x % p) = OfNat.ofNat x :=
natCast_emod _ _
theorem ofNat_eq_zero_iff_of_lt {x : Nat} (h : x < p) : OfNat.ofNat (α := α) x = 0 x = 0 := by
rw [ofNat_eq_zero_iff p, Nat.mod_eq_of_lt h]
theorem ofNat_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
OfNat.ofNat (α := α) x = OfNat.ofNat (α := α) y x = y := by
rw [ofNat_ext_iff p, Nat.mod_eq_of_lt h₁, Nat.mod_eq_of_lt h₂]
theorem natCast_eq_zero_iff_of_lt {x : Nat} (h : x < p) : (x : α) = 0 x = 0 := by
rw [natCast_eq_zero_iff p, Nat.mod_eq_of_lt h]
theorem natCast_eq_iff_of_lt {x y : Nat} (h₁ : x < p) (h₂ : y < p) :
(x : α) = (y : α) x = y := by
rw [natCast_ext_iff p, Nat.mod_eq_of_lt h₁, Nat.mod_eq_of_lt h₂]
end IsCharP
end Lean.Grind

View File

@@ -19,5 +19,12 @@ instance : CommRing (BitVec w) where
mul_one := BitVec.mul_one
left_distrib _ _ _ := BitVec.mul_add
zero_mul _ := BitVec.zero_mul
sub_eq_add_neg := BitVec.sub_eq_add_neg
pow_zero _ := BitVec.pow_zero
pow_succ _ _ := BitVec.pow_succ
ofNat_succ x := BitVec.ofNat_add x 1
instance : IsCharP (BitVec w) (2 ^ w) where
ofNat_eq_zero_iff {x} := by simp [BitVec.ofInt, BitVec.toNat_eq]
end Lean.Grind

View File

@@ -19,5 +19,12 @@ instance : CommRing Int where
mul_one := Int.mul_one
left_distrib := Int.mul_add
zero_mul := Int.zero_mul
pow_zero _ := rfl
pow_succ _ _ := rfl
ofNat_succ _ := rfl
sub_eq_add_neg _ _ := Int.sub_eq_add_neg
instance : IsCharP Int 0 where
ofNat_eq_zero_iff {x} := by erw [Int.ofNat_eq_zero]; simp
end Lean.Grind

View File

@@ -9,6 +9,9 @@ import Init.Data.SInt.Lemmas
namespace Lean.Grind
instance : IntCast Int8 where
intCast x := Int8.ofInt x
instance : CommRing Int8 where
add_assoc := Int8.add_assoc
add_comm := Int8.add_comm
@@ -19,6 +22,20 @@ instance : CommRing Int8 where
mul_one := Int8.mul_one
left_distrib _ _ _ := Int8.mul_add
zero_mul _ := Int8.zero_mul
sub_eq_add_neg := Int8.sub_eq_add_neg
pow_zero := Int8.pow_zero
pow_succ := Int8.pow_succ
ofNat_succ x := Int8.ofNat_add x 1
instance : IsCharP Int8 (2 ^ 8) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = Int8.ofInt x := rfl
rw [this]
simp [Int8.ofInt_eq_iff_bmod_eq_toInt,
Int.dvd_iff_bmod_eq_zero, Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
instance : IntCast Int16 where
intCast x := Int16.ofInt x
instance : CommRing Int16 where
add_assoc := Int16.add_assoc
@@ -30,6 +47,20 @@ instance : CommRing Int16 where
mul_one := Int16.mul_one
left_distrib _ _ _ := Int16.mul_add
zero_mul _ := Int16.zero_mul
sub_eq_add_neg := Int16.sub_eq_add_neg
pow_zero := Int16.pow_zero
pow_succ := Int16.pow_succ
ofNat_succ x := Int16.ofNat_add x 1
instance : IsCharP Int16 (2 ^ 16) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = Int16.ofInt x := rfl
rw [this]
simp [Int16.ofInt_eq_iff_bmod_eq_toInt,
Int.dvd_iff_bmod_eq_zero, Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
instance : IntCast Int32 where
intCast x := Int32.ofInt x
instance : CommRing Int32 where
add_assoc := Int32.add_assoc
@@ -41,6 +72,20 @@ instance : CommRing Int32 where
mul_one := Int32.mul_one
left_distrib _ _ _ := Int32.mul_add
zero_mul _ := Int32.zero_mul
sub_eq_add_neg := Int32.sub_eq_add_neg
pow_zero := Int32.pow_zero
pow_succ := Int32.pow_succ
ofNat_succ x := Int32.ofNat_add x 1
instance : IsCharP Int32 (2 ^ 32) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = Int32.ofInt x := rfl
rw [this]
simp [Int32.ofInt_eq_iff_bmod_eq_toInt,
Int.dvd_iff_bmod_eq_zero, Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
instance : IntCast Int64 where
intCast x := Int64.ofInt x
instance : CommRing Int64 where
add_assoc := Int64.add_assoc
@@ -52,6 +97,20 @@ instance : CommRing Int64 where
mul_one := Int64.mul_one
left_distrib _ _ _ := Int64.mul_add
zero_mul _ := Int64.zero_mul
sub_eq_add_neg := Int64.sub_eq_add_neg
pow_zero := Int64.pow_zero
pow_succ := Int64.pow_succ
ofNat_succ x := Int64.ofNat_add x 1
instance : IsCharP Int64 (2 ^ 64) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = Int64.ofInt x := rfl
rw [this]
simp [Int64.ofInt_eq_iff_bmod_eq_toInt,
Int.dvd_iff_bmod_eq_zero, Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
instance : IntCast ISize where
intCast x := ISize.ofInt x
instance : CommRing ISize where
add_assoc := ISize.add_assoc
@@ -63,5 +122,18 @@ instance : CommRing ISize where
mul_one := ISize.mul_one
left_distrib _ _ _ := ISize.mul_add
zero_mul _ := ISize.zero_mul
sub_eq_add_neg := ISize.sub_eq_add_neg
pow_zero := ISize.pow_zero
pow_succ := ISize.pow_succ
ofNat_succ x := ISize.ofNat_add x 1
open System.Platform (numBits)
instance : IsCharP ISize (2 ^ numBits) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = ISize.ofInt x := rfl
rw [this]
simp [ISize.ofInt_eq_iff_bmod_eq_toInt,
Int.dvd_iff_bmod_eq_zero, Nat.dvd_iff_mod_eq_zero, Int.ofNat_dvd_right]
end Lean.Grind

View File

@@ -7,6 +7,53 @@ prelude
import Init.Grind.CommRing.Basic
import Init.Data.UInt.Lemmas
namespace UInt8
/-- Variant of `UInt8.ofNat_mod_size` replacing `2 ^ 8` with `256`.-/
theorem ofNat_mod_size' : ofNat (x % 256) = ofNat x := ofNat_mod_size
instance : IntCast UInt8 where
intCast x := UInt8.ofInt x
end UInt8
namespace UInt16
/-- Variant of `UInt16.ofNat_mod_size` replacing `2 ^ 16` with `65536`.-/
theorem ofNat_mod_size' : ofNat (x % 65536) = ofNat x := ofNat_mod_size
instance : IntCast UInt16 where
intCast x := UInt16.ofInt x
end UInt16
namespace UInt32
/-- Variant of `UInt32.ofNat_mod_size` replacing `2 ^ 32` with `4294967296`.-/
theorem ofNat_mod_size' : ofNat (x % 4294967296) = ofNat x := ofNat_mod_size
instance : IntCast UInt32 where
intCast x := UInt32.ofInt x
end UInt32
namespace UInt64
/-- Variant of `UInt64.ofNat_mod_size` replacing `2 ^ 64` with `18446744073709551616`.-/
theorem ofNat_mod_size' : ofNat (x % 18446744073709551616) = ofNat x := ofNat_mod_size
instance : IntCast UInt64 where
intCast x := UInt64.ofInt x
end UInt64
namespace USize
instance : IntCast USize where
intCast x := USize.ofInt x
end USize
namespace Lean.Grind
instance : CommRing UInt8 where
@@ -19,6 +66,15 @@ instance : CommRing UInt8 where
mul_one := UInt8.mul_one
left_distrib _ _ _ := UInt8.mul_add
zero_mul _ := UInt8.zero_mul
sub_eq_add_neg := UInt8.sub_eq_add_neg
pow_zero := UInt8.pow_zero
pow_succ := UInt8.pow_succ
ofNat_succ x := UInt8.ofNat_add x 1
instance : IsCharP UInt8 (2 ^ 8) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = UInt8.ofNat x := rfl
simp [this, UInt8.ofNat_eq_iff_mod_eq_toNat]
instance : CommRing UInt16 where
add_assoc := UInt16.add_assoc
@@ -30,6 +86,15 @@ instance : CommRing UInt16 where
mul_one := UInt16.mul_one
left_distrib _ _ _ := UInt16.mul_add
zero_mul _ := UInt16.zero_mul
sub_eq_add_neg := UInt16.sub_eq_add_neg
pow_zero := UInt16.pow_zero
pow_succ := UInt16.pow_succ
ofNat_succ x := UInt16.ofNat_add x 1
instance : IsCharP UInt16 (2 ^ 16) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = UInt16.ofNat x := rfl
simp [this, UInt16.ofNat_eq_iff_mod_eq_toNat]
instance : CommRing UInt32 where
add_assoc := UInt32.add_assoc
@@ -41,6 +106,15 @@ instance : CommRing UInt32 where
mul_one := UInt32.mul_one
left_distrib _ _ _ := UInt32.mul_add
zero_mul _ := UInt32.zero_mul
sub_eq_add_neg := UInt32.sub_eq_add_neg
pow_zero := UInt32.pow_zero
pow_succ := UInt32.pow_succ
ofNat_succ x := UInt32.ofNat_add x 1
instance : IsCharP UInt32 (2 ^ 32) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = UInt32.ofNat x := rfl
simp [this, UInt32.ofNat_eq_iff_mod_eq_toNat]
instance : CommRing UInt64 where
add_assoc := UInt64.add_assoc
@@ -52,6 +126,15 @@ instance : CommRing UInt64 where
mul_one := UInt64.mul_one
left_distrib _ _ _ := UInt64.mul_add
zero_mul _ := UInt64.zero_mul
sub_eq_add_neg := UInt64.sub_eq_add_neg
pow_zero := UInt64.pow_zero
pow_succ := UInt64.pow_succ
ofNat_succ x := UInt64.ofNat_add x 1
instance : IsCharP UInt64 (2 ^ 64) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = UInt64.ofNat x := rfl
simp [this, UInt64.ofNat_eq_iff_mod_eq_toNat]
instance : CommRing USize where
add_assoc := USize.add_assoc
@@ -63,5 +146,16 @@ instance : CommRing USize where
mul_one := USize.mul_one
left_distrib _ _ _ := USize.mul_add
zero_mul _ := USize.zero_mul
sub_eq_add_neg := USize.sub_eq_add_neg
pow_zero := USize.pow_zero
pow_succ := USize.pow_succ
ofNat_succ x := USize.ofNat_add x 1
open System.Platform
instance : IsCharP USize (2 ^ numBits) where
ofNat_eq_zero_iff {x} := by
have : OfNat.ofNat x = USize.ofNat x := rfl
simp [this, USize.ofNat_eq_iff_mod_eq_toNat]
end Lean.Grind

View File

@@ -49,6 +49,8 @@ theorem eq_false_of_or_eq_false_right {a b : Prop} (h : (a b) = False) : b =
theorem imp_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a b) = True := by simp [h]
theorem imp_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a b) = True := by simp [h]
theorem imp_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a b) = b := by simp [h]
theorem eq_false_of_imp_eq_true {a b : Prop} (h₁ : (a b) = True) (h₂ : b = False) : a = False := by
simp at *; intro h; exact h₂ (h₁ h)
theorem eq_true_of_imp_eq_false {a b : Prop} (h : (a b) = False) : a = True := by simp_all
theorem eq_false_of_imp_eq_false {a b : Prop} (h : (a b) = False) : b = False := by simp_all

View File

@@ -71,11 +71,14 @@ theorem beq_eq_decide_eq {_ : BEq α} [LawfulBEq α] [DecidableEq α] (a b : α)
theorem bne_eq_decide_not_eq {_ : BEq α} [LawfulBEq α] [DecidableEq α] (a b : α) : (a != b) = (decide (¬ a = b)) := by
by_cases a = b <;> simp [*]
theorem natCast_div (a b : Nat) : ((a / b) : Int) = a / b := by
theorem xor_eq (a b : Bool) : (a ^^ b) = (a != b) := by
rfl
theorem natCast_mod (a b : Nat) : ((a % b) : Int) = a % b := by
rfl
theorem natCast_eq [NatCast α] (a : Nat) : (Nat.cast a : α) = (NatCast.natCast a : α) := rfl
theorem natCast_div (a b : Nat) : (NatCast.natCast (a / b) : Int) = (NatCast.natCast a) / (NatCast.natCast b) := rfl
theorem natCast_mod (a b : Nat) : (NatCast.natCast (a % b) : Int) = (NatCast.natCast a) % (NatCast.natCast b) := rfl
theorem natCast_add (a b : Nat) : (NatCast.natCast (a + b : Nat) : Int) = (NatCast.natCast a : Int) + (NatCast.natCast b : Int) := rfl
theorem natCast_mul (a b : Nat) : (NatCast.natCast (a * b : Nat) : Int) = (NatCast.natCast a : Int) * (NatCast.natCast b : Int) := rfl
theorem Nat.pow_one (a : Nat) : a ^ 1 = a := by
simp
@@ -86,6 +89,14 @@ theorem Int.pow_one (a : Int) : a ^ 1 = a := by
theorem forall_true (p : True Prop) : ( h : True, p h) = p True.intro :=
propext <| Iff.intro (fun h => h True.intro) (fun h _ => h)
-- Helper theorem used by the simproc `simpBoolEq`
theorem flip_bool_eq (a b : Bool) : (a = b) = (b = a) := by
rw [@Eq.comm _ a b]
-- Helper theorem used by the simproc `simpBoolEq`
theorem bool_eq_to_prop (a b : Bool) : (a = b) = ((a = true) = (b = true)) := by
simp
init_grind_norm
/- Pre theorems -/
not_and not_or not_ite not_forall not_exists
@@ -122,6 +133,8 @@ init_grind_norm
Bool.and_false Bool.and_true Bool.false_and Bool.true_and Bool.and_eq_true Bool.and_assoc
-- Bool not
Bool.not_not
-- Bool xor
xor_eq
-- beq
beq_iff_eq beq_eq_decide_eq beq_self_eq_true
-- bne
@@ -140,8 +153,10 @@ init_grind_norm
Int.emod_neg Int.ediv_neg
Int.ediv_zero Int.emod_zero
Int.ediv_one Int.emod_one
Int.natCast_add Int.natCast_mul Int.natCast_pow
Int.natCast_zero natCast_div natCast_mod
natCast_eq natCast_div natCast_mod
natCast_add natCast_mul
Int.pow_zero Int.pow_one
-- GT GE
ge_eq gt_eq

View File

@@ -59,12 +59,22 @@ structure Config where
If `splitIndPred` is `true`, `grind` performs case-splitting on inductive predicates.
Otherwise, it performs case-splitting only on types marked with `[grind cases]` attribute. -/
splitIndPred : Bool := false
/--
If `splitImp` is `true`, then given an implication `p → q` or `(h : p) → q h`, `grind` splits on `p`
it the implication is true. Otherwise, it will split only if `p` is an arithmetic predicate.
-/
splitImp : Bool := false
/-- By default, `grind` halts as soon as it encounters a sub-goal where no further progress can be made. -/
failures : Nat := 1
/-- Maximum number of heartbeats (in thousands) the canonicalizer can spend per definitional equality test. -/
canonHeartbeats : Nat := 1000
/-- If `ext` is `true`, `grind` uses extensionality theorems available in the environment. -/
ext : Bool := true
/--
If `funext` is `true`, `grind` creates new opportunities for applying function extensionality by case-splitting
on equalities between lambda expressions.
-/
funext : Bool := true
/-- If `verbose` is `false`, additional diagnostics information is not collected. -/
verbose : Bool := true
/-- If `clean` is `true`, `grind` uses `expose_names` and only generates accessible names. -/

View File

@@ -91,15 +91,14 @@ theorem monotone_bindM (f : γα → m (Option β)) (xs : Option α) (hmono
· apply monotone_const
@[partial_fixpoint_monotone]
theorem monotone_mapM (f : γ α m β) (xs : Option α) (hmono : monotone f) :
theorem monotone_mapM [LawfulMonad m] (f : γ α m β) (xs : Option α) (hmono : monotone f) :
monotone (fun x => xs.mapM (f x)) := by
cases xs with
| none => apply monotone_const
| some x =>
apply monotone_bind
· apply monotone_apply
apply hmono
· apply monotone_const
apply Functor.monotone_map
apply monotone_apply
apply hmono
@[partial_fixpoint_monotone]
theorem monotone_elimM (a : γ m (Option α)) (n : γ m β) (s : γ α m β)

View File

@@ -166,13 +166,13 @@ theorem combo_sat (a) (w₁ : c₁.sat x₁) (b) (w₂ : c₂.sat x₂) :
/-- The conjunction of two constraints. -/
def combine (x y : Constraint) : Constraint where
lowerBound := Option.zipWith max x.lowerBound y.lowerBound
upperBound := Option.zipWith min x.upperBound y.upperBound
lowerBound := Option.merge max x.lowerBound y.lowerBound
upperBound := Option.merge min x.upperBound y.upperBound
theorem combine_sat : (c : Constraint) (c' : Constraint) (t : Int)
(c.combine c').sat t = (c.sat t c'.sat t) := by
rintro _ | l₁, _ | u₁ <;> rintro _ | l₂, _ | u₂ t
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.zipWith] at *
<;> simp [sat, LowerBound.sat, UpperBound.sat, combine, Int.le_min, Int.max_le, Option.merge] at *
· rw [And.comm]
· rw [ and_assoc, And.comm (a := l₂ t), and_assoc]
· rw [and_assoc]

View File

@@ -2932,11 +2932,10 @@ This will be deprecated in favor of `Array.emptyWithCapacity` in the future.
def Array.mkEmpty {α : Type u} (c : @& Nat) : Array α where
toList := List.nil
set_option linter.unusedVariables false in
/--
Constructs a new empty array with initial capacity `c`.
-/
@[extern "lean_mk_empty_array_with_capacity"]
def Array.emptyWithCapacity {α : Type u} (c : @& Nat) : Array α where
toList := List.nil

View File

@@ -44,6 +44,13 @@ register_builtin_option Elab.async : Bool := {
`Lean.Command.State.snapshotTasks`."
}
register_builtin_option Elab.inServer : Bool := {
defValue := false
descr := "true if elaboration is being run inside the Lean language server\
\n\
\nThis option is set by the file worker and should not be modified otherwise."
}
/-- Performance option used by cmdline driver. -/
register_builtin_option internal.cmdlineSnapshots : Bool := {
defValue := false

View File

@@ -52,6 +52,21 @@ instance : FromJson WaitForDiagnostics :=
instance : ToJson WaitForDiagnostics :=
fun _ => mkObj []
/--
Internal `$/lean/waitForILeans` client->server request.
Yields a response once the watchdog process has loaded all .ilean files and has received
an ILean finalization notification for the worker and the document version designated in the request.
Used for test stability in tests that use the .ileans.
-/
structure WaitForILeansParams where
uri : DocumentUri
version : Nat
deriving FromJson, ToJson
structure WaitForILeans where
deriving FromJson, ToJson
inductive LeanFileProgressKind
| processing | fatalError
deriving Inhabited, BEq

View File

@@ -113,6 +113,19 @@ where
| Except.error inner => throw $ userError s!"Cannot decode publishDiagnostics parameters\n{inner}"
| _ => loop
partial def waitForILeans (waitForILeansId : RequestID := 0) (target : DocumentUri) (version : Nat) : IpcM Unit := do
writeRequest waitForILeansId, "$/lean/waitForILeans", WaitForILeansParams.mk target version
while true do
match ( readMessage) with
| .response id _ =>
if id == waitForILeansId then
return
| .responseError id _ msg _ =>
if id == waitForILeansId then
throw $ userError s!"Waiting for ILeans failed: {msg}"
| _ =>
pure ()
/--
Waits for a diagnostic notification with a specific message to be emitted. Discards all received
messages, so should not be combined with `collectDiagnostics`.

View File

@@ -510,6 +510,7 @@ where go := do
let oldCmds? := oldSnap?.map fun old =>
if old.newStx.isOfKind nullKind then old.newStx.getArgs else #[old.newStx]
let cmdPromises cmds.mapM fun _ => IO.Promise.new
let cancelTk? := ( read).cancelTk?
snap.new.resolve <| .ofTyped {
diagnostics := .empty
macroDecl := decl
@@ -517,7 +518,7 @@ where go := do
newNextMacroScope := nextMacroScope
hasTraces
next := Array.zipWith (fun cmdPromise cmd =>
{ stx? := some cmd, task := cmdPromise.resultD default }) cmdPromises cmds
{ stx? := some cmd, task := cmdPromise.resultD default, cancelTk? }) cmdPromises cmds
: MacroExpandedSnapshot
}
-- After the first command whose syntax tree changed, we must disable

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura, Sebastian Ullrich
prelude
import Lean.Parser.Module
import Lean.Util.Paths
import Lean.CoreM
namespace Lean.Elab
@@ -21,9 +22,16 @@ def processHeader (header : Syntax) (opts : Options) (messages : MessageLog)
(inputCtx : Parser.InputContext) (trustLevel : UInt32 := 0)
(plugins : Array System.FilePath := #[]) (leakEnv := false)
: IO (Environment × MessageLog) := do
let level := if experimental.module.get opts then
if Elab.inServer.get opts then
.server
else
.exported
else
.private
try
let env
importModules (leakEnv := leakEnv) (loadExts := true) (headerToImports header) opts trustLevel plugins
importModules (leakEnv := leakEnv) (loadExts := true) (level := level) (headerToImports header) opts trustLevel plugins
pure (env, messages)
catch e =>
let env mkEmptyEnvironment
@@ -46,7 +54,7 @@ def printImports (input : String) (fileName : Option String) : IO Unit := do
@[export lean_print_import_srcs]
def printImportSrcs (input : String) (fileName : Option String) : IO Unit := do
let sp initSrcSearchPath
let sp getSrcSearchPath
let (deps, _, _) parseImports input fileName
for dep in deps do
let fname findLean sp dep.module

View File

@@ -165,6 +165,8 @@ private def elabHeaders (views : Array DefView) (expandedDeclIds : Array ExpandD
-- no syntax guard to store, we already did the necessary checks
oldBodySnap? := guard reuseBody *> pure .missing, old.bodySnap
if oldBodySnap?.isNone then
-- NOTE: this will eagerly cancel async tasks not associated with an inner snapshot, most
-- importantly kernel checking and compilation of the top-level declaration
old.bodySnap.cancelRec
oldTacSnap? := do
guard reuseTac
@@ -217,6 +219,7 @@ private def elabHeaders (views : Array DefView) (expandedDeclIds : Array ExpandD
return newHeader
if let some snap := view.headerSnap? then
let (tacStx?, newTacTask?) mkTacTask view.value tacPromise
let cancelTk? := ( readThe Core.Context).cancelTk?
let bodySnap := {
stx? := view.value
reportingRange? :=
@@ -227,6 +230,8 @@ private def elabHeaders (views : Array DefView) (expandedDeclIds : Array ExpandD
else
getBodyTerm? view.value |>.getD view.value |>.getRange?
task := bodyPromise.resultD default
-- We should not cancel the entire body early if we have tactics
cancelTk? := guard newTacTask?.isNone *> cancelTk?
}
snap.new.resolve <| some {
diagnostics :=
@@ -269,7 +274,8 @@ where
:= do
if let some e := getBodyTerm? body then
if let `(by $tacs*) := e then
return (e, some { stx? := mkNullNode tacs, task := tacPromise.resultD default })
let cancelTk? := ( readThe Core.Context).cancelTk?
return (e, some { stx? := mkNullNode tacs, task := tacPromise.resultD default, cancelTk? })
tacPromise.resolve default
return (none, none)
@@ -432,8 +438,7 @@ private def elabFunValues (headers : Array DefViewElabHeader) (vars : Array Expr
snap.new.resolve <| some old
reusableResult? := some (old.value, old.state)
else
-- NOTE: this will eagerly cancel async tasks not associated with an inner snapshot, most
-- importantly kernel checking and compilation of the top-level declaration
-- make sure to cancel any async tasks that may still be running (e.g. kernel and codegen)
old.val.cancelRec
let (val, state) withRestoreOrSaveFull reusableResult? header.tacSnap? do
@@ -1158,7 +1163,7 @@ is error-free and contains no syntactical `sorry`s.
-/
private def logGoalsAccomplishedSnapshotTask (views : Array DefView)
(defsParsedSnap : DefsParsedSnapshot) : TermElabM Unit := do
if Lean.internal.cmdlineSnapshots.get ( getOptions) then
if Lean.Elab.inServer.get ( getOptions) then
-- Skip 'goals accomplished' task if we are on the command line.
-- These messages are only used in the language server.
return
@@ -1197,6 +1202,7 @@ private def logGoalsAccomplishedSnapshotTask (views : Array DefView)
-- Use first line of the mutual block to avoid covering the progress of the whole mutual block
reportingRange? := ( getRef).getPos?.map fun pos => pos, pos
task := logGoalsAccomplishedTask
cancelTk? := none
}
end Term
@@ -1235,9 +1241,10 @@ def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
} }
if snap.old?.isSome && (view.headerSnap?.bind (·.old?)).isNone then
snap.old?.forM (·.val.cancelRec)
let cancelTk? := ( read).cancelTk?
defs := defs.push {
fullHeaderRef
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default }
headerProcessedSnap := { stx? := d, task := headerPromise.resultD default, cancelTk? }
}
reusedAllHeaders := reusedAllHeaders && view.headerSnap?.any (·.old?.isSome)
views := views.push view

View File

@@ -109,11 +109,11 @@ structure InductiveView where
/-- Elaborated header for an inductive type before fvars for each inductive are added to the local context. -/
structure PreElabHeaderResult where
view : InductiveView
lctx : LocalContext
localInsts : LocalInstances
levelNames : List Name
params : Array Expr
numParams : Nat
type : Expr
/-- The parameters in the header's initial local context. Used for adding fvar alias terminfo. -/
origParams : Array Expr
deriving Inhabited
/-- The elaborated header with the `indFVar` registered for this inductive type. -/
@@ -228,16 +228,12 @@ private def checkClass (rs : Array PreElabHeaderResult) : TermElabM Unit := do
throwErrorAt r.view.ref "invalid inductive type, mutual classes are not supported"
private def checkNumParams (rs : Array PreElabHeaderResult) : TermElabM Nat := do
let numParams := rs[0]!.params.size
let numParams := rs[0]!.numParams
for r in rs do
unless r.params.size == numParams do
unless r.numParams == numParams do
throwErrorAt r.view.ref "invalid inductive type, number of parameters mismatch in mutually inductive datatypes"
return numParams
private def mkTypeFor (r : PreElabHeaderResult) : TermElabM Expr := do
withLCtx r.lctx r.localInsts do
mkForallFVars r.params r.type
/--
Execute `k` with updated binder information for `xs`. Any `x` that is explicit becomes implicit.
-/
@@ -276,7 +272,7 @@ private def checkHeaders (rs : Array PreElabHeaderResult) (numParams : Nat) (i :
checkHeaders rs numParams (i+1) type
where
checkHeader (r : PreElabHeaderResult) (numParams : Nat) (firstType? : Option Expr) : TermElabM Expr := do
let type mkTypeFor r
let type := r.type
match firstType? with
| none => return type
| some firstType =>
@@ -306,7 +302,8 @@ private def elabHeadersAux (views : Array InductiveView) (i : Nat) (acc : Array
let params Term.addAutoBoundImplicits params (view.declId.getTailPos? (canonicalOnly := true))
trace[Elab.inductive] "header params: {params}, type: {type}"
let levelNames Term.getLevelNames
return acc.push { lctx := ( getLCtx), localInsts := ( getLocalInstances), levelNames, params, type, view }
let type mkForallFVars params type
return acc.push { levelNames, numParams := params.size, type, view, origParams := params }
elabHeadersAux views (i+1) acc
else
return acc
@@ -326,21 +323,21 @@ private def elabHeaders (views : Array InductiveView) : TermElabM (Array PreElab
/--
Create a local declaration for each inductive type in `rs`, and execute `x params indFVars`, where `params` are the inductive type parameters and
`indFVars` are the new local declarations.
We use the local context/instances and parameters of rs[0].
We use the parameters of rs[0].
Note that this method is executed after we executed `checkHeaders` and established all
parameters are compatible.
-/
private def withInductiveLocalDecls (rs : Array PreElabHeaderResult) (x : Array Expr Array Expr TermElabM α) : TermElabM α := do
let namesAndTypes rs.mapM fun r => do
let type mkTypeFor r
pure (r.view.declName, r.view.shortDeclName, type)
let r0 := rs[0]!
let params := r0.params
withLCtx r0.lctx r0.localInsts <| withRef r0.view.ref do
let r0 := rs[0]!
forallBoundedTelescope r0.type r0.numParams fun params _ => withRef r0.view.ref do
let rec loop (i : Nat) (indFVars : Array Expr) := do
if h : i < namesAndTypes.size then
let (declName, shortDeclName, type) := namesAndTypes[i]
withAuxDecl shortDeclName type declName fun indFVar => loop (i+1) (indFVars.push indFVar)
if h : i < rs.size then
let r := rs[i]
for param in params, origParam in r.origParams do
if let .fvar origFVar := origParam then
Elab.pushInfoLeaf <| .ofFVarAliasInfo { id := param.fvarId!, baseId := origFVar, userName := param.fvarId!.getUserName }
withAuxDecl r.view.shortDeclName r.type r.view.declName fun indFVar =>
loop (i+1) (indFVars.push indFVar)
else
x params indFVars
loop 0 #[]
@@ -359,26 +356,6 @@ private def ElabHeaderResult.checkLevelNames (rs : Array PreElabHeaderResult) :
unless r.levelNames == levelNames do
throwErrorAt r.view.ref "invalid inductive type, universe parameters mismatch in mutually inductive datatypes"
/--
We need to work inside a single local context across all the inductive types, so we need to update the `ElabHeaderResult`s
so that resultant types refer to the fvars in `params`, the parameters for `rs[0]!` specifically.
Also updates the local contexts and local instances in each header.
-/
private def updateElabHeaderTypes (params : Array Expr) (rs : Array PreElabHeaderResult) (indFVars : Array Expr) : TermElabM (Array ElabHeaderResult) := do
rs.mapIdxM fun i r => do
/-
At this point, because of `withInductiveLocalDecls`, the only fvars that are in context are the ones related to the first inductive type.
Because of this, we need to replace the fvars present in each inductive type's header of the mutual block with those of the first inductive.
However, some mvars may still be uninstantiated there, and might hide some of the old fvars.
As such we first need to synthesize all possible mvars at this stage, instantiate them in the header types and only
then replace the parameters' fvars in the header type.
See issue #3242 (`https://github.com/leanprover/lean4/issues/3242`)
-/
let type instantiateMVars r.type
let type := type.replaceFVars r.params params
pure { r with lctx := getLCtx, localInsts := getLocalInstances, type := type, indFVar := indFVars[i]! }
private def getArity (indType : InductiveType) : MetaM Nat :=
forallTelescopeReducing indType.type fun xs _ => return xs.size
@@ -878,7 +855,7 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
trace[Elab.inductive] "level names: {allUserLevelNames}"
let res withInductiveLocalDecls rs fun params indFVars => do
trace[Elab.inductive] "indFVars: {indFVars}"
let rs updateElabHeaderTypes params rs indFVars
let rs := Array.zipWith (fun r indFVar => { r with indFVar : ElabHeaderResult }) rs indFVars
let mut indTypesArray : Array InductiveType := #[]
let mut elabs' := #[]
for h : i in [:views.size] do
@@ -886,8 +863,7 @@ private def mkInductiveDecl (vars : Array Expr) (elabs : Array InductiveElabStep
let r := rs[i]!
let elab' elabs[i]!.elabCtors rs r params
elabs' := elabs'.push elab'
let type mkForallFVars params r.type
indTypesArray := indTypesArray.push { name := r.view.declName, type, ctors := elab'.ctors }
indTypesArray := indTypesArray.push { name := r.view.declName, type := r.type, ctors := elab'.ctors }
Term.synthesizeSyntheticMVarsNoPostponing
let numExplicitParams fixedIndicesToParams params.size indTypesArray indFVars
trace[Elab.inductive] "numExplicitParams: {numExplicitParams}"

View File

@@ -1260,12 +1260,8 @@ private def addDefaults (levelParams : List Name) (params : Array Expr) (replace
let fieldInfos := ( get).fields
let lctx instantiateLCtxMVars ( getLCtx)
/- The parameters `params` for the auxiliary "default value" definitions must be marked as implicit, and all others as explicit. -/
let lctx :=
params.foldl (init := lctx) fun (lctx : LocalContext) (p : Expr) =>
if p.isFVar then
lctx.setBinderInfo p.fvarId! BinderInfo.implicit
else
lctx
let lctx := params.foldl (init := lctx) fun (lctx : LocalContext) (p : Expr) =>
lctx.setBinderInfo p.fvarId! BinderInfo.implicit
let parentFVarIds := fieldInfos |>.filter (·.kind.isParent) |>.map (·.fvar.fvarId!)
let fields := fieldInfos |>.filter (!·.kind.isParent)
withLCtx lctx ( getLocalInstances) do

View File

@@ -290,8 +290,8 @@ def canonicalizeWithSharing (P : Expr) (lhs rhs : Expr) : SimpM Simp.Step := do
-- of `lCoeff_{old}` are zero iff `lExpr` contains only neutral elements,
-- we default to `lNew` being some canonical neutral element if both
-- `commonExpr?` and `lNew?` are `none`.
let lNew := Option.zipWith (mkApp2 op.toExpr) commonExpr? lNew? |>.getD op.neutralElement
let rNew := Option.zipWith (mkApp2 op.toExpr) commonExpr? rNew? |>.getD op.neutralElement
let lNew := Option.merge (mkApp2 op.toExpr) commonExpr? lNew? |>.getD op.neutralElement
let rNew := Option.merge (mkApp2 op.toExpr) commonExpr? rNew? |>.getD op.neutralElement
let oldExpr := mkApp2 P lhs rhs
let expr := mkApp2 P lNew rNew

View File

@@ -63,13 +63,10 @@ partial def findInitialId (proof : Array IntAction) (curr : Nat := 0) : Except S
else
throw "LRAT proof doesn't contain a proper first proof step."
def findEmptyId (proof : Array IntAction) : Except String Nat := do
if h : 0 < proof.size then
match proof[proof.size - 1] with
| .addEmpty id .. => pure id
| _ => throw "Last proof step is not the empty clause"
else
throw "The LRAT proof contains no steps."
def findEmptyId (proof : Array IntAction) : Except String Nat :=
match proof.findSomeRev? (if let .addEmpty id .. := . then some id else none) with
| some id => pure id
| none => throw "LRAT proof doesn't contain the empty clause."
def run (proof : Array IntAction) (x : M α) : Except String α := do
let initialId findInitialId proof

View File

@@ -165,6 +165,7 @@ structure EvalTacticFailure where
state : SavedState
partial def evalTactic (stx : Syntax) : TacticM Unit := do
checkSystem "tactic execution"
profileitM Exception "tactic execution" (decl := stx.getKind) ( getOptions) <|
withRef stx <| withIncRecDepth <| withFreshMacroScope <| match stx with
| .node _ k _ =>
@@ -240,6 +241,7 @@ where
snap.old?.forM (·.val.cancelRec)
let promise IO.Promise.new
-- Store new unfolding in the snapshot tree
let cancelTk? := ( readThe Core.Context).cancelTk?
snap.new.resolve {
stx := stx'
diagnostics := .empty
@@ -249,7 +251,7 @@ where
state? := ( Tactic.saveState)
moreSnaps := #[]
}
next := #[{ stx? := stx', task := promise.resultD default }]
next := #[{ stx? := stx', task := promise.resultD default, cancelTk? }]
}
-- Update `tacSnap?` to old unfolding
withTheReader Term.Context ({ · with tacSnap? := some {

View File

@@ -78,13 +78,14 @@ where
let next IO.Promise.new
let finished IO.Promise.new
let inner IO.Promise.new
let cancelTk? := ( readThe Core.Context).cancelTk?
snap.new.resolve {
desc := tac.getKind.toString
diagnostics := .empty
stx := tac
inner? := some { stx? := tac, task := inner.resultD default }
finished := { stx? := tac, task := finished.resultD default }
next := #[{ stx? := stxs, task := next.resultD default }]
inner? := some { stx? := tac, task := inner.resultD default, cancelTk? }
finished := { stx? := tac, task := finished.resultD default, cancelTk? }
next := #[{ stx? := stxs, task := next.resultD default, cancelTk? }]
}
-- Run `tac` in a fresh info tree state and store resulting state in snapshot for
-- incremental reporting, then add back saved trees. Here we rely on `evalTactic`

View File

@@ -286,14 +286,15 @@ where
-- them, eventually put each of them back in `Context.tacSnap?` in `applyAltStx`
let finished IO.Promise.new
let altPromises altStxs.mapM fun _ => IO.Promise.new
let cancelTk? := ( readThe Core.Context).cancelTk?
tacSnap.new.resolve {
-- save all relevant syntax here for comparison with next document version
stx := mkNullNode altStxs
diagnostics := .empty
inner? := none
finished := { stx? := mkNullNode altStxs, reportingRange? := none, task := finished.resultD default }
finished := { stx? := mkNullNode altStxs, reportingRange? := none, task := finished.resultD default, cancelTk? }
next := Array.zipWith
(fun stx prom => { stx? := some stx, task := prom.resultD default })
(fun stx prom => { stx? := some stx, task := prom.resultD default, cancelTk? })
altStxs altPromises
}
goWithIncremental <| altPromises.mapIdx fun i prom => {

View File

@@ -1878,13 +1878,14 @@ where
go todo (autos.push auto)
/--
Similar to `autoBoundImplicits`, but immediately if the resulting array of expressions contains metavariables,
it immediately uses `mkForallFVars` + `forallBoundedTelescope` to convert them into free variables.
Similar to `addAutoBoundImplicits`, but converts all metavariables into free variables.
It uses `mkForallFVars` + `forallBoundedTelescope` to convert metavariables into free variables.
The type `type` is modified during the process if type depends on `xs`.
We use this method to simplify the conversion of code using `autoBoundImplicitsOld` to `autoBoundImplicits`.
-/
def addAutoBoundImplicits' (xs : Array Expr) (type : Expr) (k : Array Expr Expr TermElabM α) : TermElabM α := do
let xs addAutoBoundImplicits xs none
def addAutoBoundImplicits' (xs : Array Expr) (type : Expr) (k : Array Expr Expr TermElabM α) (inlayHintPos? : Option String.Pos := none) : TermElabM α := do
let xs addAutoBoundImplicits xs inlayHintPos?
if xs.all (·.isFVar) then
k xs type
else

View File

@@ -122,13 +122,15 @@ end TagDeclarationExtension
structure MapDeclarationExtension (α : Type) extends PersistentEnvExtension (Name × α) (Name × α) (NameMap α)
deriving Inhabited
def mkMapDeclarationExtension (name : Name := by exact decl_name%) : IO (MapDeclarationExtension α) :=
def mkMapDeclarationExtension (name : Name := by exact decl_name%)
(exportEntriesFn : NameMap α Array (Name × α) := (·.toArray)) : IO (MapDeclarationExtension α) :=
.mk <$> registerPersistentEnvExtension {
name := name,
mkInitial := pure {}
addImportedFn := fun _ => pure {}
addEntryFn := fun s (n, v) => s.insert n v
exportEntriesFn := fun s => s.toArray
saveEntriesFn := fun s => s.toArray
exportEntriesFn
asyncMode := .async
replay? := some fun _ newState newConsts s =>
newConsts.foldl (init := s) fun s c =>
@@ -145,10 +147,11 @@ def insert (ext : MapDeclarationExtension α) (env : Environment) (declName : Na
assert! env.asyncMayContain declName
ext.addEntry env (declName, val)
def find? [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name) : Option α :=
def find? [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name)
(includeServer := false) : Option α :=
match env.getModuleIdxFor? declName with
| some modIdx =>
match (ext.getModuleEntries env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
match (ext.getModuleEntries (includeServer := includeServer) env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
| some e => some e.2
| none => none
| none => (ext.findStateAsync env declName).find? declName

View File

@@ -506,6 +506,12 @@ structure Environment where
-/
base : Kernel.Environment
/--
Additional imported environment extension state for use in the language server. This field is
identical to `base.extensions` in other contexts. Access via
`getModuleEntries (includeServer := true)`.
-/
private serverBaseExts : Array EnvExtensionState := base.extensions
/--
Kernel environment task that is fulfilled when all asynchronously elaborated declarations are
finished, containing the resulting environment. Also collects the environment extension state of
all environment branches that contributed contained declarations.
@@ -536,6 +542,12 @@ structure Environment where
`findAsyncCore?`/`findStateAsync`; see there.
-/
private allRealizations : Task (NameMap AsyncConst) := .pure {}
/--
Indicates whether the environment is being used in an exported context, i.e. whether it should
provide access to only the data to be imported by other modules participating in the module
system.
-/
isExporting : Bool := false
deriving Nonempty
namespace Environment
@@ -549,6 +561,10 @@ def ofKernelEnv (env : Kernel.Environment) : Environment :=
def toKernelEnv (env : Environment) : Kernel.Environment :=
env.checked.get
/-- Updates `Environment.isExporting`. -/
def setExporting (env : Environment) (isExporting : Bool) : Environment :=
{ env with isExporting }
/-- Consistently updates synchronous and asynchronous parts of the environment without blocking. -/
private def modifyCheckedAsync (env : Environment) (f : Kernel.Environment Kernel.Environment) : Environment :=
{ env with checked := env.checked.map (sync := true) f, base := f env.base }
@@ -905,7 +921,8 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (
}
exts? := guard reportExts *> some (constPromise.result?.map (sync := true) fun
| some (_, exts, _) => exts
| none => env.toKernelEnv.extensions)
-- any value should work here, `base` does not block
| none => env.base.extensions)
consts := constPromise.result?.map (sync := true) fun
| some (_, _, consts) => .mk consts
| none => .mk (α := AsyncConsts) default
@@ -1378,7 +1395,15 @@ structure PersistentEnvExtension (α : Type) (β : Type) (σ : Type) where
name : Name
addImportedFn : Array (Array α) ImportM σ
addEntryFn : σ β σ
/-- Function to transform state into data that should always be imported into other modules. -/
exportEntriesFn : σ Array α
/--
Function to transform state into data that should be imported into other modules when the module
system is disabled. When it is enabled, the data is loaded only in the language server and
accessible via `getModuleEntries (includeServer := true)`. Conventionally, this is a superset of
the data returned by `exportEntriesFn`.
-/
saveEntriesFn : σ Array α
statsFn : σ Format
instance {α σ} [Inhabited σ] : Inhabited (PersistentEnvExtensionState α σ) :=
@@ -1391,14 +1416,21 @@ instance {α β σ} [Inhabited σ] : Inhabited (PersistentEnvExtension α β σ)
addImportedFn := fun _ => default,
addEntryFn := fun s _ => s,
exportEntriesFn := fun _ => #[],
saveEntriesFn := fun _ => #[],
statsFn := fun _ => Format.nil
}
namespace PersistentEnvExtension
def getModuleEntries {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment) (m : ModuleIdx) : Array α :=
-- `importedEntries` is identical on all environment branches, so `local` is always sufficient
(ext.toEnvExtension.getState (asyncMode := .local) env).importedEntries[m]!
/--
Returns the data saved by `ext.exportEntriesFn/saveEntriesFn` when `m` was elaborated. See docs on
the functions for details.
-/
def getModuleEntries {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
(env : Environment) (m : ModuleIdx) (includeServer := false) : Array α :=
let exts := if includeServer then env.serverBaseExts else env.base.extensions
-- safety: as in `getStateUnsafe`
unsafe (ext.toEnvExtension.getStateImpl exts).importedEntries[m]!
def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (b : β) : Environment :=
ext.toEnvExtension.modifyState env fun s =>
@@ -1435,10 +1467,14 @@ structure PersistentEnvExtensionDescr (α β σ : Type) where
addImportedFn : Array (Array α) ImportM σ
addEntryFn : σ β σ
exportEntriesFn : σ Array α
saveEntriesFn : σ Array α := exportEntriesFn
statsFn : σ Format := fun _ => Format.nil
asyncMode : EnvExtension.AsyncMode := .mainOnly
replay? : Option (ReplayFn σ) := none
attribute [inherit_doc PersistentEnvExtension.exportEntriesFn] PersistentEnvExtensionDescr.exportEntriesFn
attribute [inherit_doc PersistentEnvExtension.saveEntriesFn] PersistentEnvExtensionDescr.saveEntriesFn
unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ) := do
let pExts persistentEnvExtensionsRef.get
if pExts.any (fun ext => ext.name == descr.name) then throw (IO.userError s!"invalid environment extension, '{descr.name}' has already been used")
@@ -1457,6 +1493,7 @@ unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ]
addImportedFn := descr.addImportedFn,
addEntryFn := descr.addEntryFn,
exportEntriesFn := descr.exportEntriesFn,
saveEntriesFn := descr.saveEntriesFn,
statsFn := descr.statsFn
}
persistentEnvExtensionsRef.modify fun pExts => pExts.push (unsafeCast pExt)
@@ -1465,10 +1502,30 @@ unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ]
@[implemented_by registerPersistentEnvExtensionUnsafe]
opaque registerPersistentEnvExtension {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ)
@[extern "lean_save_module_data"]
opaque saveModuleData (fname : @& System.FilePath) (mod : @& Name) (data : @& ModuleData) : IO Unit
@[extern "lean_read_module_data"]
opaque readModuleData (fname : @& System.FilePath) : IO (ModuleData × CompactedRegion)
/--
Stores each given module data in the respective file name. Objects shared with prior parts are not
duplicated. Thus the data cannot be loaded with individual `readModuleData` calls but must loaded by
passing (a prefix of) the file names to `readModuleDataParts`. `mod` is used to determine an
arbitrary but deterministic base address for `mmap`.
-/
@[extern "lean_save_module_data_parts"]
opaque saveModuleDataParts (mod : @& Name) (parts : Array (System.FilePath × ModuleData)) : IO Unit
/--
Loads the module data from the given file names. The files must be (a prefix of) the result of a
`saveModuleDataParts` call.
-/
@[extern "lean_read_module_data_parts"]
opaque readModuleDataParts (fnames : @& Array System.FilePath) : IO (Array (ModuleData × CompactedRegion))
def saveModuleData (fname : System.FilePath) (mod : Name) (data : ModuleData) : IO Unit :=
saveModuleDataParts mod #[(fname, data)]
def readModuleData (fname : @& System.FilePath) : IO (ModuleData × CompactedRegion) := do
let parts readModuleDataParts #[fname]
assert! parts.size == 1
let some part := parts[0]? | unreachable!
return part
/--
Free compacted regions of imports. No live references to imported objects may exist at the time of invocation; in
@@ -1492,7 +1549,22 @@ unsafe def Environment.freeRegions (env : Environment) : IO Unit :=
TODO: statically check for this. -/
env.header.regions.forM CompactedRegion.free
def mkModuleData (env : Environment) : IO ModuleData := do
/-- The level of information to save/load. Each level includes all previous ones. -/
inductive OLeanLevel where
/-- Information from exported contexts. -/
| exported
/-- Environment extension state for the language server. -/
| server
/-- Private module data. -/
| «private»
deriving DecidableEq
def OLeanLevel.adjustFileName (base : System.FilePath) : OLeanLevel System.FilePath
| .exported => base
| .server => base.addExtension "server"
| .private => base.addExtension "private"
def mkModuleData (env : Environment) (level : OLeanLevel := .private) : IO ModuleData := do
let pExts persistentEnvExtensionsRef.get
let entries := pExts.map fun pExt => Id.run do
-- get state from `checked` at the end if `async`; it would otherwise panic
@@ -1500,19 +1572,37 @@ def mkModuleData (env : Environment) : IO ModuleData := do
if asyncMode matches .async then
asyncMode := .sync
let state := pExt.getState (asyncMode := asyncMode) env
(pExt.name, pExt.exportEntriesFn state)
(pExt.name, if level = .exported then pExt.exportEntriesFn state else pExt.saveEntriesFn state)
let kenv := env.toKernelEnv
let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
let constants := kenv.constants.foldStage2 (fun cs _ c => cs.push c) #[]
let env := env.setExporting (level != .private)
let constants := kenv.constants.foldStage2 (fun cs _ c => cs.push c) #[]
--let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
-- not all kernel constants may be exported
-- TODO: does not include cstage* constants from the old codegen
--let constants := constNames.filterMap env.find?
let constNames := constants.map (·.name)
return {
imports := env.header.imports
extraConstNames := env.checked.get.extraConstNames.toArray
constNames, constants, entries
}
register_builtin_option experimental.module : Bool := {
defValue := false
descr := "Enable module system (experimental)"
}
@[export lean_write_module]
def writeModule (env : Environment) (fname : System.FilePath) : IO Unit := do
saveModuleData fname env.mainModule ( mkModuleData env)
def writeModule (env : Environment) (fname : System.FilePath) (split := false) : IO Unit := do
if split then
let mkPart (level : OLeanLevel) :=
return (level.adjustFileName fname, ( mkModuleData env level))
saveModuleDataParts env.mainModule #[
( mkPart .exported),
( mkPart .server),
( mkPart .private)]
else
saveModuleData fname env.mainModule ( mkModuleData env)
/--
Construct a mapping from persistent extension name to extension index at the array of persistent extensions.
@@ -1526,10 +1616,9 @@ def mkExtNameMap (startingAt : Nat) : IO (Std.HashMap Name Nat) := do
result := result.insert descr.name i
return result
private def setImportedEntries (env : Environment) (mods : Array ModuleData) (startingAt : Nat := 0) : IO Environment := do
-- We work directly on the states array instead of `env` as `Environment.modifyState` introduces
-- significant overhead on such frequent calls
let mut states := env.base.extensions
private def setImportedEntries (states : Array EnvExtensionState) (mods : Array ModuleData)
(startingAt : Nat := 0) : IO (Array EnvExtensionState) := do
let mut states := states
let extDescrs persistentEnvExtensionsRef.get
/- For extensions starting at `startingAt`, ensure their `importedEntries` array have size `mods.size`. -/
for extDescr in extDescrs[startingAt:] do
@@ -1545,7 +1634,7 @@ private def setImportedEntries (env : Environment) (mods : Array ModuleData) (st
-- safety: as in `modifyState`
states := unsafe extDescrs[entryIdx]!.toEnvExtension.modifyStateImpl states fun s =>
{ s with importedEntries := s.importedEntries.set! modIdx entries }
return env.setCheckedSync { env.base with extensions := states }
return states
/--
"Forward declaration" needed for updating the attribute table with user-defined attributes.
@@ -1584,7 +1673,7 @@ where
-- This branch is executed when `pExtDescrs[i]` is the extension associated with the `init` attribute, and
-- a user-defined persistent extension is imported.
-- Thus, we invoke `setImportedEntries` to update the array `importedEntries` with the entries for the new extensions.
env setImportedEntries env mods prevSize
env := env.setCheckedSync { env.base with extensions := ( setImportedEntries env.base.extensions mods prevSize) }
-- See comment at `updateEnvAttributesRef`
env updateEnvAttributes env
loop (i + 1) env
@@ -1595,7 +1684,7 @@ structure ImportState where
moduleNameSet : NameHashSet := {}
moduleNames : Array Name := #[]
moduleData : Array ModuleData := #[]
regions : Array CompactedRegion := #[]
parts : Array (Array (ModuleData × CompactedRegion)) := #[]
def throwAlreadyImported (s : ImportState) (const2ModIdx : Std.HashMap Name ModuleIdx) (modIdx : Nat) (cname : Name) : IO α := do
let modName := s.moduleNames[modIdx]!
@@ -1607,7 +1696,8 @@ abbrev ImportStateM := StateRefT ImportState IO
@[inline] nonrec def ImportStateM.run (x : ImportStateM α) (s : ImportState := {}) : IO (α × ImportState) :=
x.run s
partial def importModulesCore (imports : Array Import) : ImportStateM Unit := do
partial def importModulesCore (imports : Array Import) (level := OLeanLevel.private) :
ImportStateM Unit := do
for i in imports do
if i.runtimeOnly || ( get).moduleNameSet.contains i.module then
continue
@@ -1615,12 +1705,22 @@ partial def importModulesCore (imports : Array Import) : ImportStateM Unit := do
let mFile findOLean i.module
unless ( mFile.pathExists) do
throw <| IO.userError s!"object file '{mFile}' of module {i.module} does not exist"
let (mod, region) readModuleData mFile
importModulesCore mod.imports
let mut fnames := #[mFile]
if level != OLeanLevel.exported then
let sFile := OLeanLevel.server.adjustFileName mFile
if ( sFile.pathExists) then
fnames := fnames.push sFile
if level == OLeanLevel.private then
let pFile := OLeanLevel.private.adjustFileName mFile
if ( pFile.pathExists) then
fnames := fnames.push pFile
let parts readModuleDataParts fnames
let some (mod, _) := parts[if level = .exported then 0 else parts.size - 1]? | unreachable!
importModulesCore (level := level) mod.imports
modify fun s => { s with
moduleData := s.moduleData.push mod
regions := s.regions.push region
moduleNames := s.moduleNames.push i.module
parts := s.parts.push parts
}
/--
@@ -1684,14 +1784,16 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
extensions := exts
header := {
trustLevel, imports
regions := s.regions
regions := s.parts.flatMap (·.map (·.2))
moduleNames := s.moduleNames
moduleData := s.moduleData
}
}
realizedImportedConsts? := none
}
env setImportedEntries env s.moduleData
env := env.setCheckedSync { env.base with extensions := ( setImportedEntries env.base.extensions s.moduleData) }
let serverData := s.parts.filterMap fun parts => (parts[1]? <|> parts[0]?).map Prod.fst
env := { env with serverBaseExts := ( setImportedEntries env.base.extensions serverData) }
if leakEnv then
/- Mark persistent a first time before `finalizePersistenExtensions`, which
avoids costly MT markings when e.g. an interpreter closure (which
@@ -1738,13 +1840,13 @@ environment's constant map can be accessed without `loadExts`, many functions th
-/
def importModules (imports : Array Import) (opts : Options) (trustLevel : UInt32 := 0)
(plugins : Array System.FilePath := #[]) (leakEnv := false) (loadExts := false)
: IO Environment := profileitIO "import" opts do
(level := OLeanLevel.private) : IO Environment := profileitIO "import" opts do
for imp in imports do
if imp.module matches .anonymous then
throw <| IO.userError "import failed, trying to import module with anonymous name"
withImporting do
plugins.forM Lean.loadPlugin
let (_, s) importModulesCore imports |>.run
let (_, s) importModulesCore (level := level) imports |>.run
finalizeImport (leakEnv := leakEnv) (loadExts := loadExts) s imports opts trustLevel
/--

View File

@@ -93,18 +93,17 @@ structure SnapshotTask (α : Type) where
Cancellation token that can be set by the server to cancel the task when it detects the results
are not needed anymore.
-/
cancelTk? : Option IO.CancelToken := none
cancelTk? : Option IO.CancelToken
/-- Underlying task producing the snapshot. -/
task : Task α
deriving Nonempty, Inhabited
/-- Creates a snapshot task from the syntax processed by the task and a `BaseIO` action. -/
def SnapshotTask.ofIO (stx? : Option Syntax)
def SnapshotTask.ofIO (stx? : Option Syntax) (cancelTk? : Option IO.CancelToken)
(reportingRange? : Option String.Range := defaultReportingRange? stx?) (act : BaseIO α) :
BaseIO (SnapshotTask α) := do
return {
stx?
reportingRange?
stx?, reportingRange?, cancelTk?
task := ( BaseIO.asTask act)
}
@@ -114,6 +113,7 @@ def SnapshotTask.finished (stx? : Option Syntax) (a : α) : SnapshotTask α wher
-- irrelevant when already finished
reportingRange? := none
task := .pure a
cancelTk? := none
/-- Transforms a task's output without changing the processed syntax. -/
def SnapshotTask.map (t : SnapshotTask α) (f : α β) (stx? : Option Syntax := t.stx?)

View File

@@ -397,7 +397,7 @@ where
diagnostics := oldProcessed.diagnostics
result? := some {
cmdState := oldProcSuccess.cmdState
firstCmdSnap := { stx? := none, task := prom.result! } } }
firstCmdSnap := { stx? := none, task := prom.result!, cancelTk? := cancelTk } } }
else
return .finished newStx oldProcessed) } }
else return old
@@ -450,7 +450,7 @@ where
processHeader (stx : Syntax) (parserState : Parser.ModuleParserState) :
LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do
let ctx read
SnapshotTask.ofIO stx (some 0, ctx.input.endPos) <|
SnapshotTask.ofIO stx none (some 0, ctx.input.endPos) <|
ReaderT.run (r := ctx) <| -- re-enter reader in new task
withHeaderExceptions (α := HeaderProcessedSnapshot) ({ · with result? := none }) do
let setup match ( setupImports stx) with
@@ -507,7 +507,7 @@ where
infoTree? := cmdState.infoState.trees[0]!
result? := some {
cmdState
firstCmdSnap := { stx? := none, task := prom.result! }
firstCmdSnap := { stx? := none, task := prom.result!, cancelTk? := cancelTk }
}
}
@@ -523,17 +523,19 @@ where
-- from `old`
if let some oldNext := old.nextCmdSnap? then do
let newProm IO.Promise.new
let cancelTk IO.CancelToken.new
-- can reuse range, syntax unchanged
BaseIO.chainTask (sync := true) old.resultSnap.task fun oldResult =>
-- also wait on old command parse snapshot as parsing is cheap and may allow for
-- elaboration reuse
BaseIO.chainTask (sync := true) oldNext.task fun oldNext => do
let cancelTk IO.CancelToken.new
parseCmd oldNext newParserState oldResult.cmdState newProm sync cancelTk ctx
prom.resolve <| { old with nextCmdSnap? := some {
stx? := none
reportingRange? := some newParserState.pos, ctx.input.endPos
task := newProm.result! } }
task := newProm.result!
cancelTk? := cancelTk
} }
else prom.resolve old -- terminal command, we're done!
-- fast path, do not even start new task for this snapshot (see [Incremental Parsing])
@@ -615,15 +617,16 @@ where
})
let diagnostics Snapshot.Diagnostics.ofMessageLog msgLog
-- use per-command cancellation token for elaboration so that
-- use per-command cancellation token for elaboration so that cancellation of further commands
-- does not affect current command
let elabCmdCancelTk IO.CancelToken.new
prom.resolve {
diagnostics, nextCmdSnap?
stx := stx', parserState := parserState'
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some elabCmdCancelTk }
resultSnap := { stx? := stx', reportingRange? := initRange?, task := resultPromise.result! }
infoTreeSnap := { stx? := stx', reportingRange? := initRange?, task := finishedPromise.result! }
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result! }
resultSnap := { stx? := stx', reportingRange? := initRange?, task := resultPromise.result!, cancelTk? := none }
infoTreeSnap := { stx? := stx', reportingRange? := initRange?, task := finishedPromise.result!, cancelTk? := none }
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result!, cancelTk? := none }
}
let cmdState doElab stx cmdState beginPos
{ old? := old?.map fun old => old.stx, old.elabSnap, new := elabPromise }
@@ -665,8 +668,8 @@ where
-- We want to trace all of `CommandParsedSnapshot` but `traceTask` is part of it, so let's
-- create a temporary snapshot tree containing all tasks but it
let snaps := #[
{ stx? := stx', task := elabPromise.result!.map (sync := true) toSnapshotTree },
{ stx? := stx', task := resultPromise.result!.map (sync := true) toSnapshotTree }] ++
{ stx? := stx', task := elabPromise.result!.map (sync := true) toSnapshotTree, cancelTk? := none },
{ stx? := stx', task := resultPromise.result!.map (sync := true) toSnapshotTree, cancelTk? := none }] ++
cmdState.snapshotTasks
let tree := SnapshotTree.mk { diagnostics := .empty } snaps
BaseIO.bindTask ( tree.waitAll) fun _ => do
@@ -690,6 +693,7 @@ where
stx? := none
reportingRange? := initRange?
task := traceTask
cancelTk? := none
}
if let some next := next? then
-- We're definitely off the fast-forwarding path now

View File

@@ -66,7 +66,10 @@ inductive MessageData where
`alt` may nest any structured message,
for example `ofGoal` to approximate a tactic state widget,
and, if necessary, even other widget instances
(for which approximations are computed recursively). -/
(for which approximations are computed recursively).
Note that unlike with `Widget.savePanelWidgetInfo`,
the infoview will not pass any additional props to the widget instance. -/
| ofWidget : Widget.WidgetInstance MessageData MessageData
/-- `withContext ctx d` specifies the pretty printing context `(env, mctx, lctx, opts)` for the nested expressions in `d`. -/
| withContext : MessageDataContext MessageData MessageData
@@ -506,13 +509,6 @@ def indentExpr (e : Expr) : MessageData :=
def aquote (msg : MessageData) : MessageData :=
"" ++ msg ++ ""
/-- Quote `e` using `「` and `」` if `e` is not a free variable, constant, or literal. -/
def quoteIfNotAtom (e : Expr) : MessageData :=
if e.isFVar || e.isConst || e.isLit then
e
else
aquote e
class AddMessageContext (m : Type Type) where
/--
Without context, a `MessageData` object may be missing information

View File

@@ -2279,6 +2279,7 @@ def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
initHeartbeats := ( IO.getNumHeartbeats)
}
let (env, exTask, dyn) env.realizeConst forConst constName (realizeAndReport coreCtx)
-- Realizations cannot be cancelled as their result is shared across elaboration runs
let exAct Core.wrapAsyncAsSnapshot (cancelTk? := none) fun
| none => return
| some ex => do
@@ -2286,6 +2287,7 @@ def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
Core.logSnapshotTask {
stx? := none
task := ( BaseIO.mapTask (t := exTask) exAct)
cancelTk? := none
}
if let some res := dyn.get? RealizeConstantResult then
let mut snap := res.snap

View File

@@ -52,6 +52,8 @@ builtin_initialize registerTraceClass `grind.split.candidate
builtin_initialize registerTraceClass `grind.split.resolved
builtin_initialize registerTraceClass `grind.beta
builtin_initialize registerTraceClass `grind.mbtc
builtin_initialize registerTraceClass `grind.ext
builtin_initialize registerTraceClass `grind.ext.candidate
/-! Trace options for `grind` developers -/
builtin_initialize registerTraceClass `grind.debug
@@ -74,5 +76,8 @@ builtin_initialize registerTraceClass `grind.debug.mbtc
builtin_initialize registerTraceClass `grind.debug.ematch
builtin_initialize registerTraceClass `grind.debug.proveEq
builtin_initialize registerTraceClass `grind.debug.pushNewFact
builtin_initialize registerTraceClass `grind.debug.ematch.activate
builtin_initialize registerTraceClass `grind.debug.appMap
builtin_initialize registerTraceClass `grind.debug.ext
end Lean

View File

@@ -22,50 +22,17 @@ namespace Lean
builtin_initialize registerTraceClass `grind.cutsat
builtin_initialize registerTraceClass `grind.cutsat.model
builtin_initialize registerTraceClass `grind.cutsat.subst
builtin_initialize registerTraceClass `grind.cutsat.eq
builtin_initialize registerTraceClass `grind.cutsat.eq.unsat (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.eq.trivial (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.assert
builtin_initialize registerTraceClass `grind.cutsat.assert.dvd
builtin_initialize registerTraceClass `grind.cutsat.dvd
builtin_initialize registerTraceClass `grind.cutsat.dvd.update (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.dvd.unsat (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.dvd.trivial (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.dvd.solve (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.dvd.solve.combine (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.dvd.solve.elim (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.internalize
builtin_initialize registerTraceClass `grind.cutsat.internalize.term (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.assert.trivial
builtin_initialize registerTraceClass `grind.cutsat.assert.unsat
builtin_initialize registerTraceClass `grind.cutsat.assert.store
builtin_initialize registerTraceClass `grind.cutsat.assert.le
builtin_initialize registerTraceClass `grind.cutsat.le
builtin_initialize registerTraceClass `grind.cutsat.le.unsat (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.le.trivial (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.le.lower (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.le.upper (inherited := true)
builtin_initialize registerTraceClass `grind.cutsat.assign
builtin_initialize registerTraceClass `grind.cutsat.conflict
builtin_initialize registerTraceClass `grind.cutsat.diseq
builtin_initialize registerTraceClass `grind.cutsat.diseq.trivial (inherited := true)
builtin_initialize registerTraceClass `grind.debug.cutsat.eq
builtin_initialize registerTraceClass `grind.debug.cutsat.dvd.le
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq
builtin_initialize registerTraceClass `grind.debug.cutsat.diseq.split
builtin_initialize registerTraceClass `grind.debug.cutsat.backtrack
builtin_initialize registerTraceClass `grind.debug.cutsat.search
builtin_initialize registerTraceClass `grind.debug.cutsat.cooper
builtin_initialize registerTraceClass `grind.debug.cutsat.cooper.diseq
builtin_initialize registerTraceClass `grind.debug.cutsat.conflict
builtin_initialize registerTraceClass `grind.debug.cutsat.assign
builtin_initialize registerTraceClass `grind.debug.cutsat.subst
builtin_initialize registerTraceClass `grind.debug.cutsat.getBestLower
builtin_initialize registerTraceClass `grind.debug.cutsat.nat
builtin_initialize registerTraceClass `grind.debug.cutsat.proof
builtin_initialize registerTraceClass `grind.debug.cutsat.search
builtin_initialize registerTraceClass `grind.debug.cutsat.search.split (inherited := true)
builtin_initialize registerTraceClass `grind.debug.cutsat.search.assign (inherited := true)
builtin_initialize registerTraceClass `grind.debug.cutsat.search.conflict (inherited := true)
builtin_initialize registerTraceClass `grind.debug.cutsat.search.backtrack (inherited := true)
builtin_initialize registerTraceClass `grind.debug.cutsat.internalize
builtin_initialize registerTraceClass `grind.debug.cutsat.markTerm
builtin_initialize registerTraceClass `grind.debug.cutsat.natCast
end Lean

View File

@@ -34,7 +34,7 @@ def DvdCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : DvdC
let q := c₂.p
let d := Int.ofNat (a * c₂.d).natAbs
let p := (q.mul a |>.combine (p.mul (-b)))
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
return { d, p, h := .subst x c₁ c₂ }
partial def DvdCnstr.applySubsts (c : DvdCnstr) : GoalM DvdCnstr := withIncRecDepth do
@@ -46,21 +46,20 @@ partial def DvdCnstr.applySubsts (c : DvdCnstr) : GoalM DvdCnstr := withIncRecDe
/-- Asserts divisibility constraint. -/
partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
if ( inconsistent) then return ()
trace[grind.cutsat.dvd] "{← c.pp}"
trace[grind.cutsat.assert] "{← c.pp}"
let c c.norm.applySubsts
if c.isUnsat then
trace[grind.cutsat.dvd.unsat] "{← c.pp}"
trace[grind.cutsat.assert.unsat] "{← c.pp}"
setInconsistent (.dvd c)
return ()
if c.isTrivial then
trace[grind.cutsat.dvd.trivial] "{← c.pp}"
trace[grind.cutsat.assert.trivial] "{← c.pp}"
return ()
let d₁ := c.d
let .add a₁ x p₁ := c.p | c.throwUnexpected
if ( c.satisfied) == .false then
resetAssignmentFrom x
if let some c' := ( get').dvds[x]! then
trace[grind.cutsat.dvd.solve] "{← c.pp}, {← c'.pp}"
let d₂ := c'.d
let .add a₂ _ p₂ := c'.p | c'.throwUnexpected
let (d, α, β) := gcdExt (a₁*d₂) (a₂*d₁)
@@ -75,16 +74,14 @@ partial def DvdCnstr.assert (c : DvdCnstr) : GoalM Unit := withIncRecDepth do
let α_d₂_p₁ := p₁.mul (α*d₂)
let β_d₁_p₂ := p₂.mul (β*d₁)
let combine := { d := d₁*d₂, p := .add d x (α_d₂_p₁.combine β_d₁_p₂), h := .solveCombine c c' : DvdCnstr }
trace[grind.cutsat.dvd.solve.combine] "{← combine.pp}"
modify' fun s => { s with dvds := s.dvds.set x none}
combine.assert
let a₂_p₁ := p₁.mul a₂
let a₁_p₂ := p₂.mul (-a₁)
let elim := { d, p := a₂_p₁.combine a₁_p₂, h := .solveElim c c' : DvdCnstr }
trace[grind.cutsat.dvd.solve.elim] "{← elim.pp}"
elim.assert
else
trace[grind.cutsat.dvd.update] "{← c.pp}"
trace[grind.cutsat.assert.store] "{← c.pp}"
c.p.updateOccs
modify' fun s => { s with dvds := s.dvds.set x (some c) }
@@ -97,7 +94,6 @@ def propagateIntDvd (e : Expr) : GoalM Unit := do
if ( isEqTrue e) then
let p toPoly b
let c := { d, p, h := .core e : DvdCnstr }
trace[grind.cutsat.assert.dvd] "{← c.pp}"
c.assert
else if ( isEqFalse e) then
pushNewFact <| mkApp4 (mkConst ``Int.Linear.of_not_dvd) a b reflBoolTrue (mkOfEqFalseCore e ( mkEqFalseProof e))

View File

@@ -38,12 +38,12 @@ def DiseqCnstr.applyEq (a : Int) (x : Var) (c₁ : EqCnstr) (b : Int) (c₂ : Di
let p := c₁.p
let q := c₂.p
let p := p.mul b |>.combine (q.mul (-a))
trace[grind.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c₁.pp}, {← c₂.pp}"
return { p, h := .subst x c₁ c₂ }
partial def DiseqCnstr.applySubsts (c : DiseqCnstr) : GoalM DiseqCnstr := withIncRecDepth do
let some (x, c₁, p) c.p.substVar | return c
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
applySubsts { p, h := .subst x c₁ c }
/--
@@ -68,10 +68,11 @@ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
trace[grind.cutsat.assert] "{← c.pp}"
let c c.norm.applySubsts
if c.p.isUnsatDiseq then
trace[grind.cutsat.assert.unsat] "{← c.pp}"
setInconsistent (.diseq c)
return ()
if c.isTrivial then
trace[grind.cutsat.diseq.trivial] "{← c.pp}"
trace[grind.cutsat.assert.trivial] "{← c.pp}"
return ()
let k := c.p.gcdCoeffs c.p.getConst
let c := if k == 1 then
@@ -82,7 +83,7 @@ def DiseqCnstr.assert (c : DiseqCnstr) : GoalM Unit := do
return ()
let .add _ x _ := c.p | c.throwUnexpected
c.p.updateOccs
trace[grind.cutsat.diseq] "{← c.pp}"
trace[grind.cutsat.assert.store] "{← c.pp}"
modify' fun s => { s with diseqs := s.diseqs.modify x (·.push c) }
if ( c.satisfied) == .false then
resetAssignmentFrom x
@@ -108,7 +109,7 @@ where
partial def EqCnstr.applySubsts (c : EqCnstr) : GoalM EqCnstr := withIncRecDepth do
let some (x, c₁, p) c.p.substVar | return c
trace[grind.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
trace[grind.debug.cutsat.subst] "{← getVar x}, {← c.pp}, {← c₁.pp}"
applySubsts { p, h := .subst x c₁ c : EqCnstr }
private def updateDvdCnstr (a : Int) (x : Var) (c : EqCnstr) (y : Var) : GoalM Unit := do
@@ -197,10 +198,11 @@ def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
trace[grind.cutsat.assert] "{← c.pp}"
let c c.norm.applySubsts
if c.p.isUnsatEq then
trace[grind.cutsat.assert.unsat] "{← c.pp}"
setInconsistent (.eq c)
return ()
if c.isTrivial then
trace[grind.cutsat.eq.trivial] "{← c.pp}"
trace[grind.cutsat.assert.trivial] "{← c.pp}"
return ()
let k := c.p.gcdCoeffs'
if c.p.getConst % k > 0 then
@@ -210,9 +212,9 @@ def EqCnstr.assertImpl (c : EqCnstr) : GoalM Unit := do
c
else
{ p := c.p.div k, h := .divCoeffs c }
trace[grind.cutsat.eq] "{← c.pp}"
let some (k, x) := c.p.pickVarToElim? | c.throwUnexpected
trace[grind.debug.cutsat.subst] ">> {← getVar x}, {← c.pp}"
trace[grind.cutsat.assert.store] "{← c.pp}"
modify' fun s => { s with
elimEqs := s.elimEqs.set x (some c)
elimStack := x :: s.elimStack
@@ -252,7 +254,6 @@ private def processNewNatEq (a b : Expr) : GoalM Unit := do
@[export lean_process_cutsat_eq]
def processNewEqImpl (a b : Expr) : GoalM Unit := do
trace[grind.debug.cutsat.eq] "{a} = {b}"
match ( foreignTerm? a), ( foreignTerm? b) with
| none, none => processNewIntEq a b
| some .nat, some .nat => processNewNatEq a b
@@ -271,7 +272,6 @@ private def processNewIntLitEq (a ke : Expr) : GoalM Unit := do
@[export lean_process_cutsat_eq_lit]
def processNewEqLitImpl (a ke : Expr) : GoalM Unit := do
trace[grind.debug.cutsat.eq] "{a} = {ke}"
match ( foreignTerm? a) with
| none => processNewIntLitEq a ke
| some .nat => processNewNatEq a ke
@@ -294,12 +294,10 @@ private def processNewNatDiseq (a b : Expr) : GoalM Unit := do
let rhs' toLinearExpr ( rhs.denoteAsIntExpr ctx) gen
let p := lhs'.sub rhs' |>.norm
let c := { p, h := .coreNat a b lhs rhs lhs' rhs' : DiseqCnstr }
trace[grind.debug.cutsat.nat] "{← c.pp}"
c.assert
@[export lean_process_cutsat_diseq]
def processNewDiseqImpl (a b : Expr) : GoalM Unit := do
trace[grind.debug.cutsat.diseq] "{a} ≠ {b}"
match ( foreignTerm? a), ( foreignTermOrLit? b) with
| none, none => processNewIntDiseq a b
| some .nat, some .nat => processNewNatDiseq a b
@@ -342,7 +340,7 @@ private def isForbiddenParent (parent? : Option Expr) (k : SupportedTermKind) :
private def internalizeInt (e : Expr) : GoalM Unit := do
if ( hasVar e) then return ()
let p toPoly e
trace[grind.cutsat.internalize] "{aquote e}:= {← p.pp}"
trace[grind.debug.cutsat.internalize] "{aquote e}:= {← p.pp}"
let x mkVar e
if p == .add 1 x (.num 0) then
-- It is pointless to assert `x = x`
@@ -390,6 +388,26 @@ private def propagateToNat (e : Expr) : GoalM Unit := do
let_expr Int.toNat a := e | return ()
pushNewFact <| mkApp (mkConst ``Int.OfNat.ofNat_toNat) a
private def internalizeNat (e : Expr) : GoalM Unit := do
let e' : Int.OfNat.Expr Int.OfNat.toOfNatExpr e
let gen getGeneration e
let ctx getForeignVars .nat
let e'' : Expr e'.denoteAsIntExpr ctx
-- If `e''` is of the form `NatCast.natCast e`, then it is wasteful to
-- assert an equality
match_expr e'' with
| NatCast.natCast _ _ a => if e == a then return ()
| _ => pure ()
let e'' : Int.Linear.Expr toLinearExpr e'' gen
let p := e''.norm
let natCast_e shareCommon (mkIntNatCast e)
internalize natCast_e gen
trace[grind.debug.cutsat.internalize] "{aquote natCast_e}:= {← p.pp}"
let x mkVar natCast_e
modify' fun s => { s with foreignDef := s.foreignDef.insert { expr := e } x }
let c := { p := .add (-1) x p, h := .defnNat e' x e'' : EqCnstr }
c.assert
/--
Internalizes an integer (and `Nat`) expression. Here are the different cases that are handled.
@@ -410,11 +428,13 @@ def internalize (e : Expr) (parent? : Option Expr) : GoalM Unit := do
| .num => pure ()
| _ => internalizeInt e
else if type.isConstOf ``Nat then
if ( hasForeignVar e) then return ()
discard <| mkForeignVar e .nat
match k with
| .sub => propagateNatSub e
| .natAbs => propagateNatAbs e
| .toNat => propagateToNat e
| _ => pure ()
| .num => pure ()
| _ => internalizeNat e
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -24,7 +24,6 @@ def mkForeignVar (e : Expr) (t : ForeignType) : GoalM Var := do
foreignVars := s.foreignVars.insert t (vars.push e)
foreignVarMap := s.foreignVarMap.insert { expr := e} (x, t)
}
trace[grind.debug.cutsat.markTerm] "mkForeignVar: {e}"
markAsCutsatTerm e
return x

View File

@@ -100,24 +100,24 @@ where
@[export lean_grind_cutsat_assert_le]
def LeCnstr.assertImpl (c : LeCnstr) : GoalM Unit := do
if ( inconsistent) then return ()
trace[grind.cutsat.assert] "{← c.pp}"
let c c.norm.applySubsts
if c.isUnsat then
trace[grind.cutsat.le.unsat] "{← c.pp}"
trace[grind.cutsat.assert.unsat] "{← c.pp}"
setInconsistent (.le c)
return ()
if c.isTrivial then
trace[grind.cutsat.le.trivial] "{← c.pp}"
trace[grind.cutsat.assert.trivial] "{← c.pp}"
return ()
let .add a x _ := c.p | c.throwUnexpected
if ( findEq c) then
return ()
let c refineWithDiseq c
trace[grind.cutsat.assert.store] "{← c.pp}"
if a < 0 then
trace[grind.cutsat.le.lower] "{← c.pp}"
c.p.updateOccs
modify' fun s => { s with lowers := s.lowers.modify x (·.push c) }
else
trace[grind.cutsat.le.upper] "{← c.pp}"
c.p.updateOccs
modify' fun s => { s with uppers := s.uppers.modify x (·.push c) }
if ( c.satisfied) == .false then
@@ -145,7 +145,6 @@ def propagateIntLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
pure { p, h := .core e : LeCnstr }
else
pure { p := p.mul (-1) |>.addConst 1, h := .coreNeg e p : LeCnstr }
trace[grind.cutsat.assert.le] "{← c.pp}"
c.assert
def propagateNatLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
@@ -155,7 +154,6 @@ def propagateNatLe (e : Expr) (eqTrue : Bool) : GoalM Unit := do
let lhs' toLinearExpr ( lhs.denoteAsIntExpr ctx) gen
let rhs' toLinearExpr ( rhs.denoteAsIntExpr ctx) gen
let p := lhs'.sub rhs' |>.norm
trace[grind.debug.cutsat.nat] "{← p.pp}"
let c if eqTrue then
pure { p, h := .coreNat e lhs rhs lhs' rhs' : LeCnstr }
else

View File

@@ -122,7 +122,7 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Rat)) := do
r := r.qsort fun (e₁, _) (e₂, _) => e₁.lt e₂
if ( isTracingEnabledFor `grind.cutsat.model) then
for (x, v) in r do
trace[grind.cutsat.model] "{quoteIfNotAtom x} := {v}"
trace[grind.cutsat.model] "{quoteIfArithTerm x} := {v}"
return r
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -136,9 +136,20 @@ def assertDenoteAsIntNonneg (e : Expr) : GoalM Unit := withIncRecDepth do
let lhs' : Int.Linear.Expr := .num 0
let rhs' toLinearExpr ( rhs.denoteAsIntExpr ctx) gen
let p := lhs'.sub rhs' |>.norm
trace[grind.debug.cutsat.nat] "{← p.pp}"
let c := { p, h := .denoteAsIntNonneg rhs rhs' : LeCnstr }
trace[grind.cutsat.assert.le] "{← c.pp}"
c.assert
/--
Given `x` whose denotation is `e`, if `e` is of the form `NatCast.natCast a`,
asserts that it is nonnegative.
-/
def assertNatCast (e : Expr) (x : Var) : GoalM Unit := do
let_expr NatCast.natCast _ inst a := e | return ()
let_expr instNatCastInt := inst | return ()
if ( get').foreignDef.contains { expr := a } then return ()
let n mkForeignVar a .nat
let p := .add (-1) x (.num 0)
let c := { p, h := .denoteAsIntNonneg (.var n) (.var x) : LeCnstr}
c.assert
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -142,6 +142,9 @@ partial def EqCnstr.toExprProof (c' : EqCnstr) : ProofM Expr := caching c' do
| .defn e p =>
let some x := ( get').varMap.find? { expr := e } | throwError "`grind` internal error, missing cutsat variable{indentExpr e}"
return mkApp6 (mkConst ``Int.Linear.eq_def) ( getContext) (toExpr x) ( mkPolyDecl p) ( mkPolyDecl c'.p) reflBoolTrue ( mkEqRefl e)
| .defnNat e x e' =>
let h := mkApp2 (mkConst ``Int.OfNat.Expr.eq_denoteAsInt) ( getForeignContext .nat) ( mkNatExprDecl e)
return mkApp6 (mkConst ``Int.Linear.eq_def') ( getContext) (toExpr x) ( mkExprDecl e') ( mkPolyDecl c'.p) reflBoolTrue h
| .norm c =>
return mkApp5 (mkConst ``Int.Linear.eq_norm) ( getContext) ( mkPolyDecl c.p) ( mkPolyDecl c'.p) reflBoolTrue ( c.toExprProof)
| .divCoeffs c =>
@@ -298,7 +301,6 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := caching c' do
( getContext) ( mkPolyDecl p₁) ( mkPolyDecl p₂) ( mkPolyDecl c₃.p) (toExpr c₃.d) (toExpr s.k) (toExpr coeff) ( mkPolyDecl c'.p) ( s.toExprProof) reflBoolTrue
partial def DiseqCnstr.toExprProof (c' : DiseqCnstr) : ProofM Expr := caching c' do
trace[grind.debug.cutsat.proof] "{← c'.pp}"
match c'.h with
| .core0 a zero =>
mkDiseqProof a zero
@@ -349,7 +351,6 @@ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s
-- `pred` is an expressions of the form `cooper_*_split ...` with type `Nat → Prop`
let mut k := n
let mut result := base -- `OrOver k (cooper_*_splti)
trace[grind.debug.cutsat.proof] "orOver_cases {n}"
result := mkApp3 (mkConst ``Int.Linear.orOver_cases) (toExpr (n-1)) pred result
for (fvarId, c) in hs do
let type := mkApp pred (toExpr (k-1))
@@ -362,7 +363,6 @@ partial def CooperSplit.toExprProof (s : CooperSplit) : ProofM Expr := caching s
return result
partial def UnsatProof.toExprProofCore (h : UnsatProof) : ProofM Expr := do
trace[grind.debug.cutsat.proof] "{← h.pp}"
match h with
| .le c =>
return mkApp4 (mkConst ``Int.Linear.le_unsat) ( getContext) ( mkPolyDecl c.p) reflBoolTrue ( c.toExprProof)
@@ -389,7 +389,6 @@ def UnsatProof.toExprProof (h : UnsatProof) : GoalM Expr := do
withProofContext do h.toExprProofCore
def setInconsistent (h : UnsatProof) : GoalM Unit := do
trace[grind.debug.cutsat.conflict] "setInconsistent [{← inconsistent}]: {← h.pp}"
if ( get').caseSplits then
-- Let the search procedure in `SearchM` resolve the conflict.
modify' fun s => { s with conflict? := some h }
@@ -419,7 +418,7 @@ private def markAsFound (fvarId : FVarId) : CollectDecVarsM Unit := do
mutual
partial def EqCnstr.collectDecVars (c' : EqCnstr) : CollectDecVarsM Unit := do unless ( alreadyVisited c') do
match c'.h with
| .core0 .. | .core .. | .coreNat .. | .defn .. => return () -- Equalities coming from the core never contain cutsat decision variables
| .core0 .. | .core .. | .coreNat .. | .defn .. | .defnNat .. => return () -- Equalities coming from the core never contain cutsat decision variables
| .norm c | .divCoeffs c => c.collectDecVars
| .subst _ c₁ c₂ | .ofLeGe c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars

View File

@@ -27,7 +27,6 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
let p₁' := p.mul b |>.combine (q.mul (-a))
let p₁' := p₁'.addConst <| if left then b*k else (-a)*k
let c₁' := { p := p₁', h := .cooper cs : LeCnstr }
trace[grind.debug.cutsat.cooper] "{← c₁'.pp}"
c₁'.assert
if ( inconsistent) then return ()
let d := if left then a else b
@@ -35,7 +34,6 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
let p₂' := if left then p else q
let p₂' := p₂'.addConst k
let c₂' := { d, p := p₂', h := .cooper₁ cs : DvdCnstr }
trace[grind.debug.cutsat.cooper] "dvd₁: {← c₂'.pp}"
c₂'.assert
if ( inconsistent) then return ()
let some c₃ := c₃? | return ()
@@ -51,7 +49,6 @@ def CooperSplit.assert (cs : CooperSplit) : GoalM Unit := do
let p₃' := q.mul (-c) |>.combine (s.mul b)
let p₃' := p₃'.addConst (-c*k)
{ d := b*d, p := p₃', h := .cooper₂ cs : DvdCnstr }
trace[grind.debug.cutsat.cooper] "dvd₂: {← c₃'.pp}"
c₃'.assert
private def checkIsNextVar (x : Var) : GoalM Unit := do
@@ -59,7 +56,7 @@ private def checkIsNextVar (x : Var) : GoalM Unit := do
throwError "`grind` internal error, assigning variable out of order"
private def traceAssignment (x : Var) (v : Rat) : GoalM Unit := do
trace[grind.cutsat.assign] "{quoteIfNotAtom (← getVar x)} := {v}"
trace[grind.debug.cutsat.search.assign] "{quoteIfArithTerm (← getVar x)} := {v}"
private def setAssignment (x : Var) (v : Rat) : GoalM Unit := do
checkIsNextVar x
@@ -88,7 +85,6 @@ where
modify' fun s => { s with assignment := s.assignment.set x 0 }
let some v c.p.eval? | c.throwUnexpected
let v := (-v) / a
trace[grind.debug.cutsat.assign] "{← getVar x}, {← c.pp}, {v}"
traceAssignment x v
modify' fun s => { s with assignment := s.assignment.set x v }
go xs
@@ -108,7 +104,6 @@ def tightUsingDvd (c : LeCnstr) (dvd? : Option DvdCnstr) : GoalM LeCnstr := do
let b₂ := c.p.getConst
if (b₂ - b₁) % d != 0 then
let b₂' := b₁ - d * ((b₁ - b₂) / d)
trace[grind.debug.cutsat.dvd.le] "[pos] {← c.pp}, {← dvd.pp}, {b₂'}"
let p := c.p.addConst (b₂'-b₂)
return { p, h := .dvdTight dvd c }
if eqCoeffs dvd.p c.p true then
@@ -116,7 +111,6 @@ def tightUsingDvd (c : LeCnstr) (dvd? : Option DvdCnstr) : GoalM LeCnstr := do
let b₂ := c.p.getConst
if (b₂ - b₁) % d != 0 then
let b₂' := b₁ - d * ((b₁ - b₂) / d)
trace[grind.debug.cutsat.dvd.le] "[neg] {← c.pp}, {← dvd.pp}, {b₂'}"
let p := c.p.addConst (b₂'-b₂)
return { p, h := .negDvdTight dvd c }
return c
@@ -134,7 +128,6 @@ def getBestLower? (x : Var) (dvd? : Option DvdCnstr) : GoalM (Option (Rat × LeC
let .add k _ p := c.p | c.throwUnexpected
let some v p.eval? | c.throwUnexpected
let lower' := v / (-k)
trace[grind.debug.cutsat.getBestLower] "k: {k}, x: {x}, p: {repr p}, v: {v}, best?: {best?.map (·.1)}, c: {← c.pp}"
if let some (lower, _) := best? then
if lower' > lower then
best? := some (lower', c)
@@ -214,7 +207,7 @@ def DvdCnstr.getSolutions? (c : DvdCnstr) : SearchM (Option DvdSolution) := do
return some { d, b := -b*a' }
def resolveDvdConflict (c : DvdCnstr) : GoalM Unit := do
trace[grind.cutsat.conflict] "{← c.pp}"
trace[grind.debug.cutsat.search.conflict] "{← c.pp}"
let d := c.d
let .add a _ p := c.p | c.throwUnexpected
{ d := a.gcd d, p, h := .elim c : DvdCnstr }.assert
@@ -300,7 +293,7 @@ partial def findRatVal (lower upper : Rat) (diseqVals : Array (Rat × DiseqCnstr
v
def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
trace[grind.cutsat.conflict] "{← c₁.pp}, {← c₂.pp}"
trace[grind.debug.cutsat.search.conflict] "{← c₁.pp}, {← c₂.pp}"
let .add a₁ _ p₁ := c₁.p | c₁.throwUnexpected
let .add a₂ _ p₂ := c₂.p | c₂.throwUnexpected
let p := p₁.mul a₂.natAbs |>.combine (p₂.mul a₁.natAbs)
@@ -313,7 +306,7 @@ def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
{ p, h := .combine c₁ c₂ : LeCnstr }
else
{ p := p.div k, h := .combineDivCoeffs c₁ c₂ k : LeCnstr }
trace[grind.cutsat.conflict] "resolved: {← c.pp}"
trace[grind.debug.cutsat.search.conflict] "resolved: {← c.pp}"
c.assert
return true
@@ -330,7 +323,7 @@ def resolveCooperUnary (pred : CooperSplitPred) : SearchM Bool := do
return true
def resolveCooperPred (pred : CooperSplitPred) : SearchM Unit := do
trace[grind.cutsat.conflict] "[{pred.numCases}]: {← pred.pp}"
trace[grind.debug.cutsat.search.conflict] "[{pred.numCases}]: {← pred.pp}"
if ( resolveCooperUnary pred) then
return
let n := pred.numCases
@@ -347,11 +340,11 @@ def resolveCooperDvd (c₁ c₂ : LeCnstr) (c₃ : DvdCnstr) : SearchM Unit := d
def DiseqCnstr.split (c : DiseqCnstr) : SearchM LeCnstr := do
let fvarId if let some fvarId := ( get').diseqSplits.find? c.p then
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, reusing {fvarId.name}"
trace[grind.debug.cutsat.search.split] "{← c.pp}, reusing {fvarId.name}"
pure fvarId
else
let fvarId mkCase (.diseq c)
trace[grind.debug.cutsat.diseq.split] "{← c.pp}, {fvarId.name}"
trace[grind.debug.cutsat.search.split] "{← c.pp}, {fvarId.name}"
modify' fun s => { s with diseqSplits := s.diseqSplits.insert c.p fvarId }
pure fvarId
let p₂ := c.p.addConst 1
@@ -419,16 +412,15 @@ def processVar (x : Var) : SearchM Unit := do
| some (lower, _), none =>
let lower := lower.ceil
let v := dvdSol.geAvoiding lower diseqVals
trace[grind.debug.cutsat.search] "{lower} ≤ {quoteIfNotAtom (← getVar x)} := {v}"
trace[grind.debug.cutsat.search] "{lower} ≤ {quoteIfArithTerm (← getVar x)} := {v}"
setAssignment x v
| none, some (upper, _) =>
let upper := upper.floor
let v := dvdSol.leAvoiding upper diseqVals
trace[grind.debug.cutsat.search] "{quoteIfNotAtom (← getVar x)} := {v} ≤ {upper}"
trace[grind.debug.cutsat.search] "{quoteIfArithTerm (← getVar x)} := {v} ≤ {upper}"
setAssignment x v
| some (lower, c₁), some (upper, c₂) =>
trace[grind.debug.cutsat.search] "{lower} ≤ {lower.ceil} ≤ {quoteIfNotAtom (← getVar x)} ≤ {upper.floor} ≤ {upper}"
trace[grind.debug.cutsat.getBestLower] "lower: {lower}, c₁: {← c₁.pp}"
trace[grind.debug.cutsat.search] "{lower} ≤ {lower.ceil} ≤ {quoteIfArithTerm (← getVar x)} ≤ {upper.floor} ≤ {upper}"
if lower > upper then
let .true resolveRealLowerUpperConflict c₁ c₂
| throwError "`grind` internal error, conflict resolution failed"
@@ -472,43 +464,42 @@ private def findCase (decVars : FVarIdSet) : SearchM Case := do
if decVars.contains case.fvarId then
return case
-- Conflict does not depend on this case.
trace[grind.debug.cutsat.backtrack] "skipping {case.fvarId.name}"
trace[grind.debug.cutsat.search.backtrack] "skipping {case.fvarId.name}"
unreachable!
private def union (vs₁ vs₂ : FVarIdSet) : FVarIdSet :=
vs₁.fold (init := vs₂) (·.insert ·)
def resolveConflict (h : UnsatProof) : SearchM Unit := do
trace[grind.debug.cutsat.backtrack] "resolve conflict, decision stack: {(← get).cases.toList.map fun c => c.fvarId.name}"
trace[grind.debug.cutsat.search.backtrack] "resolve conflict, decision stack: {(← get).cases.toList.map fun c => c.fvarId.name}"
let decVars := h.collectDecVars.run ( get).decVars
trace[grind.debug.cutsat.backtrack] "dec vars: {decVars.toList.map (·.name)}"
trace[grind.debug.cutsat.search.backtrack] "dec vars: {decVars.toList.map (·.name)}"
if decVars.isEmpty then
trace[grind.debug.cutsat.backtrack] "close goal: {← h.pp}"
trace[grind.debug.cutsat.search.backtrack] "close goal: {← h.pp}"
closeGoal ( h.toExprProof)
return ()
let c findCase decVars
modify' fun _ => c.saved
trace[grind.debug.cutsat.backtrack] "backtracking {c.fvarId.name}"
trace[grind.debug.cutsat.search.backtrack] "backtracking {c.fvarId.name}"
let decVars := decVars.erase c.fvarId
match c.kind with
| .diseq c₁ =>
let decVars := decVars.toArray
let p' := c₁.p.mul (-1) |>.addConst 1
let c' := { p := p', h := .ofDiseqSplit c₁ c.fvarId h decVars : LeCnstr }
trace[grind.debug.cutsat.backtrack] "resolved diseq split: {← c'.pp}"
trace[grind.debug.cutsat.search.backtrack] "resolved diseq split: {← c'.pp}"
c'.assert
| .cooper pred hs decVars' =>
let decVars' := union decVars decVars'
let n := pred.numCases
let hs := hs.push (c.fvarId, h)
trace[grind.debug.cutsat.backtrack] "cooper #{hs.size + 1}, {← pred.pp}, {hs.map fun p => p.1.name}"
trace[grind.debug.cutsat.search.backtrack] "cooper #{hs.size + 1}, {← pred.pp}, {hs.map fun p => p.1.name}"
let s if hs.size + 1 < n then
let fvarId mkCase (.cooper pred hs decVars')
pure { pred, k := n - hs.size - 1, h := .dec fvarId : CooperSplit }
else
let decVars' := decVars'.toArray
trace[grind.debug.cutsat.backtrack] "cooper last case, {← pred.pp}, dec vars: {decVars'.map (·.name)}"
trace[grind.debug.cutsat.proof] "CooperSplit.last"
trace[grind.debug.cutsat.search.backtrack] "cooper last case, {← pred.pp}, dec vars: {decVars'.map (·.name)}"
pure { pred, k := 0, h := .last hs decVars' : CooperSplit }
s.assert

View File

@@ -74,7 +74,6 @@ def mkCase (kind : CaseKind) : SearchM FVarId := do
decVars := s.decVars.insert fvarId
}
modify' fun s => { s with caseSplits := true }
trace[grind.debug.cutsat.backtrack] "mkCase fvarId: {fvarId.name}"
return fvarId
end Lean.Meta.Grind.Arith.Cutsat

View File

@@ -85,12 +85,13 @@ inductive EqCnstrProof where
-/
core (a b : Expr) (p₁ p₂ : Poly)
| coreNat (a b : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr)
| /-- `e` is `p` -/
defn (e : Expr) (p : Poly)
| defnNat (e : Int.OfNat.Expr) (x : Var) (e' : Int.Linear.Expr)
| norm (c : EqCnstr)
| divCoeffs (c : EqCnstr)
| subst (x : Var) (c₁ : EqCnstr) (c₂ : EqCnstr)
| ofLeGe (c₁ : LeCnstr) (c₂ : LeCnstr)
| /-- `e` is `p` -/
defn (e : Expr) (p : Poly)
/-- A divisibility constraint and its justification/proof. -/
structure DvdCnstr where
@@ -237,6 +238,12 @@ structure State where
foreignVarMap : PHashMap ENodeKey (Var × ForeignType) := {}
foreignVars : PHashMap ForeignType (PArray Expr) := {}
/--
Some foreign variables encode nested terms such as `b+1`.
This is a mapping from this kind of variable to the integer variable
representing `natCast (b+1)`.
-/
foreignDef : PHashMap ENodeKey Var := {}
/--
Mapping from variables to divisibility constraints. Recall that we keep the divisibility constraint in solved form.
Thus, we have at most one divisibility per variable. -/
dvds : PArray (Option DvdCnstr) := {}

View File

@@ -87,15 +87,15 @@ def resetAssignmentFrom (x : Var) : GoalM Unit := do
def _root_.Int.Linear.Poly.pp (p : Poly) : GoalM MessageData := do
match p with
| .num k => return m!"{k}"
| .add 1 x p => go (quoteIfNotAtom ( getVar x)) p
| .add k x p => go m!"{k}*{quoteIfNotAtom (← getVar x)}" p
| .add 1 x p => go (quoteIfArithTerm ( getVar x)) p
| .add k x p => go m!"{k}*{quoteIfArithTerm (← getVar x)}" p
where
go (r : MessageData) (p : Int.Linear.Poly) : GoalM MessageData := do
match p with
| .num 0 => return r
| .num k => return m!"{r} + {k}"
| .add 1 x p => go m!"{r} + {quoteIfNotAtom (← getVar x)}" p
| .add k x p => go m!"{r} + {k}*{quoteIfNotAtom (← getVar x)}" p
| .add 1 x p => go m!"{r} + {quoteIfArithTerm (← getVar x)}" p
| .add k x p => go m!"{r} + {k}*{quoteIfArithTerm (← getVar x)}" p
def _root_.Int.Linear.Poly.denoteExpr' (p : Poly) : GoalM Expr := do
let vars getVars

View File

@@ -11,23 +11,12 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.Nat
namespace Lean.Meta.Grind.Arith.Cutsat
private def assertNatCast (e : Expr) : GoalM Unit := do
let_expr NatCast.natCast _ inst a := e | return ()
let_expr instNatCastInt := inst | return ()
trace[grind.debug.cutsat.natCast] "{a}"
pushNewFact <| mkApp (mkConst ``Int.Linear.natCast_nonneg) a
discard <| mkForeignVar a .nat
private def assertHelpers (e : Expr) : GoalM Unit := do
assertNatCast e
assertDenoteAsIntNonneg e
@[export lean_grind_cutsat_mk_var]
def mkVarImpl (expr : Expr) : GoalM Var := do
if let some var := ( get').varMap.find? { expr } then
return var
let var : Var := ( get').vars.size
trace[grind.cutsat.internalize.term] "{expr} ↦ #{var}"
trace[grind.debug.cutsat.internalize] "{expr} ↦ #{var}"
modify' fun s => { s with
vars := s.vars.push expr
varMap := s.varMap.insert { expr } var
@@ -38,9 +27,9 @@ def mkVarImpl (expr : Expr) : GoalM Var := do
occurs := s.occurs.push {}
elimEqs := s.elimEqs.push none
}
trace[grind.debug.cutsat.markTerm] "mkVar: {expr}"
markAsCutsatTerm expr
assertHelpers expr
assertNatCast expr var
assertDenoteAsIntNonneg expr
return var
def isInt (e : Expr) : GoalM Bool := do

View File

@@ -76,7 +76,7 @@ def mkModel (goal : Goal) : MetaM (Array (Expr × Nat)) := do
r := r.qsort fun (e₁, _) (e₂, _) => e₁.lt e₂
if ( isTracingEnabledFor `grind.offset.model) then
for (x, v) in r do
trace[grind.offset.model] "{quoteIfNotAtom x} := {v}"
trace[grind.offset.model] "{quoteIfArithTerm x} := {v}"
return r
end Lean.Meta.Grind.Arith.Offset

View File

@@ -13,6 +13,10 @@ namespace Lean.Meta.Grind.Arith
def isNatType (e : Expr) : Bool :=
e.isConstOf ``Nat
/-- Returns `true` if `e` is of the form `Int` -/
def isIntType (e : Expr) : Bool :=
e.isConstOf ``Int
/-- Returns `true` if `e` is of the form `@instHAdd Nat instAddNat` -/
def isInstAddNat (e : Expr) : Bool :=
let_expr instHAdd a b := e | false
@@ -49,5 +53,34 @@ def isNatNum? (e : Expr) : Option Nat := Id.run do
let .lit (.natVal k) := k | none
some k
def isSupportedType (e : Expr) : Bool :=
isNatType e || isIntType e
partial def isRelevantPred (e : Expr) : Bool :=
match_expr e with
| Not p => isRelevantPred p
| LE.le α _ _ _ => isSupportedType α
| Eq α _ _ => isSupportedType α
| Dvd.dvd α _ _ _ => isSupportedType α
| _ => false
def isArithTerm (e : Expr) : Bool :=
match_expr e with
| HAdd.hAdd _ _ _ _ _ _ => true
| HSub.hSub _ _ _ _ _ _ => true
| HMul.hMul _ _ _ _ _ _ => true
| HDiv.hDiv _ _ _ _ _ _ => true
| HMod.hMod _ _ _ _ _ _ => true
| HPow.hPow _ _ _ _ _ _ => true
| Neg.neg _ _ _ => true
| OfNat.ofNat _ _ _ => true
| _ => false
/-- Quote `e` using `「` and `」` if `e` is an arithmetic term that is being treated as a variable. -/
def quoteIfArithTerm (e : Expr) : MessageData :=
if isArithTerm e then
aquote e
else
e
end Lean.Meta.Grind.Arith

View File

@@ -35,6 +35,7 @@ def instantiateExtTheorem (thm : Ext.ExtTheorem) (e : Expr) : GoalM Unit := with
if proof'.hasMVar || prop'.hasMVar then
reportIssue! "failed to apply extensionality theorem `{thm.declName}` for {indentExpr e}\nresulting terms contain metavariables"
return ()
trace[grind.ext] "{prop'}"
addNewRawFact proof' prop' (( getGeneration e) + 1)
end Lean.Meta.Grind

View File

@@ -46,6 +46,9 @@ where
else if ( isEqTrue b) then
-- b = True → (a → b) = True
pushEqTrue e <| mkApp3 (mkConst ``Grind.imp_eq_of_eq_true_right) a b ( mkEqTrueProof b)
else if ( isEqFalse b <&&> isEqTrue e <&&> isProp a) then
-- (a → b) = True → b = False → a = False
pushEqFalse a <| mkApp4 (mkConst ``Grind.eq_false_of_imp_eq_true) a b ( mkEqTrueProof e) ( mkEqFalseProof b)
private def isEqTrueHyp? (proof : Expr) : Option FVarId := Id.run do
let_expr eq_true _ p := proof | return none
@@ -105,5 +108,19 @@ def propagateForallPropDown (e : Expr) : GoalM Unit := do
else
if b.hasLooseBVars then
addLocalEMatchTheorems e
else
unless ( alreadyInternalized b) do return ()
if ( isEqFalse b <&&> isProp a) then
-- (a → b) = True → b = False → a = False
pushEqFalse a <| mkApp4 (mkConst ``Grind.eq_false_of_imp_eq_true) a b ( mkEqTrueProof e) ( mkEqFalseProof b)
builtin_grind_propagator propagateExistsDown Exists := fun e => do
if ( isEqFalse e) then
let_expr f@Exists α p := e | return ()
let u := f.constLevels!
let notP := mkApp (mkConst ``Not) (mkApp p (.bvar 0) |>.headBeta)
let prop := mkForall `x .default α notP
let proof := mkApp3 (mkConst ``forall_not_of_not_exists u) α p (mkOfEqFalseCore e ( mkEqFalseProof e))
addNewRawFact proof prop ( getGeneration e)
end Lean.Meta.Grind

View File

@@ -42,6 +42,7 @@ adds entry `f ↦ e` to `appMap`. Recall that `appMap` is a multi-map.
-/
private def updateAppMap (e : Expr) : GoalM Unit := do
let key := e.toHeadIndex
trace_goal[grind.debug.appMap] "{e} => {repr key}"
modify fun s => { s with
appMap := if let some es := s.appMap.find? key then
s.appMap.insert key (e :: es)
@@ -93,6 +94,9 @@ private def checkAndAddSplitCandidate (e : Expr) : GoalM Unit := do
let .const declName _ := ( whnfD ( inferType e)).getAppFn | return ()
if ( get).split.casesTypes.isSplit declName then
addSplitCandidate e
| .forallE _ d _ _ =>
if Arith.isRelevantPred d || ( getConfig).splitImp then
addSplitCandidate e
| _ => pure ()
/--
@@ -168,13 +172,16 @@ private def activateTheoremPatterns (fName : Name) (generation : Nat) : GoalM Un
modify fun s => { s with ematch.thmMap := thmMap }
let appMap := ( get).appMap
for thm in thms do
trace[grind.debug.ematch.activate] "`{fName}` => `{thm.origin.key}`"
unless ( get).ematch.thmMap.isErased thm.origin do
let symbols := thm.symbols.filter fun sym => !appMap.contains sym
let thm := { thm with symbols }
match symbols with
| [] => activateTheorem thm generation
| [] =>
trace_goal[grind.debug.ematch.activate] "`{thm.origin.key}`"
activateTheorem thm generation
| _ =>
trace_goal[grind.ematch] "reinsert `{thm.origin.key}`"
trace_goal[grind.debug.ematch.activate] "reinsert `{thm.origin.key}`"
modify fun s => { s with ematch.thmMap := s.ematch.thmMap.insert thm }
/--
@@ -200,6 +207,65 @@ private def propagateUnitLike (a : Expr) (generation : Nat) : GoalM Unit := do
internalize unit generation
pushEq a unit <| ( mkEqRefl unit)
/-- Returns `true` if we can ignore `ext` for functions occurring as arguments of a `declName`-application. -/
private def extParentsToIgnore (declName : Name) : Bool :=
declName == ``Eq || declName == ``HEq || declName == ``dite || declName == ``ite
|| declName == ``Exists || declName == ``Subtype
/--
Given a term `e` that occurs as the argument at position `i` of an `f`-application `parent?`,
we consider `e` as a candidate for case-splitting. For every other argument `e'` that also appears
at position `i` in an `f`-application and has the same type as `e`, we add the case-split candidate `e = e'`.
When performing the case split, we consider the following two cases:
- `e = e'`, which may introduce a new congruence between the corresponding `f`-applications.
- `¬(e = e')`, which may trigger extensionality theorems for the type of `e`.
This feature enables `grind` to solve examples such as:
```lean
example (f : (Nat → Nat) → Nat) : a = b → f (fun x => a + x) = f (fun x => b + x) := by
grind
```
-/
private def addSplitCandidatesForExt (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
let some parent := parent? | return ()
unless parent.isApp do return ()
let f := parent.getAppFn
if let .const declName _ := f then
if extParentsToIgnore declName then return ()
let type inferType e
-- Remark: we currently do not perform function extensionality on functions that produce a type that is not a proposition.
-- We may add an option to enable that in the future.
let u? typeFormerTypeLevel type
if u? != .none && u? != some .zero then return ()
let mut i := parent.getAppNumArgs
let mut it := parent
repeat
if !it.isApp then return ()
i := i - 1
let arg := it.appArg!
if isSameExpr arg e then
found f i type
it := it.appFn!
where
found (f : Expr) (i : Nat) (type : Expr) : GoalM Unit := do
trace[grind.debug.ext] "{f}, {i}, {e}"
let others := ( get).termsAt.find? (f, i) |>.getD []
for (e', type') in others do
if ( withDefault <| isDefEq type type') then
let eq := mkApp3 (mkConst ``Eq [ getLevel type]) type e e'
let eq shareCommon eq
internalize eq generation
trace_goal[grind.ext.candidate] "{eq}"
addSplitCandidate eq
modify fun s => { s with termsAt := s.termsAt.insert (f, i) ((e, type) :: others) }
return ()
/-- Applies `addSplitCandidatesForExt` if `funext` is enabled. -/
private def addSplitCandidatesForFunext (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := do
unless ( getConfig).funext do return ()
addSplitCandidatesForExt e generation parent?
@[export lean_grind_internalize]
private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Option Expr := none) : GoalM Unit := withIncRecDepth do
if ( alreadyInternalized e) then
@@ -222,7 +288,10 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
| .fvar .. =>
mkENode' e generation
checkAndAddSplitCandidate e
| .letE .. | .lam .. =>
| .letE .. =>
mkENode' e generation
| .lam .. =>
addSplitCandidatesForFunext e generation parent?
mkENode' e generation
| .forallE _ d b _ =>
mkENode' e generation
@@ -233,6 +302,7 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
internalizeImpl b generation e
registerParent e b
propagateUp e
checkAndAddSplitCandidate e
| .lit .. =>
mkENode e generation
| .const declName _ =>
@@ -256,6 +326,7 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
internalizeMatchCond e generation
else e.withApp fun f args => do
mkENode e generation
updateAppMap e
checkAndAddSplitCandidate e
pushCastHEqs e
addMatchEqns f generation
@@ -280,7 +351,6 @@ private partial def internalizeImpl (e : Expr) (generation : Nat) (parent? : Opt
internalize arg generation e
registerParent e arg
addCongrTable e
updateAppMap e
Arith.internalize e parent?
propagateUp e
propagateBetaForNewApp e

View File

@@ -60,8 +60,9 @@ def mbtc (ctx : MBTC.Context) : GoalM Bool := do
unless others.any (isSameExpr arg ·) do
for other in others do
if ( ctx.eqAssignment arg other) then
let k := mkCandidateKey arg other
candidates := candidates.insert k
if ( hasSameType arg other) then
let k := mkCandidateKey arg other
candidates := candidates.insert k
map := map.insert (f, i) (arg :: others)
else
map := map.insert (f, i) [arg]

View File

@@ -148,9 +148,12 @@ def Result.toMessageData (result : Result) : MetaM MessageData := do
let mut msgs result.failures.mapM (goalToMessageData · result.config)
if result.config.verbose then
let mut issues := result.issues
-- We did not find the following very useful in practice.
/-
unless result.skipped.isEmpty do
let m := m!"#{result.skipped.length} other goal(s) were not fully processed due to previous failures, threshold: `(failures := {result.config.failures})`"
issues := .trace { cls := `issue } m #[] :: issues
-/
unless issues.isEmpty do
msgs := msgs ++ [.trace { cls := `grind } "Issues" issues.reverse.toArray]
if let some msg mkGlobalDiag result.counters result.simp then

View File

@@ -126,7 +126,7 @@ private def ppOffset : M Unit := do
if model.isEmpty then return ()
let mut ms := #[]
for (e, val) in model do
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
ms := ms.push <| .trace { cls := `assign } m!"{Arith.quoteIfArithTerm e} := {val}" #[]
pushMsg <| .trace { cls := `offset } "Assignment satisfying offset constraints" ms
private def ppCutsat : M Unit := do
@@ -138,7 +138,7 @@ private def ppCutsat : M Unit := do
if model.isEmpty then return ()
let mut ms := #[]
for (e, val) in model do
ms := ms.push <| .trace { cls := `assign } m!"{quoteIfNotAtom e} := {val}" #[]
ms := ms.push <| .trace { cls := `assign } m!"{Arith.quoteIfArithTerm e} := {val}" #[]
pushMsg <| .trace { cls := `cutsat } "Assignment satisfying linear constraints" ms
private def ppThresholds (c : Grind.Config) : M Unit := do

View File

@@ -23,6 +23,30 @@ def registerNormTheorems (preDeclNames : Array Name) (postDeclNames : Array Name
for declName in postDeclNames do
addSimpTheorem normExt declName (post := true) (inv := false) .global (eval_prio default)
-- TODO: should we make this extensible?
private def isBoolEqTarget (declName : Name) : Bool :=
declName == ``Bool.and ||
declName == ``Bool.or ||
declName == ``Bool.not ||
declName == ``BEq.beq ||
declName == ``decide
builtin_simproc_decl simpBoolEq (@Eq Bool _ _) := fun e => do
let_expr f@Eq bool lhs rhs e | return .continue
let .const rhsName _ := rhs.getAppFn | return .continue
if rhsName == ``true || rhsName == ``false then return .continue
let .const lhsName _ := lhs.getAppFn | return .continue
if lhsName == ``true || lhsName == ``false then
-- Just apply comm
let e' := mkApp3 f bool rhs lhs
return .visit { expr := e', proof? := mkApp2 (mkConst ``Grind.flip_bool_eq) lhs rhs }
if isBoolEqTarget lhsName || isBoolEqTarget rhsName then
-- Convert into `(lhs = true) = (rhs = true)`
let tr := mkConst ``true
let e' mkEq (mkApp3 f bool lhs tr) (mkApp3 f bool rhs tr)
return .visit { expr := e', proof? := mkApp2 (mkConst ``Grind.bool_eq_to_prop) lhs rhs }
return .continue
/-- Returns the array of simprocs used by `grind`. -/
protected def getSimprocs : MetaM (Array Simprocs) := do
let s Simp.getSEvalSimprocs
@@ -42,6 +66,7 @@ protected def getSimprocs : MetaM (Array Simprocs) := do
let s := s.erase ``List.reduceReplicate
let s addSimpMatchDiscrsOnly s
let s addPreMatchCondSimproc s
let s s.add ``simpBoolEq (post := false)
return #[s]
/-- Returns the simplification context used by `grind`. -/

View File

@@ -15,7 +15,7 @@ inductive CaseSplitStatus where
| resolved
| notReady
| ready (numCases : Nat) (isRec := false)
deriving Inhabited, BEq
deriving Inhabited, BEq, Repr
/-- Given `c`, the condition of an `if-then-else`, check whether we need to case-split on the `if-then-else` or not -/
private def checkIteCondStatus (c : Expr) : GoalM CaseSplitStatus := do
@@ -76,11 +76,26 @@ private def checkIffStatus (e a b : Expr) : GoalM CaseSplitStatus := do
/-- Returns `true` is `c` is congruent to a case-split that was already performed. -/
private def isCongrToPrevSplit (c : Expr) : GoalM Bool := do
unless c.isApp do return false
( get).split.resolved.foldM (init := false) fun flag { expr := c' } => do
if flag then
return true
else
return isCongruent ( get).enodes c c'
return c'.isApp && isCongruent ( get).enodes c c'
private def checkForallStatus (e : Expr) : GoalM CaseSplitStatus := do
if ( isEqTrue e) then
let .forallE _ p q _ := e | return .resolved
if ( isEqTrue p <||> isEqFalse p) then
return .resolved
unless q.hasLooseBVars do
if ( isEqTrue q <||> isEqFalse q) then
return .resolved
return .ready 2
else if ( isEqFalse e) then
return .resolved
else
return .notReady
private def checkCaseSplitStatus (e : Expr) : GoalM CaseSplitStatus := do
match_expr e with
@@ -97,6 +112,10 @@ private def checkCaseSplitStatus (e : Expr) : GoalM CaseSplitStatus := do
if ( isResolvedCaseSplit e) then
trace_goal[grind.debug.split] "split resolved: {e}"
return .resolved
if e.isForall then
let s checkForallStatus e
trace_goal[grind.debug.split] "{e}, status: {repr s}"
return s
if ( isCongrToPrevSplit e) then
return .resolved
if let some info := isMatcherAppCore? ( getEnv) e then
@@ -137,6 +156,7 @@ where
modify fun s => { s with split.num := numSplits, ematch.num := 0 }
return c?
| c::cs =>
trace_goal[grind.debug.split] "checking: {c}"
match ( checkCaseSplitStatus c) with
| .notReady => go cs c? (c::cs')
| .resolved => go cs c? cs'
@@ -174,7 +194,9 @@ private def mkCasesMajor (c : Expr) : GoalM Expr := do
-- model-based theory combination split
return mkGrindEM c
| _ =>
if ( isEqTrue c) then
if let .forallE _ p _ _ := c then
return mkGrindEM p
else if ( isEqTrue c) then
return mkOfEqTrueCore c ( mkEqTrueProof c)
else
return c

View File

@@ -529,6 +529,13 @@ structure Goal where
arith : Arith.State := {}
/-- State of the clean name generator. -/
clean : Clean.State := {}
/--
Mapping from pairs `(f, i)` to a list of `(e, type)`.
The meaning is: `e : type` is lambda expression that occurs at argument `i` of an `f`-application.
We use this information to add case-splits for triggering extensionality theorems.
See `addSplitCandidatesForExt`.
-/
termsAt : PHashMap (Expr × Nat) (List (Expr × Expr)) := {}
deriving Inhabited
def Goal.admit (goal : Goal) : MetaM Unit :=

View File

@@ -44,9 +44,10 @@ where `<replacement*>` is a link which will perform the replacement.
@[builtin_widget_module] def tryThisWidget : Widget.Module where
javascript := "
import * as React from 'react';
import { EditorContext } from '@leanprover/infoview';
import { EditorContext, EnvPosContext } from '@leanprover/infoview';
const e = React.createElement;
export default function ({ pos, suggestions, range, header, isInline, style }) {
export default function ({ suggestions, range, header, isInline, style }) {
const pos = React.useContext(EnvPosContext)
const editorConnection = React.useContext(EditorContext)
const defStyle = style || {
className: 'link pointer dim',

View File

@@ -20,10 +20,10 @@ open TSyntax.Compat
/--
If `cond` is true, wraps the syntax produced by `d` in a type ascription.
-/
def withTypeAscription (d : Delab) (cond : Bool := true) : DelabM Term := do
def withTypeAscription (d : Delab) (cond : Bool := true) : Delab := do
let stx d
if cond then
let stx annotateCurPos stx
let stx annotateTermInfoUnlessAnnotated stx
let typeStx withType delab
`(($stx : $typeStx))
else
@@ -124,6 +124,18 @@ def delabConst : Delab := do
else
return stx
/--
If `pp.tagAppFns` is set, and if the current expression is a constant application,
then `d` is evaluated with the head constant delaborated with `delabConst` as the ref.
-/
def withFnRefWhenTagAppFns (d : Delab) : Delab := do
if ( getExpr).getAppFn.isConst && ( getPPOption getPPTagAppFns) then
-- delabConst in `pp.tagAppFns` mode annotates the term.
let head withNaryFn delabConst
withRef head <| d
else
d
def withMDataOptions [Inhabited α] (x : DelabM α) : DelabM α := do
match getExpr with
| Expr.mdata m .. =>
@@ -637,7 +649,7 @@ def delabStructureInstance : Delab := do
else
return (i + 1, args))
withTypeAscription (cond := ( withType <| getPPOption getPPStructureInstanceType)) do
`($[$args],*)
withFnRefWhenTagAppFns `($[$args],*)
else
/-
Otherwise, we use structure instance notation.
@@ -650,7 +662,7 @@ def delabStructureInstance : Delab := do
let (_, fields) collectStructFields s.induct levels params #[] {} s
let tyStx? : Option Term withType do
if getPPOption getPPStructureInstanceType then delab else pure none
`({ $fields,* $[: $tyStx?]? })
withFnRefWhenTagAppFns `({ $fields,* $[: $tyStx?]? })
/-- State for `delabAppMatch` and helpers. -/

View File

@@ -108,7 +108,7 @@ def collectAvailableImportsFromLake : IO (Option AvailableImports) := do
def collectAvailableImportsFromSrcSearchPath : IO AvailableImports :=
(·.2) <$> StateT.run (s := #[]) do
let srcSearchPath initSrcSearchPath
let srcSearchPath getSrcSearchPath
for p in srcSearchPath do
if ! ( p.isDir) then
continue

View File

@@ -348,7 +348,6 @@ structure WorkerState where
doc : EditableDocument
/-- Token flagged for aborting `doc.reporter` when a new document version comes in. -/
reporterCancelTk : CancelToken
srcSearchPathTask : ServerTask SearchPath
importCachingTask? : Option (ServerTask (Except Error AvailableImportsCache))
pendingRequests : PendingRequestMap
/-- A map of RPC session IDs. We allow asynchronous elab tasks and request handlers
@@ -365,9 +364,12 @@ open Language Lean in
Callback from Lean language processor after parsing imports that requests necessary information from
Lake for processing imports.
-/
def setupImports (meta : DocumentMeta) (cmdlineOpts : Options) (chanOut : Std.Channel JsonRpc.Message)
(srcSearchPathPromise : Promise SearchPath) (stx : Syntax) :
Language.ProcessingT IO (Except Language.Lean.HeaderProcessedSnapshot SetupImportsResult) := do
def setupImports
(meta : DocumentMeta)
(cmdlineOpts : Options)
(chanOut : Std.Channel JsonRpc.Message)
(stx : Syntax)
: Language.ProcessingT IO (Except Language.Lean.HeaderProcessedSnapshot SetupImportsResult) := do
let importsAlreadyLoaded importsLoadedRef.modifyGet ((·, true))
if importsAlreadyLoaded then
-- As we never unload imports in the server, we should not run the code below twice in the
@@ -405,25 +407,16 @@ def setupImports (meta : DocumentMeta) (cmdlineOpts : Options) (chanOut : Std.Ch
}
| _ => pure ()
srcSearchPathPromise.resolve fileSetupResult.srcSearchPath
let mainModuleName if let some path := System.Uri.fileUriToPath? meta.uri then
EIO.catchExceptions (h := fun _ => pure Name.anonymous) do
if let some mod searchModuleNameOfFileName path fileSetupResult.srcSearchPath then
pure mod
else
moduleNameOfFileName path none
else
pure Name.anonymous
-- override cmdline options with file options
let opts := cmdlineOpts.mergeBy (fun _ _ fileOpt => fileOpt) fileSetupResult.fileOptions
-- default to async elaboration; see also `Elab.async` docs
let opts := Elab.async.setIfNotSet opts true
let opts := Elab.inServer.set opts true
return .ok {
mainModuleName
mainModuleName := meta.mod
opts
plugins := fileSetupResult.plugins
}
@@ -438,7 +431,6 @@ section Initialization
let stickyDiagnosticsRef IO.mkRef
let pendingServerRequestsRef IO.mkRef
let chanOut mkLspOutputChannel maxDocVersionRef
let srcSearchPathPromise IO.Promise.new
let timestamp IO.monoMsNow
let partialHandlersRef IO.mkRef <| RBMap.fromArray (cmp := compare) <|
( partialLspRequestHandlerMethods).map fun (method, refreshMethod, _) =>
@@ -448,12 +440,12 @@ section Initialization
-- Emit a refresh request after a file worker restart.
pendingRefreshInfo? := some { lastRefreshTimestamp := timestamp, successiveRefreshAttempts := 0 }
})
let processor := Language.Lean.process (setupImports meta opts chanOut srcSearchPathPromise)
let processor := Language.Lean.process (setupImports meta opts chanOut)
let processor Language.mkIncrementalProcessor processor
let initSnap processor meta.mkInputContext
let _ ServerTask.IO.mapTaskCostly (t := srcSearchPathPromise.result!) fun srcSearchPath => do
let _ ServerTask.IO.asTask do
let importClosure := getImportClosure? initSnap
let importClosure importClosure.filterMapM (documentUriFromModule srcSearchPath ·)
let importClosure importClosure.filterMapM (documentUriFromModule? ·)
chanOut.send <| mkImportClosureNotification importClosure
let ctx := {
chanOut
@@ -477,7 +469,6 @@ section Initialization
return (ctx, {
doc := { doc with reporter }
reporterCancelTk
srcSearchPathTask := srcSearchPathPromise.result!
pendingRequests := RBMap.empty
rpcSessions := RBMap.empty
importCachingTask? := none
@@ -579,7 +570,6 @@ section NotificationHandling
let newVersion := docId.version?.getD 0
let rc : RequestContext := {
rpcSessions := st.rpcSessions
srcSearchPathTask := st.srcSearchPathTask
doc := oldDoc
cancelTk
hLog := ctx.hLog
@@ -589,7 +579,10 @@ section NotificationHandling
RequestM.runInIO (handleOnDidChange p) rc
if ¬ changes.isEmpty then
let newDocText := foldDocumentChanges changes oldDoc.meta.text
updateDocument docId.uri, newVersion, newDocText, oldDoc.meta.dependencyBuildMode
updateDocument { oldDoc.meta with
version := newVersion
text := newDocText
}
for (_, r) in st.pendingRequests do
r.cancelTk.cancelByEdit
@@ -872,7 +865,6 @@ section MessageHandling
-- TODO: move into language-specific request handling
let rc : RequestContext := {
rpcSessions := st.rpcSessions
srcSearchPathTask := st.srcSearchPathTask
doc := st.doc
cancelTk
hLog := ctx.hLog
@@ -1017,9 +1009,16 @@ def initAndRunWorker (i o e : FS.Stream) (opts : Options) : IO Unit := do
let initParams i.readLspRequestAs "initialize" InitializeParams
let _, param i.readLspNotificationAs "textDocument/didOpen" LeanDidOpenTextDocumentParams
let doc := param.textDocument
-- LSP always refers to characters by (line, column),
-- so converting CRLF to LF preserves line and column numbers.
let meta : DocumentMeta := doc.uri, doc.version, doc.text.crlfToLf.toFileMap, param.dependencyBuildMode?.getD .always
let meta : DocumentMeta := {
uri := doc.uri
mod := moduleFromDocumentUri doc.uri
version := doc.version
-- LSP always refers to characters by (line, column),
-- so converting CRLF to LF preserves line and column numbers.
text := doc.text.crlfToLf.toFileMap
dependencyBuildMode := param.dependencyBuildMode?.getD .always
}
let e := e.withPrefix s!"[{param.textDocument.uri}] "
let _ IO.setStderr e
let (ctx, st) try

View File

@@ -9,28 +9,28 @@ import Lean.Server.Requests
namespace Lean.Elab
def InlayHintLinkLocation.toLspLocation (srcSearchPath : SearchPath) (text : FileMap)
def InlayHintLinkLocation.toLspLocation (text : FileMap)
(l : InlayHintLinkLocation) : IO (Option Lsp.Location) := do
let some uri Server.documentUriFromModule srcSearchPath l.module
let some uri Server.documentUriFromModule? l.module
| return none
return some {
uri
range := text.utf8RangeToLspRange l.range
}
def InlayHintLabelPart.toLspInlayHintLabelPart (srcSearchPath : SearchPath) (text : FileMap)
def InlayHintLabelPart.toLspInlayHintLabelPart (text : FileMap)
(p : InlayHintLabelPart) : IO Lsp.InlayHintLabelPart := do
let location? p.location?.bindM fun loc => loc.toLspLocation srcSearchPath text
let tooltip? := do return .markdown { kind := .markdown, value := p.tooltip? }
return {
value := p.value
location?,
tooltip?
}
let location? p.location?.bindM fun loc => loc.toLspLocation text
let tooltip? := do return .markdown { kind := .markdown, value := p.tooltip? }
return {
value := p.value
location?,
tooltip?
}
def InlayHintLabel.toLspInlayHintLabel (srcSearchPath : SearchPath) (text : FileMap) : InlayHintLabel IO Lsp.InlayHintLabel
def InlayHintLabel.toLspInlayHintLabel (text : FileMap) : InlayHintLabel IO Lsp.InlayHintLabel
| .name n => do return .name n
| .parts p => do return .parts <| p.mapM (·.toLspInlayHintLabelPart srcSearchPath text)
| .parts p => do return .parts <| p.mapM (·.toLspInlayHintLabelPart text)
def InlayHintKind.toLspInlayHintKind : InlayHintKind Lsp.InlayHintKind
| .type => .type
@@ -41,10 +41,10 @@ def InlayHintTextEdit.toLspTextEdit (text : FileMap) (e : InlayHintTextEdit) : L
newText := e.newText
}
def InlayHintInfo.toLspInlayHint (srcSearchPath : SearchPath) (text : FileMap) (i : InlayHintInfo) : IO Lsp.InlayHint := do
def InlayHintInfo.toLspInlayHint (text : FileMap) (i : InlayHintInfo) : IO Lsp.InlayHint := do
return {
position := text.utf8PosToLspPos i.position
label := i.label.toLspInlayHintLabel srcSearchPath text
label := i.label.toLspInlayHintLabel text
kind? := i.kind?.map (·.toLspInlayHintKind)
textEdits? := some <| i.textEdits.map (·.toLspTextEdit text)
tooltip? := do return .markdown { kind := .markdown, value := i.tooltip? }
@@ -116,7 +116,6 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
let ctx read
let text := ctx.doc.meta.text
let range := text.lspRangeToUtf8Range p.range
let srcSearchPath := ctx.srcSearchPath
if s.isFirstRequestAfterEdit then
-- We immediately respond to the first inlay hint request after an edit with the old inlay hints,
-- without waiting for the edit delay.
@@ -127,7 +126,7 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
-- To reduce the size of the window for this race condition, we attempt to minimize the delay
-- after an edit, providing VS Code with a set of old inlay hints that we have already updated
-- correctly for VS Code ASAP.
let lspInlayHints s.oldInlayHints.mapM (·.toLspInlayHint srcSearchPath text)
let lspInlayHints s.oldInlayHints.mapM (·.toLspInlayHint text)
let r := { response := lspInlayHints, isComplete := false }
let s := { s with isFirstRequestAfterEdit := false }
return (r, s)
@@ -149,7 +148,7 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
-- In the latter case, we respond with the old inlay hints, since we can't respond with an error.
-- This is to prevent cancellation from making us serve updated inlay hints before the
-- edit delay has passed.
let lspInlayHints s.oldInlayHints.mapM (·.toLspInlayHint srcSearchPath text)
let lspInlayHints s.oldInlayHints.mapM (·.toLspInlayHint text)
let r := { response := lspInlayHints, isComplete := false }
return (r, s)
let snaps := snaps.toArray
@@ -179,7 +178,7 @@ def handleInlayHints (p : InlayHintParams) (s : InlayHintState) :
modify (·.push ih.toInlayHintInfo))
let allInlayHints := newInlayHints ++ oldInlayHints
let inlayHintsInRange := allInlayHints.filter (range.contains (includeStop := true) ·.position)
let lspInlayHints inlayHintsInRange.mapM (·.toLspInlayHint srcSearchPath text)
let lspInlayHints inlayHintsInRange.mapM (·.toLspInlayHint text)
let r := { response := lspInlayHints, isComplete }
let s := { s with
oldInlayHints := allInlayHints
@@ -204,22 +203,6 @@ where
updateOldInlayHints (oldInlayHints : Array Elab.InlayHintInfo) : RequestM (Array Elab.InlayHintInfo) := do
let meta := ( read).doc.meta
let text := meta.text
let srcSearchPath := ( read).srcSearchPath
let modName? EIO.toBaseIO <| do
let some path := System.Uri.fileUriToPath? meta.uri
| return none
let some mod searchModuleNameOfFileName path srcSearchPath
| return some <| moduleNameOfFileName path none
return some mod
let modName match modName? with
| .ok (some modName) => pure modName
-- `.anonymous` occurs in untitled files (`.ok none` case).
-- There is an intentional bug here where the `.error _` case spits out `.anonymous`.
-- This means that we don't correctly update inlay hint locations when the file for this
-- file worker has been deleted. As of writing this, there are no inlay hints that use this
-- field anyways.
-- In the future, we should resolve this by caching the module name in `DocumentMeta`.
| _ => pure .anonymous
let mut updatedOldInlayHints := #[]
for ihi in oldInlayHints do
let mut ihi := ihi
@@ -228,7 +211,7 @@ where
let .rangeChange changeRange newText := c
| return #[] -- `fullChange` => all old inlay hints invalidated
let changeRange := text.lspRangeToUtf8Range changeRange
let some ihi' := applyEditToHint? modName ihi changeRange newText
let some ihi' := applyEditToHint? meta.mod ihi changeRange newText
| -- Change in some position of inlay hint => inlay hint invalidated
inlayHintInvalidated := true
break

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