mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-28 15:54:08 +00:00
Compare commits
2 Commits
upstream_A
...
issue_3713
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47f3e6bb92 | ||
|
|
34d8976646 |
2
.github/workflows/copyright-header.yml
vendored
2
.github/workflows/copyright-header.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
|
||||
- name: Verify .lean files start with a copyright header.
|
||||
run: |
|
||||
FILES=$(find ./src -type d \( -path "./src/lake/examples" -o -path "./src/lake/tests" \) -prune -o -type f -name "*.lean" -exec perl -ne 'BEGIN { $/ = undef; } print "$ARGV\n" if !m{\A/-\nCopyright}; exit;' {} \;)
|
||||
FILES=$(find . -type d \( -path "./tests" -o -path "./doc" -o -path "./src/lake/examples" -o -path "./src/lake/tests" -o -path "./build" -o -path "./nix" \) -prune -o -type f -name "*.lean" -exec perl -ne 'BEGIN { $/ = undef; } print "$ARGV\n" if !m{\A/-\nCopyright}; exit;' {} \;)
|
||||
if [ -n "$FILES" ]; then
|
||||
echo "Found .lean files which do not have a copyright header:"
|
||||
echo "$FILES"
|
||||
|
||||
3
.github/workflows/pr-release.yml
vendored
3
.github/workflows/pr-release.yml
vendored
@@ -149,8 +149,7 @@ jobs:
|
||||
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
|
||||
git -C lean4.git log -10 origin/master
|
||||
|
||||
git -C lean4.git fetch origin nightly-with-mathlib
|
||||
NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "origin/nightly-with-mathlib")"
|
||||
NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "nightly-with-mathlib")"
|
||||
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MATHLIB_SHA\`."
|
||||
fi
|
||||
|
||||
|
||||
@@ -78,10 +78,6 @@ add_custom_target(update-stage0
|
||||
COMMAND $(MAKE) -C stage1 update-stage0
|
||||
DEPENDS stage1)
|
||||
|
||||
add_custom_target(update-stage0-commit
|
||||
COMMAND $(MAKE) -C stage1 update-stage0-commit
|
||||
DEPENDS stage1)
|
||||
|
||||
add_custom_target(test
|
||||
COMMAND $(MAKE) -C stage1 test
|
||||
DEPENDS stage1)
|
||||
|
||||
24
CODEOWNERS
24
CODEOWNERS
@@ -21,27 +21,3 @@
|
||||
/src/Lean/Server/ @mhuisi
|
||||
/src/Lean/Widget/ @Vtec234
|
||||
/src/runtime/io.cpp @joehendrix
|
||||
/src/Init/Data/ @semorrison
|
||||
/src/Init/Data/Array/Lemmas.lean @digama0
|
||||
/src/Init/Data/List/Lemmas.lean @digama0
|
||||
/src/Init/Data/List/BasicAux.lean @digama0
|
||||
/src/Init/Data/Array/Subarray.lean @david-christiansen
|
||||
/src/Lean/Elab/Tactic/RCases.lean @digama0
|
||||
/src/Init/RCases.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Ext.lean @digama0
|
||||
/src/Init/Ext.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Simpa.lean @digama0
|
||||
/src/Lean/Elab/Tactic/NormCast.lean @digama0
|
||||
/src/Lean/Meta/Tactic/NormCast.lean @digama0
|
||||
/src/Lean/Meta/Tactic/TryThis.lean @digama0
|
||||
/src/Lean/Elab/Tactic/SimpTrace.lean @digama0
|
||||
/src/Lean/Elab/Tactic/NoMatch.lean @digama0
|
||||
/src/Lean/Elab/Tactic/ShowTerm.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Repeat.lean @digama0
|
||||
/src/Lean/Meta/Tactic/Repeat.lean @digama0
|
||||
/src/Lean/Meta/CoeAttr.lean @digama0
|
||||
/src/Lean/Elab/GuardMsgs.lean @digama0
|
||||
/src/Lean/Elab/Tactic/Guard.lean @digama0
|
||||
/src/Init/Guard.lean @digama0
|
||||
/src/Lean/Server/CodeActions/ @digama0
|
||||
|
||||
|
||||
32
RELEASES.md
32
RELEASES.md
@@ -21,7 +21,7 @@ v4.8.0 (development in progress)
|
||||
|
||||
* Importing two different files containing proofs of the same theorem is no longer considered an error. This feature is particularly useful for theorems that are automatically generated on demand (e.g., equational theorems).
|
||||
|
||||
* Functional induction principles.
|
||||
* Funcitonal induction principles.
|
||||
|
||||
Derived from the definition of a (possibly mutually) recursive function, a **functional induction principle** is created that is tailored to proofs about that function.
|
||||
|
||||
@@ -57,15 +57,6 @@ v4.8.0 (development in progress)
|
||||
```
|
||||
is recognized without having to say `termination_by arr.size - i`.
|
||||
|
||||
* Shorter instances names. There is a new algorithm for generating names for anonymous instances.
|
||||
Across Std and Mathlib, the median ratio between lengths of new names and of old names is about 72%.
|
||||
With the old algorithm, the longest name was 1660 characters, and now the longest name is 202 characters.
|
||||
The new algorithm's 95th percentile name length is 67 characters, versus 278 for the old algorithm.
|
||||
While the new algorithm produces names that are 1.2% less unique,
|
||||
it avoids cross-project collisions by adding a module-based suffix
|
||||
when it does not refer to declarations from the same "project" (modules that share the same root).
|
||||
PR [#3089](https://github.com/leanprover/lean4/pull/3089).
|
||||
|
||||
* Attribute `@[pp_using_anonymous_constructor]` to make structures pretty print like `⟨x, y, z⟩`
|
||||
rather than `{a := x, b := y, c := z}`.
|
||||
This attribute is applied to `Sigma`, `PSigma`, `PProd`, `Subtype`, `And`, and `Fin`.
|
||||
@@ -89,19 +80,8 @@ v4.8.0 (development in progress)
|
||||
Gives custom eliminators for `Nat` so that `induction` and `cases` put goal states into terms of `0` and `n + 1`
|
||||
rather than `Nat.zero` and `Nat.succ n`.
|
||||
Added option `tactic.customEliminators` to control whether to use custom eliminators.
|
||||
Added a hack for `rcases`/`rintro`/`obtain` to use the custom eliminator for `Nat`.
|
||||
[#3629](https://github.com/leanprover/lean4/pull/3629),
|
||||
[#3655](https://github.com/leanprover/lean4/pull/3655), and
|
||||
[#3747](https://github.com/leanprover/lean4/pull/3747).
|
||||
|
||||
* The `#guard_msgs` command now has options to change whitespace normalization and sensitivity to message ordering.
|
||||
For example, `#guard_msgs (whitespace := lax) in cmd` collapses whitespace before checking messages,
|
||||
and `#guard_msgs (ordering := sorted) in cmd` sorts the messages in lexicographic order before checking.
|
||||
PR [#3883](https://github.com/leanprover/lean4/pull/3883).
|
||||
|
||||
* The `#guard_msgs` command now supports showing a diff between the expected and actual outputs. This feature is currently
|
||||
disabled by default, but can be enabled with `set_option guard_msgs.diff true`. Depending on user feedback, this option
|
||||
may default to `true` in a future version of Lean.
|
||||
[#3629](https://github.com/leanprover/lean4/pull/3629) and
|
||||
[#3655](https://github.com/leanprover/lean4/pull/3655).
|
||||
|
||||
Breaking changes:
|
||||
|
||||
@@ -132,12 +112,6 @@ fact.def :
|
||||
|
||||
* The coercion from `String` to `Name` was removed. Previously, it was `Name.mkSimple`, which does not separate strings at dots, but experience showed that this is not always the desired coercion. For the previous behavior, manually insert a call to `Name.mkSimple`.
|
||||
|
||||
* The `Subarray` fields `as`, `h₁` and `h₂` have been renamed to `array`, `start_le_stop`, and `stop_le_array_size`, respectively. This more closely follows standard Lean conventions. Deprecated aliases for the field projections were added; these will be removed in a future release.
|
||||
|
||||
* The change to the instance name algorithm (described above) can break projects that made use of the auto-generated names.
|
||||
|
||||
* `Option.toMonad` has been renamed to `Option.getM` and the unneeded `[Monad m]` instance argument has been removed.
|
||||
|
||||
v4.7.0
|
||||
---------
|
||||
|
||||
|
||||
@@ -81,8 +81,20 @@ or using Github CLI with
|
||||
gh workflow run update-stage0.yml
|
||||
```
|
||||
|
||||
Leaving stage0 updates to the CI automation is preferable, but should you need to do it locally, you can use `make update-stage0-commit` in `build/release` to update `stage0` from `stage1` or `make -C stageN update-stage0-commit` to update from another stage.
|
||||
This command will automatically stage the updated files and introduce a commit, so make sure to commit your work before that. Then coordinate with the admins to not squash your PR so that stage 0 updates are preserved as separate commits.
|
||||
Leaving stage0 updates to the CI automation is preferrable, but should you need
|
||||
to do it locally, you can use `make update-stage0` in `build/release`, to
|
||||
update `stage0` from `stage1`, `make -C stageN update-stage0` to update from
|
||||
another stage, or `nix run .#update-stage0-commit` to update using nix.
|
||||
|
||||
Updates to `stage0` should be their own commits in the Git history. So should
|
||||
you have to include the stage0 update in your PR (rather than using above
|
||||
automation after merging changes), commit your work before running `make
|
||||
update-stage0`, commit the updated `stage0` compiler code with the commit
|
||||
message:
|
||||
```
|
||||
chore: update stage0
|
||||
```
|
||||
and coordinate with the admins to not squash your PR.
|
||||
|
||||
## Further Bootstrapping Complications
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- Reconcile discrepancies in the `v4.6.0` section,
|
||||
usually via copy and paste and a commit to `releases/v4.6.0`.
|
||||
- `git tag v4.6.0`
|
||||
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
|
||||
- `git push origin v4.6.0`
|
||||
- Now wait, while CI runs.
|
||||
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`,
|
||||
looking for the `v4.6.0` tag.
|
||||
@@ -34,76 +34,48 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
(e.g. `v4.6.0-rc1`), and quickly sanity check.
|
||||
- Next, we will move a curated list of downstream repos to the latest stable release.
|
||||
- For each of the repositories listed below:
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
|
||||
- Update the toolchain file
|
||||
- In the Lakefile, if there are dependencies on specific version tags of dependencies that you've already pushed as part of this process, update them to the new tag.
|
||||
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so it will work and be saved in the manifest
|
||||
- Run `lake update`
|
||||
- The PR title should be "chore: bump toolchain to v4.6.0".
|
||||
- Merge the PR once CI completes.
|
||||
- Create the tag `v4.6.0` from `master`/`main` and push it.
|
||||
- Merge the tag `v4.6.0` into the `stable` branch and push it.
|
||||
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`.
|
||||
The PR title should be "chore: bump toolchain to v4.6.0".
|
||||
Since the `v4.6.0` release should be functionally identical to the last release candidate,
|
||||
which the repository should already be on, this PR is a no-op besides changing the toolchain.
|
||||
- Once this is merged, create the tag `v4.6.0` from `master`/`main` and push it.
|
||||
- Merge the tag `v4.6.0` into the stable branch.
|
||||
- We do this for the repositories:
|
||||
- [lean4checker](https://github.com/leanprover/lean4checker)
|
||||
- No dependencies
|
||||
- Note: `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [Std](https://github.com/leanprover-community/std4)
|
||||
- No dependencies
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
|
||||
- [Std](https://github.com/leanprover-community/repl)
|
||||
- [ProofWidgets4](https://github.com/leanprover-community/ProofWidgets4)
|
||||
- Dependencies: `Std`
|
||||
- Note on versions and branches:
|
||||
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
|
||||
which does not refer to the toolchain being used.
|
||||
- Make a new release in this sequence after merging the toolchain bump PR.
|
||||
- `ProofWidgets` does not maintain a `stable` branch.
|
||||
- Toolchain bump PR
|
||||
- Create and push the tag, following the version convention of the repository
|
||||
- `ProofWidgets` uses a sequential version tagging scheme, e.g. `v0.0.29`,
|
||||
which does not refer to the toolchain being used.
|
||||
- Make a new release in this sequence after merging the toolchain bump PR.
|
||||
- `ProofWidgets` does not maintain a `stable` branch.
|
||||
- [Aesop](https://github.com/leanprover-community/aesop)
|
||||
- Dependencies: `Std`
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
- [doc-gen4](https://github.com/leanprover/doc-gen4)
|
||||
- Dependencies: exist, but they're not part of the release workflow
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [import-graph](https://github.com/leanprover-community/import-graph)
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- There is no `stable` branch; skip this step
|
||||
- [Mathlib](https://github.com/leanprover-community/mathlib4)
|
||||
- Dependencies: `Aesop`, `ProofWidgets4`, `lean4checker`, `Std`, `doc-gen4`, `import-graph`
|
||||
- Toolchain bump PR notes:
|
||||
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
|
||||
in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
|
||||
`git checkout toolchain/v4.6.0` to the appropriate tag,
|
||||
and then run `.github/workflows/mk_build_yml.sh`. Coordinate with
|
||||
a Mathlib maintainer to get this merged.
|
||||
- Push the PR branch to the main Mathlib repository rather than a fork, or CI may not work reliably
|
||||
- Create and push the tag
|
||||
- Create a new branch from the tag, push it, and open a pull request against `stable`.
|
||||
Coordinate with a Mathlib maintainer to get this merged.
|
||||
- In addition to updating the `lean-toolchain` and `lakefile.lean`,
|
||||
in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
|
||||
`git checkout toolchain/v4.6.0` to the appropriate tag,
|
||||
and then run `.github/workflows/mk_build_yml.sh`.
|
||||
- [REPL](https://github.com/leanprover-community/repl)
|
||||
- Dependencies: `Mathlib` (for test code)
|
||||
- Note that there are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
|
||||
- Toolchain bump PR including updated Lake manifest
|
||||
- Create and push the tag
|
||||
- Merge the tag into `stable`
|
||||
in the root, and in `test/Mathlib/`.
|
||||
- Note that there are dependencies between these packages:
|
||||
you should update the lakefile so that you are using the `v4.6.0` tag of upstream repositories
|
||||
(or the sequential tag for `ProofWidgets4`), and run `lake update` before committing.
|
||||
- This means that this process is sequential; each repository must have its bump PR merged,
|
||||
and the new tag pushed, before you can make the PR for the downstream repositories.
|
||||
- `lean4checker` has no dependencies
|
||||
- `Std` has no dependencies
|
||||
- `Aesop` depends on `Std`
|
||||
- `ProofWidgets4` depends on `Std`
|
||||
- `Mathlib` depends on `Aesop`, `ProofWidgets4`, and `lean4checker` (and transitively on `Std`)
|
||||
- `REPL` depends on `Mathlib` (this dependency is only for testing).
|
||||
- Merge the release announcement PR for the Lean website - it will be deployed automatically
|
||||
- Finally, make an announcement!
|
||||
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
|
||||
Please see previous announcements for suggested language.
|
||||
You will want a few bullet points for main topics from the release notes.
|
||||
Link to the blog post from the Zulip announcement.
|
||||
- Make sure that whoever is handling social media knows the release is out.
|
||||
Please also make sure that whoever is handling social media knows the release is out.
|
||||
|
||||
## Optimistic(?) time estimates:
|
||||
- Initial checks and push the tag: 30 minutes.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import Lean.Util.Profiler
|
||||
|
||||
/-!
|
||||
|
||||
Usage:
|
||||
```sh
|
||||
lean --run ./script/collideProfiles.lean **/*.lean.json ... > merged.json
|
||||
```
|
||||
|
||||
Merges multiple `trace.profiler.output` profiles into a single one while deduplicating samples with
|
||||
the same stack. This is useful for building cumulative profiles of medium-to-large projects because
|
||||
Firefox Profiler cannot handle hundreds of tracks and the deduplication will also ensure that the
|
||||
profile is small enough for uploading.
|
||||
|
||||
As ordering of samples is not meaningful after this transformation, only "Call Tree" and "Flame
|
||||
Graph" are useful for such profiles.
|
||||
-/
|
||||
|
||||
open Lean
|
||||
|
||||
def main (args : List String) : IO Unit := do
|
||||
let profiles ← args.toArray.mapM fun path => do
|
||||
let json ← IO.FS.readFile ⟨path⟩
|
||||
let profile ← IO.ofExcept $ Json.parse json
|
||||
IO.ofExcept <| fromJson? profile
|
||||
-- NOTE: `collide` should not be interpreted
|
||||
let profile := Firefox.Profile.collide profiles
|
||||
IO.println <| Json.compress <| toJson profile
|
||||
@@ -588,10 +588,6 @@ if(PREV_STAGE)
|
||||
COMMAND bash -c 'CSRCS=${CMAKE_BINARY_DIR}/lib/temp script/update-stage0'
|
||||
DEPENDS make_stdlib
|
||||
WORKING_DIRECTORY "${LEAN_SOURCE_DIR}/..")
|
||||
|
||||
add_custom_target(update-stage0-commit
|
||||
COMMAND git commit -m "chore: update stage0"
|
||||
DEPENDS update-stage0)
|
||||
endif()
|
||||
|
||||
# use Bash version for building, use Lean version in bin/ for tests & distribution
|
||||
|
||||
@@ -15,13 +15,6 @@ namespace Classical
|
||||
noncomputable def indefiniteDescription {α : Sort u} (p : α → Prop) (h : ∃ x, p x) : {x // p x} :=
|
||||
choice <| let ⟨x, px⟩ := h; ⟨⟨x, px⟩⟩
|
||||
|
||||
/--
|
||||
Given that there exists an element satisfying `p`, returns one such element.
|
||||
|
||||
This is a straightforward consequence of, and equivalent to, `Classical.choice`.
|
||||
|
||||
See also `choose_spec`, which asserts that the returned value has property `p`.
|
||||
-/
|
||||
noncomputable def choose {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : α :=
|
||||
(indefiniteDescription p h).val
|
||||
|
||||
|
||||
@@ -20,29 +20,8 @@ def Functor.discard {f : Type u → Type v} {α : Type u} [Functor f] (x : f α)
|
||||
|
||||
export Functor (discard)
|
||||
|
||||
/--
|
||||
An `Alternative` functor is an `Applicative` functor that can "fail" or be "empty"
|
||||
and a binary operation `<|>` that “collects values” or finds the “left-most success”.
|
||||
|
||||
Important instances include
|
||||
* `Option`, where `failure := none` and `<|>` returns the left-most `some`.
|
||||
* Parser combinators typically provide an `Applicative` instance for error-handling and
|
||||
backtracking.
|
||||
|
||||
Error recovery and state can interact subtly. For example, the implementation of `Alternative` for `OptionT (StateT σ Id)` keeps modifications made to the state while recovering from failure, while `StateT σ (OptionT Id)` discards them.
|
||||
-/
|
||||
-- NB: List instance is in mathlib. Once upstreamed, add
|
||||
-- * `List`, where `failure` is the empty list and `<|>` concatenates.
|
||||
class Alternative (f : Type u → Type v) extends Applicative f : Type (max (u+1) v) where
|
||||
/--
|
||||
Produces an empty collection or recoverable failure. The `<|>` operator collects values or recovers
|
||||
from failures. See `Alternative` for more details.
|
||||
-/
|
||||
failure : {α : Type u} → f α
|
||||
/--
|
||||
Depending on the `Alternative` instance, collects values or recovers from `failure`s by
|
||||
returning the leftmost success. Can be written using the `<|>` operator syntax.
|
||||
-/
|
||||
orElse : {α : Type u} → f α → (Unit → f α) → f α
|
||||
|
||||
instance (f : Type u → Type v) (α : Type u) [Alternative f] : OrElse (f α) := ⟨Alternative.orElse⟩
|
||||
@@ -51,15 +30,9 @@ variable {f : Type u → Type v} [Alternative f] {α : Type u}
|
||||
|
||||
export Alternative (failure)
|
||||
|
||||
/--
|
||||
If the proposition `p` is true, does nothing, else fails (using `failure`).
|
||||
-/
|
||||
@[always_inline, inline] def guard {f : Type → Type v} [Alternative f] (p : Prop) [Decidable p] : f Unit :=
|
||||
if p then pure () else failure
|
||||
|
||||
/--
|
||||
Returns `some x` if `f` succeeds with value `x`, else returns `none`.
|
||||
-/
|
||||
@[always_inline, inline] def optional (x : f α) : f (Option α) :=
|
||||
some <$> x <|> pure none
|
||||
|
||||
|
||||
@@ -12,15 +12,6 @@ open Function
|
||||
@[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
|
||||
rfl
|
||||
|
||||
/--
|
||||
The `Functor` typeclass only contains the operations of a functor.
|
||||
`LawfulFunctor` further asserts that these operations satisfy the laws of a functor,
|
||||
including the preservation of the identity and composition laws:
|
||||
```
|
||||
id <$> x = x
|
||||
(h ∘ g) <$> x = h <$> g <$> x
|
||||
```
|
||||
-/
|
||||
class LawfulFunctor (f : Type u → Type v) [Functor f] : Prop where
|
||||
map_const : (Functor.mapConst : α → f β → f α) = Functor.map ∘ const β
|
||||
id_map (x : f α) : id <$> x = x
|
||||
@@ -33,16 +24,6 @@ attribute [simp] id_map
|
||||
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
|
||||
id_map x
|
||||
|
||||
/--
|
||||
The `Applicative` typeclass only contains the operations of an applicative functor.
|
||||
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
|
||||
```
|
||||
pure id <*> v = v
|
||||
pure (·∘·) <*> u <*> v <*> w = u <*> (v <*> w)
|
||||
pure f <*> pure x = pure (f x)
|
||||
u <*> pure y = pure (· y) <*> u
|
||||
```
|
||||
-/
|
||||
class LawfulApplicative (f : Type u → Type v) [Applicative f] extends LawfulFunctor f : Prop where
|
||||
seqLeft_eq (x : f α) (y : f β) : x <* y = const β <$> x <*> y
|
||||
seqRight_eq (x : f α) (y : f β) : x *> y = const α id <$> x <*> y
|
||||
@@ -61,18 +42,6 @@ attribute [simp] map_pure seq_pure
|
||||
@[simp] theorem pure_id_seq [Applicative f] [LawfulApplicative f] (x : f α) : pure id <*> x = x := by
|
||||
simp [pure_seq]
|
||||
|
||||
/--
|
||||
The `Monad` typeclass only contains the operations of a monad.
|
||||
`LawfulMonad` further asserts that these operations satisfy the laws of a monad,
|
||||
including associativity and identity laws for `bind`:
|
||||
```
|
||||
pure x >>= f = f x
|
||||
x >>= pure = x
|
||||
x >>= f >>= g = x >>= (fun x => f x >>= g)
|
||||
```
|
||||
|
||||
`LawfulMonad.mk'` is an alternative constructor containing useful defaults for many fields.
|
||||
-/
|
||||
class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m : Prop where
|
||||
bind_pure_comp (f : α → β) (x : m α) : x >>= (fun a => pure (f a)) = f <$> x
|
||||
bind_map {α β : Type u} (f : m (α → β)) (x : m α) : f >>= (. <$> x) = f <*> x
|
||||
|
||||
@@ -235,13 +235,13 @@ end StateT
|
||||
|
||||
instance : LawfulMonad (EStateM ε σ) := .mk'
|
||||
(id_map := fun x => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonad, EStateM.map]
|
||||
dsimp only [EStateM.instMonadEStateM, EStateM.map]
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(bind_assoc := fun x _ _ => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonad, EStateM.bind]
|
||||
dsimp only [EStateM.instMonadEStateM, EStateM.bind]
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
|
||||
@@ -10,7 +10,7 @@ import Init.Control.Except
|
||||
|
||||
universe u v
|
||||
|
||||
instance : ToBool (Option α) := ⟨Option.isSome⟩
|
||||
instance : ToBool (Option α) := ⟨Option.toBool⟩
|
||||
|
||||
def OptionT (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Option α)
|
||||
|
||||
@@ -1308,6 +1308,7 @@ gen_injective_theorems% Fin
|
||||
gen_injective_theorems% Array
|
||||
gen_injective_theorems% Sum
|
||||
gen_injective_theorems% PSum
|
||||
gen_injective_theorems% Nat
|
||||
gen_injective_theorems% Option
|
||||
gen_injective_theorems% List
|
||||
gen_injective_theorems% Except
|
||||
@@ -1315,12 +1316,6 @@ gen_injective_theorems% EStateM.Result
|
||||
gen_injective_theorems% Lean.Name
|
||||
gen_injective_theorems% Lean.Syntax
|
||||
|
||||
theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ → m = n :=
|
||||
fun x => Nat.noConfusion x id
|
||||
|
||||
theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
|
||||
Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
|
||||
|
||||
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] (a b : α) : a == b ↔ a = b :=
|
||||
⟨eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl⟩
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import Init.Data.String
|
||||
import Init.Data.List
|
||||
import Init.Data.Int
|
||||
import Init.Data.Array
|
||||
import Init.Data.Array.Subarray.Split
|
||||
import Init.Data.ByteArray
|
||||
import Init.Data.FloatArray
|
||||
import Init.Data.Fin
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.MinMax
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.Data.Array.Mem
|
||||
@@ -188,8 +187,7 @@ theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start
|
||||
theorem mem_def (a : α) (as : Array α) : a ∈ as ↔ a ∈ as.data :=
|
||||
⟨fun | .mk h => h, Array.Mem.mk⟩
|
||||
|
||||
/-! # get -/
|
||||
|
||||
/-- # get -/
|
||||
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
|
||||
|
||||
theorem getElem?_lt
|
||||
@@ -219,7 +217,7 @@ theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default
|
||||
@[simp] theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) : a.get! i = (a.get? i).getD default := by
|
||||
by_cases p : i < a.size <;> simp [getD_get?, get!_eq_getD, p]
|
||||
|
||||
/-! # set -/
|
||||
/-- # set -/
|
||||
|
||||
@[simp] theorem getElem_set_eq (a : Array α) (i : Fin a.size) (v : α) {j : Nat}
|
||||
(eq : i.val = j) (p : j < (a.set i v).size) :
|
||||
@@ -242,7 +240,7 @@ theorem getElem_set (a : Array α) (i : Fin a.size) (v : α) (j : Nat)
|
||||
(ne : i.val ≠ j) : (a.set i v)[j]? = a[j]? := by
|
||||
by_cases h : j < a.size <;> simp [getElem?_lt, getElem?_ge, Nat.ge_of_not_lt, ne, h]
|
||||
|
||||
/-! # setD -/
|
||||
/- # setD -/
|
||||
|
||||
@[simp] theorem set!_is_setD : @set! = @setD := rfl
|
||||
|
||||
@@ -268,44 +266,4 @@ theorem getElem?_setD_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) : (a
|
||||
by_cases h : i < a.size <;>
|
||||
simp [setD, Nat.not_lt_of_le, h, getD_get?]
|
||||
|
||||
/-! # ofFn -/
|
||||
|
||||
@[simp] theorem size_ofFn_go {n} (f : Fin n → α) (i acc) :
|
||||
(ofFn.go f i acc).size = acc.size + (n - i) := by
|
||||
if hin : i < n then
|
||||
unfold ofFn.go
|
||||
have : 1 + (n - (i + 1)) = n - i :=
|
||||
Nat.sub_sub .. ▸ Nat.add_sub_cancel' (Nat.le_sub_of_add_le (Nat.add_comm .. ▸ hin))
|
||||
rw [dif_pos hin, size_ofFn_go f (i+1), size_push, Nat.add_assoc, this]
|
||||
else
|
||||
have : n - i = 0 := Nat.sub_eq_zero_of_le (Nat.le_of_not_lt hin)
|
||||
unfold ofFn.go
|
||||
simp [hin, this]
|
||||
termination_by n - i
|
||||
|
||||
@[simp] theorem size_ofFn (f : Fin n → α) : (ofFn f).size = n := by simp [ofFn]
|
||||
|
||||
theorem getElem_ofFn_go (f : Fin n → α) (i) {acc k}
|
||||
(hki : k < n) (hin : i ≤ n) (hi : i = acc.size)
|
||||
(hacc : ∀ j, ∀ hj : j < acc.size, acc[j] = f ⟨j, Nat.lt_of_lt_of_le hj (hi ▸ hin)⟩) :
|
||||
haveI : acc.size + (n - acc.size) = n := Nat.add_sub_cancel' (hi ▸ hin)
|
||||
(ofFn.go f i acc)[k]'(by simp [*]) = f ⟨k, hki⟩ := by
|
||||
unfold ofFn.go
|
||||
if hin : i < n then
|
||||
have : 1 + (n - (i + 1)) = n - i :=
|
||||
Nat.sub_sub .. ▸ Nat.add_sub_cancel' (Nat.le_sub_of_add_le (Nat.add_comm .. ▸ hin))
|
||||
simp only [dif_pos hin]
|
||||
rw [getElem_ofFn_go f (i+1) _ hin (by simp [*]) (fun j hj => ?hacc)]
|
||||
cases (Nat.lt_or_eq_of_le <| Nat.le_of_lt_succ (by simpa using hj)) with
|
||||
| inl hj => simp [get_push, hj, hacc j hj]
|
||||
| inr hj => simp [get_push, *]
|
||||
else
|
||||
simp [hin, hacc k (Nat.lt_of_lt_of_le hki (Nat.le_of_not_lt (hi ▸ hin)))]
|
||||
termination_by n - i
|
||||
|
||||
@[simp] theorem getElem_ofFn (f : Fin n → α) (i : Nat) (h) :
|
||||
(ofFn f)[i] = f ⟨i, size_ofFn f ▸ h⟩ :=
|
||||
getElem_ofFn_go _ _ _ (by simp) (by simp) nofun
|
||||
|
||||
|
||||
end Array
|
||||
|
||||
@@ -9,40 +9,25 @@ import Init.Data.Array.Basic
|
||||
universe u v w
|
||||
|
||||
structure Subarray (α : Type u) where
|
||||
array : Array α
|
||||
as : Array α
|
||||
start : Nat
|
||||
stop : Nat
|
||||
start_le_stop : start ≤ stop
|
||||
stop_le_array_size : stop ≤ array.size
|
||||
|
||||
@[deprecated Subarray.array]
|
||||
abbrev Subarray.as (s : Subarray α) : Array α := s.array
|
||||
|
||||
@[deprecated Subarray.start_le_stop]
|
||||
theorem Subarray.h₁ (s : Subarray α) : s.start ≤ s.stop := s.start_le_stop
|
||||
|
||||
@[deprecated Subarray.stop_le_array_size]
|
||||
theorem Subarray.h₂ (s : Subarray α) : s.stop ≤ s.as.size := s.stop_le_array_size
|
||||
h₁ : start ≤ stop
|
||||
h₂ : stop ≤ as.size
|
||||
|
||||
namespace Subarray
|
||||
|
||||
def size (s : Subarray α) : Nat :=
|
||||
s.stop - s.start
|
||||
|
||||
theorem size_le_array_size {s : Subarray α} : s.size ≤ s.array.size := by
|
||||
let {array, start, stop, start_le_stop, stop_le_array_size} := s
|
||||
simp [size]
|
||||
apply Nat.le_trans (Nat.sub_le stop start)
|
||||
assumption
|
||||
|
||||
def get (s : Subarray α) (i : Fin s.size) : α :=
|
||||
have : s.start + i.val < s.array.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.stop_le_array_size
|
||||
have : s.start + i.val < s.as.size := by
|
||||
apply Nat.lt_of_lt_of_le _ s.h₂
|
||||
have := i.isLt
|
||||
simp [size] at this
|
||||
rw [Nat.add_comm]
|
||||
exact Nat.add_lt_of_lt_sub this
|
||||
s.array[s.start + i.val]
|
||||
s.as[s.start + i.val]
|
||||
|
||||
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
|
||||
getElem xs i h := xs.get ⟨i, h⟩
|
||||
@@ -57,7 +42,7 @@ abbrev get! [Inhabited α] (s : Subarray α) (i : Nat) : α :=
|
||||
|
||||
def popFront (s : Subarray α) : Subarray α :=
|
||||
if h : s.start < s.stop then
|
||||
{ s with start := s.start + 1, start_le_stop := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
{ s with start := s.start + 1, h₁ := Nat.le_of_lt_succ (Nat.add_lt_add_right h 1) }
|
||||
else
|
||||
s
|
||||
|
||||
@@ -65,7 +50,7 @@ def popFront (s : Subarray α) : Subarray α :=
|
||||
let sz := USize.ofNat s.stop
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
if i < sz then
|
||||
let a := s.array.uget i lcProof
|
||||
let a := s.as.uget i lcProof
|
||||
match (← f a b) with
|
||||
| ForInStep.done b => pure b
|
||||
| ForInStep.yield b => loop (i+1) b
|
||||
@@ -83,27 +68,27 @@ instance : ForIn m (Subarray α) α where
|
||||
|
||||
@[inline]
|
||||
def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
as.as.foldlM f (init := init) (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Subarray α) : m β :=
|
||||
as.array.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
as.as.foldrM f (init := init) (start := as.stop) (stop := as.start)
|
||||
|
||||
@[inline]
|
||||
def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.anyM p (start := as.start) (stop := as.stop)
|
||||
as.as.anyM p (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Subarray α) : m Bool :=
|
||||
as.array.allM p (start := as.start) (stop := as.stop)
|
||||
as.as.allM p (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def forM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forM f (start := as.start) (stop := as.stop)
|
||||
as.as.forM f (start := as.start) (stop := as.stop)
|
||||
|
||||
@[inline]
|
||||
def forRevM {α : Type u} {m : Type v → Type w} [Monad m] (f : α → m PUnit) (as : Subarray α) : m PUnit :=
|
||||
as.array.forRevM f (start := as.stop) (stop := as.start)
|
||||
as.as.forRevM f (start := as.stop) (stop := as.start)
|
||||
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Subarray α) : β :=
|
||||
@@ -150,25 +135,15 @@ variable {α : Type u}
|
||||
|
||||
def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Subarray α :=
|
||||
if h₂ : stop ≤ as.size then
|
||||
if h₁ : start ≤ stop then
|
||||
{ array := as, start := start, stop := stop,
|
||||
start_le_stop := h₁, stop_le_array_size := h₂ }
|
||||
else
|
||||
{ array := as, start := stop, stop := stop,
|
||||
start_le_stop := Nat.le_refl _, stop_le_array_size := h₂ }
|
||||
if h₁ : start ≤ stop then
|
||||
{ as := as, start := start, stop := stop, h₁ := h₁, h₂ := h₂ }
|
||||
else
|
||||
{ as := as, start := stop, stop := stop, h₁ := Nat.le_refl _, h₂ := h₂ }
|
||||
else
|
||||
if h₁ : start ≤ as.size then
|
||||
{ array := as,
|
||||
start := start,
|
||||
stop := as.size,
|
||||
start_le_stop := h₁,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
else
|
||||
{ array := as,
|
||||
start := as.size,
|
||||
stop := as.size,
|
||||
start_le_stop := Nat.le_refl _,
|
||||
stop_le_array_size := Nat.le_refl _ }
|
||||
if h₁ : start ≤ as.size then
|
||||
{ as := as, start := start, stop := as.size, h₁ := h₁, h₂ := Nat.le_refl _ }
|
||||
else
|
||||
{ as := as, start := as.size, stop := as.size, h₁ := Nat.le_refl _, h₂ := Nat.le_refl _ }
|
||||
|
||||
@[coe]
|
||||
def ofSubarray (s : Subarray α) : Array α := Id.run do
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: David Thrane Christiansen
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import Init.Data.Array.Subarray
|
||||
import Init.Omega
|
||||
|
||||
/-
|
||||
This module contains splitting operations on subarrays that crucially rely on `omega` for proof
|
||||
automation. Placing them in another module breaks an import cycle, because `omega` itself uses the
|
||||
array library.
|
||||
-/
|
||||
|
||||
namespace Subarray
|
||||
/--
|
||||
Splits a subarray into two parts.
|
||||
-/
|
||||
def split (s : Subarray α) (i : Fin s.size.succ) : (Subarray α × Subarray α) :=
|
||||
let ⟨i', isLt⟩ := i
|
||||
have := s.start_le_stop
|
||||
have := s.stop_le_array_size
|
||||
have : i' ≤ s.stop - s.start := Nat.lt_succ.mp isLt
|
||||
have : s.start + i' ≤ s.stop := by omega
|
||||
have : s.start + i' ≤ s.array.size := by omega
|
||||
have : s.start + i' ≤ s.stop := by
|
||||
simp only [size] at isLt
|
||||
omega
|
||||
let pre := {s with
|
||||
stop := s.start + i',
|
||||
start_le_stop := by omega,
|
||||
stop_le_array_size := by assumption
|
||||
}
|
||||
let post := {s with
|
||||
start := s.start + i'
|
||||
start_le_stop := by assumption
|
||||
}
|
||||
(pre, post)
|
||||
|
||||
/--
|
||||
Removes the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
|
||||
subarray is empty.
|
||||
-/
|
||||
def drop (arr : Subarray α) (i : Nat) : Subarray α where
|
||||
array := arr.array
|
||||
start := min (arr.start + i) arr.stop
|
||||
stop := arr.stop
|
||||
start_le_stop := by
|
||||
rw [Nat.min_def]
|
||||
split <;> simp only [Nat.le_refl, *]
|
||||
stop_le_array_size := arr.stop_le_array_size
|
||||
|
||||
/--
|
||||
Keeps only the first `i` elements of the subarray. If there are `i` or fewer elements, the resulting
|
||||
subarray is empty.
|
||||
-/
|
||||
def take (arr : Subarray α) (i : Nat) : Subarray α where
|
||||
array := arr.array
|
||||
start := arr.start
|
||||
stop := min (arr.start + i) arr.stop
|
||||
start_le_stop := by
|
||||
have := arr.start_le_stop
|
||||
rw [Nat.min_def]
|
||||
split <;> omega
|
||||
stop_le_array_size := by
|
||||
have := arr.stop_le_array_size
|
||||
rw [Nat.min_def]
|
||||
split <;> omega
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer, Harun Khan, Abdalrhman M Mohamed
|
||||
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
@@ -34,7 +34,7 @@ structure BitVec (w : Nat) where
|
||||
O(1), because we use `Fin` as the internal representation of a bitvector. -/
|
||||
toFin : Fin (2^w)
|
||||
|
||||
@[deprecated] protected abbrev Std.BitVec := _root_.BitVec
|
||||
@[deprecated] abbrev Std.BitVec := _root_.BitVec
|
||||
|
||||
-- We manually derive the `DecidableEq` instances for `BitVec` because
|
||||
-- we want to have builtin support for bit-vector literals, and we
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix, Harun Khan, Alex Keizer, Abdalrhman M Mohamed,
|
||||
Authors: Joe Hendrix
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Bool
|
||||
@@ -728,7 +728,8 @@ theorem toNat_cons' {x : BitVec w} :
|
||||
rw [← BitVec.msb, msb_cons]
|
||||
|
||||
@[simp] theorem getMsb_cons_succ : (cons a x).getMsb (i + 1) = x.getMsb i := by
|
||||
simp [cons, Nat.le_add_left 1 i]
|
||||
simp [cons, cond_eq_if]
|
||||
omega
|
||||
|
||||
theorem truncate_succ (x : BitVec w) :
|
||||
truncate (i+1) x = cons (getLsb x i) (truncate i x) := by
|
||||
@@ -817,13 +818,9 @@ Definition of bitvector addition as a nat.
|
||||
.ofFin x + y = .ofFin (x + y.toFin) := rfl
|
||||
@[simp] theorem add_ofFin (x : BitVec n) (y : Fin (2^n)) :
|
||||
x + .ofFin y = .ofFin (x.toFin + y) := rfl
|
||||
|
||||
theorem ofNat_add {n} (x y : Nat) : (x + y)#n = x#n + y#n := by
|
||||
@[simp] theorem ofNat_add_ofNat {n} (x y : Nat) : x#n + y#n = (x + y)#n := by
|
||||
apply eq_of_toNat_eq ; simp [BitVec.ofNat]
|
||||
|
||||
theorem ofNat_add_ofNat {n} (x y : Nat) : x#n + y#n = (x + y)#n :=
|
||||
(ofNat_add x y).symm
|
||||
|
||||
protected theorem add_assoc (x y z : BitVec n) : x + y + z = x + (y + z) := by
|
||||
apply eq_of_toNat_eq ; simp [Nat.add_assoc]
|
||||
|
||||
@@ -839,15 +836,6 @@ theorem truncate_add (x y : BitVec w) (h : i ≤ w) :
|
||||
have dvd : 2^i ∣ 2^w := Nat.pow_dvd_pow _ h
|
||||
simp [bv_toNat, h, Nat.mod_mod_of_dvd _ dvd]
|
||||
|
||||
@[simp, bv_toNat] theorem toInt_add (x y : BitVec w) :
|
||||
(x + y).toInt = (x.toInt + y.toInt).bmod (2^w) := by
|
||||
simp [toInt_eq_toNat_bmod]
|
||||
|
||||
theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
|
||||
BitVec.ofInt n x + BitVec.ofInt n y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp
|
||||
|
||||
/-! ### sub/neg -/
|
||||
|
||||
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n (x.toNat + (2^n - y.toNat)) := by rfl
|
||||
@@ -924,15 +912,6 @@ instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_asso
|
||||
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
|
||||
right_id := BitVec.mul_one
|
||||
|
||||
@[simp, bv_toNat] theorem toInt_mul (x y : BitVec w) :
|
||||
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
|
||||
simp [toInt_eq_toNat_bmod]
|
||||
|
||||
theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
|
||||
BitVec.ofInt n x * BitVec.ofInt n y := by
|
||||
apply eq_of_toInt_eq
|
||||
simp
|
||||
|
||||
/-! ### le and lt -/
|
||||
|
||||
@[bv_toNat] theorem le_def (x y : BitVec n) :
|
||||
|
||||
@@ -220,12 +220,6 @@ due to `beq_iff_eq`.
|
||||
|
||||
/-! ### coercision related normal forms -/
|
||||
|
||||
theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
|
||||
(a == b) = decide (a = b) := by
|
||||
cases h : a == b
|
||||
· simp [ne_of_beq_false h]
|
||||
· simp [eq_of_beq h]
|
||||
|
||||
@[simp] theorem not_eq_not : ∀ {a b : Bool}, ¬a = !b ↔ a = b := by decide
|
||||
|
||||
@[simp] theorem not_not_eq : ∀ {a b : Bool}, ¬(!a) = b ↔ a = b := by decide
|
||||
@@ -236,11 +230,6 @@ theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
|
||||
@[simp] theorem coe_false_iff_true : ∀(a b : Bool), (a = false ↔ b) ↔ (!a) = b := by decide
|
||||
@[simp] theorem coe_false_iff_false : ∀(a b : Bool), (a = false ↔ b = false) ↔ (!a) = (!b) := by decide
|
||||
|
||||
/-! ### beq properties -/
|
||||
|
||||
theorem beq_comm {α} [BEq α] [LawfulBEq α] {a b : α} : (a == b) = (b == a) :=
|
||||
(Bool.coe_iff_coe (a == b) (b == a)).mp (by simp [@eq_comm α])
|
||||
|
||||
/-! ### xor -/
|
||||
|
||||
theorem false_xor : ∀ (x : Bool), xor false x = x := false_bne
|
||||
|
||||
@@ -13,40 +13,17 @@ namespace Fin
|
||||
instance coeToNat : CoeOut (Fin n) Nat :=
|
||||
⟨fun v => v.val⟩
|
||||
|
||||
/--
|
||||
From the empty type `Fin 0`, any desired result `α` can be derived. This is simlar to `Empty.elim`.
|
||||
-/
|
||||
def elim0.{u} {α : Sort u} : Fin 0 → α
|
||||
| ⟨_, h⟩ => absurd h (not_lt_zero _)
|
||||
|
||||
/--
|
||||
Returns the successor of the argument.
|
||||
|
||||
The bound in the result type is increased:
|
||||
```
|
||||
(2 : Fin 3).succ = (3 : Fin 4)
|
||||
```
|
||||
This differs from addition, which wraps around:
|
||||
```
|
||||
(2 : Fin 3) + 1 = (0 : Fin 3)
|
||||
```
|
||||
-/
|
||||
def succ : Fin n → Fin n.succ
|
||||
| ⟨i, h⟩ => ⟨i+1, Nat.succ_lt_succ h⟩
|
||||
|
||||
variable {n : Nat}
|
||||
|
||||
/--
|
||||
Returns `a` modulo `n + 1` as a `Fin n.succ`.
|
||||
-/
|
||||
protected def ofNat {n : Nat} (a : Nat) : Fin n.succ :=
|
||||
⟨a % (n+1), Nat.mod_lt _ (Nat.zero_lt_succ _)⟩
|
||||
|
||||
/--
|
||||
Returns `a` modulo `n` as a `Fin n`.
|
||||
|
||||
The assumption `n > 0` ensures that `Fin n` is nonempty.
|
||||
-/
|
||||
protected def ofNat' {n : Nat} (a : Nat) (h : n > 0) : Fin n :=
|
||||
⟨a % n, Nat.mod_lt _ h⟩
|
||||
|
||||
@@ -56,15 +33,12 @@ private theorem mlt {b : Nat} : {a : Nat} → a < n → b % n < n
|
||||
have : n > 0 := Nat.lt_trans (Nat.zero_lt_succ _) h;
|
||||
Nat.mod_lt _ this
|
||||
|
||||
/-- Addition modulo `n` -/
|
||||
protected def add : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + b) % n, mlt h⟩
|
||||
|
||||
/-- Multiplication modulo `n` -/
|
||||
protected def mul : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a * b) % n, mlt h⟩
|
||||
|
||||
/-- Subtraction modulo `n` -/
|
||||
protected def sub : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + (n - b)) % n, mlt h⟩
|
||||
|
||||
|
||||
@@ -541,7 +541,7 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
|
||||
∀ {a b : Fin (n + 1)} {ha : a ≠ 0} {hb : b ≠ 0}, a.pred ha = b.pred hb ↔ a = b
|
||||
| ⟨0, _⟩, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
|
||||
| ⟨i + 1, _⟩, ⟨0, _⟩, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
|
||||
| ⟨i + 1, hi⟩, ⟨j + 1, hj⟩, ha, hb => by simp [ext_iff, Nat.succ.injEq]
|
||||
| ⟨i + 1, hi⟩, ⟨j + 1, hj⟩, ha, hb => by simp [ext_iff]
|
||||
|
||||
@[simp] theorem pred_one {n : Nat} :
|
||||
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
|
||||
@@ -683,7 +683,6 @@ and `cast` defines the inductive step using `motive i.succ`, inducting downwards
|
||||
termination_by n + 1 - i
|
||||
decreasing_by decreasing_with
|
||||
-- FIXME: we put the proof down here to avoid getting a dummy `have` in the definition
|
||||
try simp only [Nat.succ_sub_succ_eq_sub]
|
||||
exact Nat.add_sub_add_right .. ▸ Nat.sub_lt_sub_left i.2 (Nat.lt_succ_self i)
|
||||
|
||||
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) → Sort _} {zero succ} :
|
||||
|
||||
@@ -100,7 +100,7 @@ protected def neg (n : @& Int) : Int :=
|
||||
```
|
||||
-/
|
||||
@[default_instance mid]
|
||||
instance instNegInt : Neg Int where
|
||||
instance : Neg Int where
|
||||
neg := Int.neg
|
||||
|
||||
/-- Subtraction of two natural numbers. -/
|
||||
@@ -173,13 +173,13 @@ inductive NonNeg : Int → Prop where
|
||||
/-- Definition of `a ≤ b`, encoded as `b - a ≥ 0`. -/
|
||||
protected def le (a b : Int) : Prop := NonNeg (b - a)
|
||||
|
||||
instance instLEInt : LE Int where
|
||||
instance : LE Int where
|
||||
le := Int.le
|
||||
|
||||
/-- Definition of `a < b`, encoded as `a + 1 ≤ b`. -/
|
||||
protected def lt (a b : Int) : Prop := (a + 1) ≤ b
|
||||
|
||||
instance instLTInt : LT Int where
|
||||
instance : LT Int where
|
||||
lt := Int.lt
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
|
||||
@@ -1054,39 +1054,19 @@ theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n + y) n = Int.bmo
|
||||
simp [Int.emod_def, Int.sub_eq_add_neg]
|
||||
rw [←Int.mul_neg, Int.add_right_comm, Int.bmod_add_mul_cancel]
|
||||
|
||||
@[simp]
|
||||
theorem emod_mul_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n * y) n = Int.bmod (x * y) n := by
|
||||
simp [Int.emod_def, Int.sub_eq_add_neg]
|
||||
rw [←Int.mul_neg, Int.add_mul, Int.mul_assoc, Int.bmod_add_mul_cancel]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_add_bmod_congr : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n := by
|
||||
rw [bmod_def x n]
|
||||
split
|
||||
case inl p =>
|
||||
simp only [emod_add_bmod_congr]
|
||||
simp
|
||||
case inr p =>
|
||||
rw [Int.sub_eq_add_neg, Int.add_right_comm, ←Int.sub_eq_add_neg]
|
||||
simp
|
||||
|
||||
@[simp] theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
|
||||
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]
|
||||
|
||||
@[simp]
|
||||
theorem bmod_mul_bmod : Int.bmod (Int.bmod x n * y) n = Int.bmod (x * y) n := by
|
||||
rw [bmod_def x n]
|
||||
split
|
||||
case inl p =>
|
||||
simp
|
||||
case inr p =>
|
||||
rw [Int.sub_mul, Int.sub_eq_add_neg, ← Int.mul_neg]
|
||||
simp
|
||||
|
||||
@[simp] theorem mul_bmod_bmod : Int.bmod (x * Int.bmod y n) n = Int.bmod (x * y) n := by
|
||||
rw [Int.mul_comm x, bmod_mul_bmod, Int.mul_comm x]
|
||||
|
||||
theorem add_bmod (a b : Int) (n : Nat) : (a + b).bmod n = (a.bmod n + b.bmod n).bmod n := by
|
||||
simp
|
||||
theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
|
||||
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]
|
||||
|
||||
theorem emod_bmod {x : Int} {m : Nat} : bmod (x % m) m = bmod x m := by
|
||||
simp [bmod]
|
||||
|
||||
@@ -5,7 +5,6 @@ Author: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.Ext
|
||||
|
||||
universe u
|
||||
|
||||
@@ -13,147 +12,60 @@ namespace List
|
||||
/-! The following functions can't be defined at `Init.Data.List.Basic`, because they depend on `Init.Util`,
|
||||
and `Init.Util` depends on `Init.Data.List.Basic`. -/
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function panics when executed, and returns
|
||||
`default`. See `get?` and `getD` for safer alternatives.
|
||||
-/
|
||||
def get! [Inhabited α] : (as : List α) → (i : Nat) → α
|
||||
def get! [Inhabited α] : List α → Nat → α
|
||||
| a::_, 0 => a
|
||||
| _::as, n+1 => get! as n
|
||||
| _, _ => panic! "invalid index"
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `none`.
|
||||
Also see `get`, `getD` and `get!`.
|
||||
-/
|
||||
def get? : (as : List α) → (i : Nat) → Option α
|
||||
def get? : List α → Nat → Option α
|
||||
| a::_, 0 => some a
|
||||
| _::as, n+1 => get? as n
|
||||
| _, _ => none
|
||||
|
||||
/--
|
||||
Returns the `i`-th element in the list (zero-based).
|
||||
def getD (as : List α) (idx : Nat) (a₀ : α) : α :=
|
||||
(as.get? idx).getD a₀
|
||||
|
||||
If the index is out of bounds (`i ≥ as.length`), this function returns `fallback`.
|
||||
See also `get?` and `get!`.
|
||||
-/
|
||||
def getD (as : List α) (i : Nat) (fallback : α) : α :=
|
||||
(as.get? i).getD fallback
|
||||
|
||||
@[ext] theorem ext : ∀ {l₁ l₂ : List α}, (∀ n, l₁.get? n = l₂.get? n) → l₁ = l₂
|
||||
| [], [], _ => rfl
|
||||
| a :: l₁, [], h => nomatch h 0
|
||||
| [], a' :: l₂, h => nomatch h 0
|
||||
| a :: l₁, a' :: l₂, h => by
|
||||
have h0 : some a = some a' := h 0
|
||||
injection h0 with aa; simp only [aa, ext fun n => h (n+1)]
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `head` and `headD` for safer alternatives.
|
||||
-/
|
||||
def head! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
| a::_ => a
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
|
||||
If the list is empty, this function returns `none`.
|
||||
Also see `headD` and `head!`.
|
||||
-/
|
||||
def head? : List α → Option α
|
||||
| [] => none
|
||||
| a::_ => some a
|
||||
|
||||
/--
|
||||
Returns the first element in the list.
|
||||
|
||||
If the list is empty, this function returns `fallback`.
|
||||
Also see `head?` and `head!`.
|
||||
-/
|
||||
def headD : (as : List α) → (fallback : α) → α
|
||||
| [], fallback => fallback
|
||||
def headD : List α → α → α
|
||||
| [], a₀ => a₀
|
||||
| a::_, _ => a
|
||||
|
||||
/--
|
||||
Returns the first element of a non-empty list.
|
||||
-/
|
||||
def head : (as : List α) → as ≠ [] → α
|
||||
| a::_, _ => a
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns the empty list.
|
||||
See `tail` and `tailD` for safer alternatives.
|
||||
-/
|
||||
def tail! : List α → List α
|
||||
| [] => panic! "empty list"
|
||||
| _::as => as
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
|
||||
If the list is empty, this function returns `none`.
|
||||
Also see `tailD` and `tail!`.
|
||||
-/
|
||||
def tail? : List α → Option (List α)
|
||||
| [] => none
|
||||
| _::as => some as
|
||||
|
||||
/--
|
||||
Drops the first element of the list.
|
||||
def tailD : List α → List α → List α
|
||||
| [], as₀ => as₀
|
||||
| _::as, _ => as
|
||||
|
||||
If the list is empty, this function returns `fallback`.
|
||||
Also see `head?` and `head!`.
|
||||
-/
|
||||
def tailD (list fallback : List α) : List α :=
|
||||
match list with
|
||||
| [] => fallback
|
||||
| _ :: tl => tl
|
||||
|
||||
/--
|
||||
Returns the last element of a non-empty list.
|
||||
-/
|
||||
def getLast : ∀ (as : List α), as ≠ [] → α
|
||||
| [], h => absurd rfl h
|
||||
| [a], _ => a
|
||||
| _::b::as, _ => getLast (b::as) (fun h => List.noConfusion h)
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
|
||||
If the list is empty, this function panics when executed, and returns `default`.
|
||||
See `getLast` and `getLastD` for safer alternatives.
|
||||
-/
|
||||
def getLast! [Inhabited α] : List α → α
|
||||
| [] => panic! "empty list"
|
||||
| a::as => getLast (a::as) (fun h => List.noConfusion h)
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
|
||||
If the list is empty, this function returns `none`.
|
||||
Also see `getLastD` and `getLast!`.
|
||||
-/
|
||||
def getLast? : List α → Option α
|
||||
| [] => none
|
||||
| a::as => some (getLast (a::as) (fun h => List.noConfusion h))
|
||||
|
||||
/--
|
||||
Returns the last element in the list.
|
||||
|
||||
If the list is empty, this function returns `fallback`.
|
||||
Also see `getLast?` and `getLast!`.
|
||||
-/
|
||||
def getLastD : (as : List α) → (fallback : α) → α
|
||||
def getLastD : List α → α → α
|
||||
| [], a₀ => a₀
|
||||
| a::as, _ => getLast (a::as) (fun h => List.noConfusion h)
|
||||
|
||||
|
||||
@@ -40,13 +40,6 @@ Finally, we rarely use `mapM` with something that is not a `Monad`.
|
||||
Users that want to use `mapM` with `Applicative` should use `mapA` instead.
|
||||
-/
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
See `List.forM` for the variant that discards the results.
|
||||
See `List.mapA` for the variant that works with `Applicative`.
|
||||
-/
|
||||
@[inline]
|
||||
def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m β) (as : List α) : m (List β) :=
|
||||
let rec @[specialize] loop
|
||||
@@ -54,42 +47,17 @@ def mapM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α
|
||||
| a :: as, bs => do loop as ((← f a)::bs)
|
||||
loop as []
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right, and returns the list of
|
||||
results.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `mapM` can be more efficient.
|
||||
|
||||
See `List.forA` for the variant that discards the results.
|
||||
See `List.mapM` for the variant that works with `Monad`.
|
||||
|
||||
**Warning**: this function is not tail-recursive, meaning that it may fail with a stack overflow on long lists.
|
||||
-/
|
||||
@[specialize]
|
||||
def mapA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m (List β)
|
||||
| [] => pure []
|
||||
| a::as => List.cons <$> f a <*> mapA f as
|
||||
|
||||
/--
|
||||
Applies the monadic action `f` on every element in the list, left-to-right.
|
||||
|
||||
See `List.mapM` for the variant that collects results.
|
||||
See `List.forA` for the variant that works with `Applicative`.
|
||||
-/
|
||||
@[specialize]
|
||||
protected def forM {m : Type u → Type v} [Monad m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
match as with
|
||||
| [] => pure ⟨⟩
|
||||
| a :: as => do f a; List.forM as f
|
||||
|
||||
/--
|
||||
Applies the applicative action `f` on every element in the list, left-to-right.
|
||||
|
||||
NB: If `m` is also a `Monad`, then using `forM` can be more efficient.
|
||||
|
||||
See `List.mapA` for the variant that collects results.
|
||||
See `List.forM` for the variant that works with `Monad`.
|
||||
-/
|
||||
@[specialize]
|
||||
def forA {m : Type u → Type v} [Applicative m] {α : Type w} (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
match as with
|
||||
@@ -103,27 +71,15 @@ def filterAuxM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) :
|
||||
let b ← f h
|
||||
filterAuxM f t (cond b (h :: acc) acc)
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, left-to-right, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
-/
|
||||
@[inline]
|
||||
def filterM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) := do
|
||||
let as ← filterAuxM p as []
|
||||
def filterM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) (as : List α) : m (List α) := do
|
||||
let as ← filterAuxM f as []
|
||||
pure as.reverse
|
||||
|
||||
/--
|
||||
Applies the monadic predicate `p` on every element in the list, right-to-left, and returns those
|
||||
elements `x` for which `p x` returns `true`.
|
||||
-/
|
||||
@[inline]
|
||||
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (p : α → m Bool) (as : List α) : m (List α) :=
|
||||
filterAuxM p as.reverse []
|
||||
def filterRevM {m : Type → Type v} [Monad m] {α : Type} (f : α → m Bool) (as : List α) : m (List α) :=
|
||||
filterAuxM f as.reverse []
|
||||
|
||||
/--
|
||||
Applies the monadic function `f` on every element `x` in the list, left-to-right, and returns those
|
||||
results `y` for which `f x` returns `some y`.
|
||||
-/
|
||||
@[inline]
|
||||
def filterMapM {m : Type u → Type v} [Monad m] {α β : Type u} (f : α → m (Option β)) (as : List α) : m (List β) :=
|
||||
let rec @[specialize] loop
|
||||
@@ -134,16 +90,6 @@ def filterMapM {m : Type u → Type v} [Monad m] {α β : Type u} (f : α → m
|
||||
| some b => loop as (b::bs)
|
||||
loop as.reverse []
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from left to right:
|
||||
```
|
||||
foldlM f x₀ [a, b, c] = do
|
||||
let x₁ ← f x₀ a
|
||||
let x₂ ← f x₁ b
|
||||
let x₃ ← f x₂ c
|
||||
pure x₃
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
protected def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} : (f : s → α → m s) → (init : s) → List α → m s
|
||||
| _, s, [] => pure s
|
||||
@@ -151,26 +97,10 @@ protected def foldlM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w
|
||||
let s' ← f s a
|
||||
List.foldlM f s' as
|
||||
|
||||
/--
|
||||
Folds a monadic function over a list from right to left:
|
||||
```
|
||||
foldrM f x₀ [a, b, c] = do
|
||||
let x₁ ← f c x₀
|
||||
let x₂ ← f b x₁
|
||||
let x₃ ← f a x₂
|
||||
pure x₃
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def foldrM {m : Type u → Type v} [Monad m] {s : Type u} {α : Type w} (f : α → s → m s) (init : s) (l : List α) : m s :=
|
||||
l.reverse.foldlM (fun s a => f a s) init
|
||||
|
||||
/--
|
||||
Maps `f` over the list and collects the results with `<|>`.
|
||||
```
|
||||
firstM f [a, b, c] = f a <|> f b <|> f c <|> failure
|
||||
```
|
||||
-/
|
||||
@[specialize]
|
||||
def firstM {m : Type u → Type v} [Alternative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m β
|
||||
| [] => failure
|
||||
|
||||
@@ -249,14 +249,12 @@ theorem getD_eq_get? : ∀ l n (a : α), getD l n a = (get? l n).getD a
|
||||
theorem get?_append_right : ∀ {l₁ l₂ : List α} {n : Nat}, l₁.length ≤ n →
|
||||
(l₁ ++ l₂).get? n = l₂.get? (n - l₁.length)
|
||||
| [], _, n, _ => rfl
|
||||
| a :: l, _, n+1, h₁ => by
|
||||
rw [cons_append]
|
||||
simp [Nat.succ_sub_succ_eq_sub, get?_append_right (Nat.lt_succ.1 h₁)]
|
||||
| a :: l, _, n+1, h₁ => by rw [cons_append]; simp [get?_append_right (Nat.lt_succ.1 h₁)]
|
||||
|
||||
theorem get?_reverse' : ∀ {l : List α} (i j), i + j + 1 = length l →
|
||||
get? l.reverse i = get? l j
|
||||
| [], _, _, _ => rfl
|
||||
| a::l, i, 0, h => by simp [Nat.succ.injEq] at h; simp [h, get?_append_right, Nat.succ.injEq]
|
||||
| a::l, i, 0, h => by simp at h; simp [h, get?_append_right]
|
||||
| a::l, i, j+1, h => by
|
||||
have := Nat.succ.inj h; simp at this ⊢
|
||||
rw [get?_append, get?_reverse' _ j this]
|
||||
@@ -274,19 +272,6 @@ theorem get?_reverse {l : List α} (i) (h : i < length l) :
|
||||
|
||||
@[simp] theorem getD_cons_succ : getD (x :: xs) (n + 1) d = getD xs n d := rfl
|
||||
|
||||
theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||||
(h : ∀ n h₁ h₂, get l₁ ⟨n, h₁⟩ = get l₂ ⟨n, h₂⟩) : l₁ = l₂ :=
|
||||
ext fun n =>
|
||||
if h₁ : n < length l₁ then by
|
||||
rw [get?_eq_get, get?_eq_get, h n h₁ (by rwa [← hl])]
|
||||
else by
|
||||
have h₁ := Nat.le_of_not_lt h₁
|
||||
rw [get?_len_le h₁, get?_len_le]; rwa [← hl]
|
||||
|
||||
@[simp] theorem get_map (f : α → β) {l n} :
|
||||
get (map f l) n = f (get l ⟨n, length_map l f ▸ n.2⟩) :=
|
||||
Option.some.inj <| by rw [← get?_eq_get, get?_map, get?_eq_get]; rfl
|
||||
|
||||
/-! ### take and drop -/
|
||||
|
||||
@[simp] theorem take_append_drop : ∀ (n : Nat) (l : List α), take n l ++ drop n l = l
|
||||
@@ -404,14 +389,6 @@ theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
|
||||
|
||||
theorem foldr_self (l : List α) : l.foldr cons [] = l := by simp
|
||||
|
||||
theorem foldl_map (f : β₁ → β₂) (g : α → β₂ → α) (l : List β₁) (init : α) :
|
||||
(l.map f).foldl g init = l.foldl (fun x y => g x (f y)) init := by
|
||||
induction l generalizing init <;> simp [*]
|
||||
|
||||
theorem foldr_map (f : α₁ → α₂) (g : α₂ → β → β) (l : List α₁) (init : β) :
|
||||
(l.map f).foldr g init = l.foldr (fun x y => g (f x) y) init := by
|
||||
induction l generalizing init <;> simp [*]
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
/-- Alternate (non-tail-recursive) form of mapM for proofs. -/
|
||||
|
||||
@@ -19,4 +19,3 @@ import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Nat.Mod
|
||||
import Init.Data.Nat.Lcm
|
||||
import Init.Data.Nat.Compare
|
||||
import Init.Data.Nat.Simproc
|
||||
|
||||
@@ -174,7 +174,7 @@ protected theorem add_right_comm (n m k : Nat) : (n + m) + k = (n + k) + m := by
|
||||
protected theorem add_left_cancel {n m k : Nat} : n + m = n + k → m = k := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih => simp [succ_add, succ.injEq]; intro h; apply ih h
|
||||
| succ n ih => simp [succ_add]; intro h; apply ih h
|
||||
|
||||
protected theorem add_right_cancel {n m k : Nat} (h : n + m = k + m) : n = k := by
|
||||
rw [Nat.add_comm n m, Nat.add_comm k m] at h
|
||||
@@ -248,7 +248,7 @@ theorem lt_succ_of_le {n m : Nat} : n ≤ m → n < succ m := succ_le_succ
|
||||
|
||||
@[simp] protected theorem sub_zero (n : Nat) : n - 0 = n := rfl
|
||||
|
||||
theorem succ_sub_succ_eq_sub (n m : Nat) : succ n - succ m = n - m := by
|
||||
@[simp] theorem succ_sub_succ_eq_sub (n m : Nat) : succ n - succ m = n - m := by
|
||||
induction m with
|
||||
| zero => exact rfl
|
||||
| succ m ih => apply congrArg pred ih
|
||||
@@ -574,7 +574,7 @@ theorem eq_zero_or_eq_succ_pred : ∀ n, n = 0 ∨ n = succ (pred n)
|
||||
| 0 => .inl rfl
|
||||
| _+1 => .inr rfl
|
||||
|
||||
theorem succ_inj' : succ a = succ b ↔ a = b := (Nat.succ.injEq a b).to_iff
|
||||
theorem succ_inj' : succ a = succ b ↔ a = b := ⟨succ.inj, congrArg _⟩
|
||||
|
||||
theorem succ_le_succ_iff : succ a ≤ succ b ↔ a ≤ b := ⟨le_of_succ_le_succ, succ_le_succ⟩
|
||||
|
||||
@@ -802,7 +802,7 @@ theorem add_sub_of_le {a b : Nat} (h : a ≤ b) : a + (b - a) = b := by
|
||||
protected theorem add_sub_add_right (n k m : Nat) : (n + k) - (m + k) = n - m := by
|
||||
induction k with
|
||||
| zero => simp
|
||||
| succ k ih => simp [← Nat.add_assoc, succ_sub_succ_eq_sub, ih]
|
||||
| succ k ih => simp [← Nat.add_assoc, ih]
|
||||
|
||||
protected theorem add_sub_add_left (k n m : Nat) : (k + n) - (k + m) = n - m := by
|
||||
rw [Nat.add_comm k n, Nat.add_comm k m, Nat.add_sub_add_right]
|
||||
|
||||
@@ -9,7 +9,6 @@ import Init.Data.Bool
|
||||
import Init.Data.Int.Pow
|
||||
import Init.Data.Nat.Bitwise.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.Nat.Simproc
|
||||
import Init.TacticsExtra
|
||||
import Init.Omega
|
||||
|
||||
@@ -272,7 +271,7 @@ theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
|
||||
induction i generalizing n x with
|
||||
| zero =>
|
||||
match n with
|
||||
| 0 => simp [succ_sub_succ_eq_sub]
|
||||
| 0 => simp
|
||||
| n+1 =>
|
||||
simp [not_decide_mod_two_eq_one]
|
||||
omega
|
||||
@@ -280,7 +279,7 @@ theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
|
||||
simp only [testBit_succ]
|
||||
match n with
|
||||
| 0 =>
|
||||
simp [decide_eq_false, succ_sub_succ_eq_sub]
|
||||
simp [decide_eq_false]
|
||||
| n+1 =>
|
||||
rw [Nat.two_pow_succ_sub_succ_div_two, ih]
|
||||
· simp [Nat.succ_lt_succ_iff]
|
||||
|
||||
@@ -28,7 +28,7 @@ protected def div (x y : @& Nat) : Nat :=
|
||||
0
|
||||
decreasing_by apply div_rec_lemma; assumption
|
||||
|
||||
instance instDiv : Div Nat := ⟨Nat.div⟩
|
||||
instance : Div Nat := ⟨Nat.div⟩
|
||||
|
||||
theorem div_eq (x y : Nat) : x / y = if 0 < y ∧ y ≤ x then (x - y) / y + 1 else 0 := by
|
||||
show Nat.div x y = _
|
||||
@@ -90,7 +90,7 @@ protected def mod : @& Nat → @& Nat → Nat
|
||||
| 0, _ => 0
|
||||
| x@(_ + 1), y => Nat.modCore x y
|
||||
|
||||
instance instMod : Mod Nat := ⟨Nat.mod⟩
|
||||
instance : Mod Nat := ⟨Nat.mod⟩
|
||||
|
||||
protected theorem modCore_eq_mod (x y : Nat) : Nat.modCore x y = x % y := by
|
||||
cases x with
|
||||
|
||||
@@ -10,24 +10,6 @@ import Init.RCases
|
||||
|
||||
namespace Nat
|
||||
|
||||
/--
|
||||
Computes the greatest common divisor of two natural numbers.
|
||||
|
||||
This reference implementation via the Euclidean algorithm
|
||||
is overridden in both the kernel and the compiler to efficiently
|
||||
evaluate using the "bignum" representation (see `Nat`).
|
||||
The definition provided here is the logical model
|
||||
(and it is soundness-critical that they coincide).
|
||||
|
||||
The GCD of two natural numbers is the largest natural number
|
||||
that divides both arguments.
|
||||
In particular, the GCD of a number and `0` is the number itself:
|
||||
```
|
||||
example : Nat.gcd 10 15 = 5 := rfl
|
||||
example : Nat.gcd 0 5 = 5 := rfl
|
||||
example : Nat.gcd 7 0 = 7 := rfl
|
||||
```
|
||||
-/
|
||||
@[extern "lean_nat_gcd"]
|
||||
def gcd (m n : @& Nat) : Nat :=
|
||||
if m = 0 then
|
||||
|
||||
@@ -88,7 +88,7 @@ protected theorem add_pos_right (m) (h : 0 < n) : 0 < m + n :=
|
||||
Nat.lt_of_lt_of_le h (Nat.le_add_left ..)
|
||||
|
||||
protected theorem add_self_ne_one : ∀ n, n + n ≠ 1
|
||||
| n+1, h => by rw [Nat.succ_add, Nat.succ.injEq] at h; contradiction
|
||||
| n+1, h => by rw [Nat.succ_add, Nat.succ_inj'] at h; contradiction
|
||||
|
||||
/-! ## sub -/
|
||||
|
||||
|
||||
@@ -580,7 +580,7 @@ attribute [-simp] Nat.right_distrib Nat.left_distrib
|
||||
|
||||
theorem PolyCnstr.denote_mul (ctx : Context) (k : Nat) (c : PolyCnstr) : (c.mul (k+1)).denote ctx = c.denote ctx := by
|
||||
cases c; rename_i eq lhs rhs
|
||||
have : k ≠ 0 → k + 1 ≠ 1 := by intro h; match k with | 0 => contradiction | k+1 => simp [Nat.succ.injEq]
|
||||
have : k ≠ 0 → k + 1 ≠ 1 := by intro h; match k with | 0 => contradiction | k+1 => simp
|
||||
have : ¬ (k == 0) → (k + 1 == 1) = false := fun h => beq_false_of_ne (this (ne_of_beq_false (Bool.of_not_eq_true h)))
|
||||
have : ¬ ((k + 1 == 0) = true) := fun h => absurd (eq_of_beq h) (Nat.succ_ne_zero k)
|
||||
have : (1 == (0 : Nat)) = false := rfl
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2023 Lean FRO. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Joe Hendrix
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Bool
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
/-!
|
||||
This contains lemmas used by the Nat simprocs for simplifying arithmetic
|
||||
addition offsets.
|
||||
-/
|
||||
namespace Nat.Simproc
|
||||
|
||||
/- Sub proofs -/
|
||||
|
||||
theorem sub_add_eq_comm (a b c : Nat) : a - (b + c) = a - c - b := by
|
||||
rw [Nat.add_comm b c]
|
||||
exact Nat.sub_add_eq a c b
|
||||
|
||||
theorem add_sub_add_le (a c : Nat) {b d : Nat} (h : b ≤ d) : a + b - (c + d) = a - (c + (d-b)) := by
|
||||
induction b generalizing a c d with
|
||||
| zero =>
|
||||
simp
|
||||
| succ b ind =>
|
||||
match d with
|
||||
| 0 =>
|
||||
contradiction
|
||||
| d + 1 =>
|
||||
have g := Nat.le_of_succ_le_succ h
|
||||
rw [Nat.add_succ a, Nat.add_succ c, Nat.succ_sub_succ, Nat.succ_sub_succ,
|
||||
ind _ _ g]
|
||||
|
||||
theorem add_sub_add_ge (a c : Nat) {b d : Nat} (h : b ≥ d) : a + b - (c + d) = a + (b - d) - c := by
|
||||
rw [Nat.add_comm c d, Nat.sub_add_eq, Nat.add_sub_assoc h a]
|
||||
|
||||
theorem add_sub_le (a : Nat) {b c : Nat} (h : b ≤ c) : a + b - c = a - (c - b) := by
|
||||
have p := add_sub_add_le a 0 h
|
||||
simp only [Nat.zero_add] at p
|
||||
exact p
|
||||
|
||||
/- Eq proofs -/
|
||||
|
||||
theorem add_eq_gt (a : Nat) {b c : Nat} (h : b > c) : (a + b = c) = False :=
|
||||
eq_false (Nat.ne_of_gt (Nat.lt_of_lt_of_le h (le_add_left b a)))
|
||||
|
||||
theorem eq_add_gt (a : Nat) {b c : Nat} (h : c > a) : (a = b + c) = False := by
|
||||
rw [@Eq.comm Nat a (b + c)]
|
||||
exact add_eq_gt b h
|
||||
|
||||
theorem add_eq_add_le (a c : Nat) {b d : Nat} (h : b ≤ d) : (a + b = c + d) = (a = c + (d - b)) := by
|
||||
have g : b ≤ c + d := Nat.le_trans h (le_add_left d c)
|
||||
rw [← Nat.add_sub_assoc h, @Eq.comm _ a, Nat.sub_eq_iff_eq_add g, @Eq.comm _ (a + b)]
|
||||
|
||||
theorem add_eq_add_ge (a c : Nat) {b d : Nat} (h : b ≥ d) : (a + b = c + d) = (a + (b - d) = c) := by
|
||||
rw [@Eq.comm _ (a + b) _, add_eq_add_le c a h, @Eq.comm _ _ c]
|
||||
|
||||
theorem add_eq_le (a : Nat) {b c : Nat} (h : b ≤ c) : (a + b = c) = (a = c - b) := by
|
||||
have r := add_eq_add_le a 0 h
|
||||
simp only [Nat.zero_add] at r
|
||||
exact r
|
||||
|
||||
theorem eq_add_le {a : Nat} (b : Nat) {c : Nat} (h : c ≤ a) : (a = b + c) = (b = a - c) := by
|
||||
rw [@Eq.comm Nat a (b + c)]
|
||||
exact add_eq_le b h
|
||||
|
||||
/- Lemmas for lifting Eq proofs to beq -/
|
||||
|
||||
theorem beqEqOfEqEq {a b c d : Nat} (p : (a = b) = (c = d)) : (a == b) = (c == d) := by
|
||||
simp only [Bool.beq_eq_decide_eq, p]
|
||||
|
||||
theorem beqFalseOfEqFalse {a b : Nat} (p : (a = b) = False) : (a == b) = false := by
|
||||
simp [Bool.beq_eq_decide_eq, p]
|
||||
|
||||
theorem bneEqOfEqEq {a b c d : Nat} (p : (a = b) = (c = d)) : (a != b) = (c != d) := by
|
||||
simp only [bne, beqEqOfEqEq p]
|
||||
|
||||
theorem bneTrueOfEqFalse {a b : Nat} (p : (a = b) = False) : (a != b) = true := by
|
||||
simp [bne, beqFalseOfEqFalse p]
|
||||
|
||||
/- le proofs -/
|
||||
|
||||
theorem add_le_add_le (a c : Nat) {b d : Nat} (h : b ≤ d) : (a + b ≤ c + d) = (a ≤ c + (d - b)) := by
|
||||
rw [← Nat.add_sub_assoc h, Nat.le_sub_iff_add_le]
|
||||
exact Nat.le_trans h (le_add_left d c)
|
||||
|
||||
theorem add_le_add_ge (a c : Nat) {b d : Nat} (h : b ≥ d) : (a + b ≤ c + d) = (a + (b - d) ≤ c) := by
|
||||
rw [← Nat.add_sub_assoc h, Nat.sub_le_iff_le_add]
|
||||
|
||||
theorem add_le_le (a : Nat) {b c : Nat} (h : b ≤ c) : (a + b ≤ c) = (a ≤ c - b) := by
|
||||
have r := add_le_add_le a 0 h
|
||||
simp only [Nat.zero_add] at r
|
||||
exact r
|
||||
|
||||
theorem add_le_gt (a : Nat) {b c : Nat} (h : b > c) : (a + b ≤ c) = False :=
|
||||
eq_false (Nat.not_le_of_gt (Nat.lt_of_lt_of_le h (le_add_left b a)))
|
||||
|
||||
theorem le_add_le (a : Nat) {b c : Nat} (h : a ≤ c) : (a ≤ b + c) = True :=
|
||||
eq_true (Nat.le_trans h (le_add_left c b))
|
||||
|
||||
theorem le_add_ge (a : Nat) {b c : Nat} (h : a ≥ c) : (a ≤ b + c) = (a - c ≤ b) := by
|
||||
have r := add_le_add_ge 0 b h
|
||||
simp only [Nat.zero_add] at r
|
||||
exact r
|
||||
|
||||
end Nat.Simproc
|
||||
@@ -13,36 +13,29 @@ namespace Option
|
||||
deriving instance DecidableEq for Option
|
||||
deriving instance BEq for Option
|
||||
|
||||
/-- Lifts an optional value to any `Alternative`, sending `none` to `failure`. -/
|
||||
def getM [Alternative m] : Option α → m α
|
||||
def toMonad [Monad m] [Alternative m] : Option α → m α
|
||||
| none => failure
|
||||
| some a => pure a
|
||||
|
||||
@[deprecated getM] def toMonad [Monad m] [Alternative m] : Option α → m α :=
|
||||
getM
|
||||
@[inline] def toBool : Option α → Bool
|
||||
| some _ => true
|
||||
| none => false
|
||||
|
||||
/-- Returns `true` on `some x` and `false` on `none`. -/
|
||||
@[inline] def isSome : Option α → Bool
|
||||
| some _ => true
|
||||
| none => false
|
||||
|
||||
@[deprecated isSome, inline] def toBool : Option α → Bool := isSome
|
||||
|
||||
/-- Returns `true` on `none` and `false` on `some x`. -/
|
||||
@[inline] def isNone : Option α → Bool
|
||||
| some _ => false
|
||||
| none => true
|
||||
|
||||
/--
|
||||
`x?.isEqSome y` is equivalent to `x? == some y`, but avoids an allocation.
|
||||
-/
|
||||
@[inline] def isEqSome [BEq α] : Option α → α → Bool
|
||||
| some a, b => a == b
|
||||
| none, _ => false
|
||||
|
||||
@[inline] protected def bind : Option α → (α → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, f => f a
|
||||
| some a, b => b a
|
||||
|
||||
/-- Runs `f` on `o`'s value, if any, and returns its result, or else returns `none`. -/
|
||||
@[inline] protected def bindM [Monad m] (f : α → m (Option β)) (o : Option α) : m (Option β) := do
|
||||
@@ -51,10 +44,6 @@ def getM [Alternative m] : Option α → m α
|
||||
else
|
||||
return none
|
||||
|
||||
/--
|
||||
Runs a monadic function `f` on an optional value.
|
||||
If the optional value is `none` the function is not called.
|
||||
-/
|
||||
@[inline] protected def mapM [Monad m] (f : α → m β) (o : Option α) : m (Option β) := do
|
||||
if let some a := o then
|
||||
return some (← f a)
|
||||
@@ -64,24 +53,18 @@ If the optional value is `none` the function is not called.
|
||||
theorem map_id : (Option.map id : Option α → Option α) = id :=
|
||||
funext (fun o => match o with | none => rfl | some _ => rfl)
|
||||
|
||||
/-- Keeps an optional value only if it satisfies the predicate `p`. -/
|
||||
@[always_inline, inline] protected def filter (p : α → Bool) : Option α → Option α
|
||||
| some a => if p a then some a else none
|
||||
| none => none
|
||||
|
||||
/-- Checks that an optional value satisfies a predicate `p` or is `none`. -/
|
||||
@[always_inline, inline] protected def all (p : α → Bool) : Option α → Bool
|
||||
| some a => p a
|
||||
| none => true
|
||||
|
||||
/-- Checks that an optional value is not `none` and the value satisfies a predicate `p`. -/
|
||||
@[always_inline, inline] protected def any (p : α → Bool) : Option α → Bool
|
||||
| some a => p a
|
||||
| none => false
|
||||
|
||||
/--
|
||||
Implementation of `OrElse`'s `<|>` syntax for `Option`.
|
||||
-/
|
||||
@[always_inline, macro_inline] protected def orElse : Option α → (Unit → Option α) → Option α
|
||||
| some a, _ => some a
|
||||
| none, b => b ()
|
||||
|
||||
@@ -114,18 +114,7 @@ by `cmp₂` to break the tie.
|
||||
@[inline] def compareLex (cmp₁ cmp₂ : α → β → Ordering) (a : α) (b : β) : Ordering :=
|
||||
(cmp₁ a b).then (cmp₂ a b)
|
||||
|
||||
/--
|
||||
`Ord α` provides a computable total order on `α`, in terms of the
|
||||
`compare : α → α → Ordering` function.
|
||||
|
||||
Typically instances will be transitive, reflexive, and antisymmetric,
|
||||
but this is not enforced by the typeclass.
|
||||
|
||||
There is a derive handler, so appending `deriving Ord` to an inductive type or structure
|
||||
will attempt to create an `Ord` instance.
|
||||
-/
|
||||
class Ord (α : Type u) where
|
||||
/-- Compare two elements in `α` using the comparator contained in an `[Ord α]` instance. -/
|
||||
compare : α → α → Ordering
|
||||
|
||||
export Ord (compare)
|
||||
@@ -182,13 +171,15 @@ instance [Ord α] : Ord (Option α) where
|
||||
|
||||
/-- The lexicographic order on pairs. -/
|
||||
def lexOrd [Ord α] [Ord β] : Ord (α × β) where
|
||||
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
|
||||
compare p1 p2 := match compare p1.1 p2.1 with
|
||||
| .eq => compare p1.2 p2.2
|
||||
| o => o
|
||||
|
||||
def ltOfOrd [Ord α] : LT α where
|
||||
lt a b := compare a b = Ordering.lt
|
||||
lt a b := compare a b == Ordering.lt
|
||||
|
||||
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) :=
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
|
||||
inferInstanceAs (DecidableRel (fun a b => compare a b == Ordering.lt))
|
||||
|
||||
def leOfOrd [Ord α] : LE α where
|
||||
le a b := (compare a b).isLE
|
||||
|
||||
@@ -13,24 +13,11 @@ open Sum Subtype Nat
|
||||
|
||||
open Std
|
||||
|
||||
/--
|
||||
A typeclass that specifies the standard way of turning values of some type into `Format`.
|
||||
|
||||
When rendered this `Format` should be as close as possible to something that can be parsed as the
|
||||
input value.
|
||||
-/
|
||||
class Repr (α : Type u) where
|
||||
/--
|
||||
Turn a value of type `α` into `Format` at a given precedence. The precedence value can be used
|
||||
to avoid parentheses if they are not necessary.
|
||||
-/
|
||||
reprPrec : α → Nat → Format
|
||||
|
||||
export Repr (reprPrec)
|
||||
|
||||
/--
|
||||
Turn `a` into `Format` using its `Repr` instance. The precedence level is initially set to 0.
|
||||
-/
|
||||
abbrev repr [Repr α] (a : α) : Format :=
|
||||
reprPrec a 0
|
||||
|
||||
@@ -116,11 +103,6 @@ instance {p : α → Prop} [Repr α] : Repr (Subtype p) where
|
||||
|
||||
namespace Nat
|
||||
|
||||
/-
|
||||
We have pure functions for calculating the decimal representation of a `Nat` (`toDigits`), but also
|
||||
a fast variant that handles small numbers (`USize`) via C code (`lean_string_of_usize`).
|
||||
-/
|
||||
|
||||
def digitChar (n : Nat) : Char :=
|
||||
if n = 0 then '0' else
|
||||
if n = 1 then '1' else
|
||||
@@ -151,20 +133,6 @@ def toDigitsCore (base : Nat) : Nat → Nat → List Char → List Char
|
||||
def toDigits (base : Nat) (n : Nat) : List Char :=
|
||||
toDigitsCore base (n+1) n []
|
||||
|
||||
@[extern "lean_string_of_usize"]
|
||||
protected def _root_.USize.repr (n : @& USize) : String :=
|
||||
(toDigits 10 n.toNat).asString
|
||||
|
||||
/-- We statically allocate and memoize reprs for small natural numbers. -/
|
||||
private def reprArray : Array String := Id.run do
|
||||
List.range 128 |>.map (·.toUSize.repr) |> Array.mk
|
||||
|
||||
private def reprFast (n : Nat) : String :=
|
||||
if h : n < 128 then Nat.reprArray.get ⟨n, h⟩ else
|
||||
if h : n < USize.size then (USize.ofNatCore n h).repr
|
||||
else (toDigits 10 n).asString
|
||||
|
||||
@[implemented_by reprFast]
|
||||
protected def repr (n : Nat) : String :=
|
||||
(toDigits 10 n).asString
|
||||
|
||||
@@ -194,32 +162,6 @@ def toSuperDigits (n : Nat) : List Char :=
|
||||
def toSuperscriptString (n : Nat) : String :=
|
||||
(toSuperDigits n).asString
|
||||
|
||||
def subDigitChar (n : Nat) : Char :=
|
||||
if n = 0 then '₀' else
|
||||
if n = 1 then '₁' else
|
||||
if n = 2 then '₂' else
|
||||
if n = 3 then '₃' else
|
||||
if n = 4 then '₄' else
|
||||
if n = 5 then '₅' else
|
||||
if n = 6 then '₆' else
|
||||
if n = 7 then '₇' else
|
||||
if n = 8 then '₈' else
|
||||
if n = 9 then '₉' else
|
||||
'*'
|
||||
|
||||
partial def toSubDigitsAux : Nat → List Char → List Char
|
||||
| n, ds =>
|
||||
let d := subDigitChar <| n % 10;
|
||||
let n' := n / 10;
|
||||
if n' = 0 then d::ds
|
||||
else toSubDigitsAux n' (d::ds)
|
||||
|
||||
def toSubDigits (n : Nat) : List Char :=
|
||||
toSubDigitsAux n []
|
||||
|
||||
def toSubscriptString (n : Nat) : String :=
|
||||
(toSubDigits n).asString
|
||||
|
||||
end Nat
|
||||
|
||||
instance : Repr Nat where
|
||||
|
||||
@@ -94,8 +94,7 @@ instance : Stream (Subarray α) α where
|
||||
next? s :=
|
||||
if h : s.start < s.stop then
|
||||
have : s.start + 1 ≤ s.stop := Nat.succ_le_of_lt h
|
||||
some (s.as.get ⟨s.start, Nat.lt_of_lt_of_le h s.stop_le_array_size⟩,
|
||||
{ s with start := s.start + 1, start_le_stop := this })
|
||||
some (s.as.get ⟨s.start, Nat.lt_of_lt_of_le h s.h₂⟩, { s with start := s.start + 1, h₁ := this })
|
||||
else
|
||||
none
|
||||
|
||||
|
||||
@@ -44,16 +44,6 @@ def append : String → (@& String) → String
|
||||
def toList (s : String) : List Char :=
|
||||
s.data
|
||||
|
||||
/-- Returns true if `p` is a valid UTF-8 position in the string `s`, meaning that `p ≤ s.endPos`
|
||||
and `p` lies on a UTF-8 character boundary. This has an O(1) implementation in the runtime. -/
|
||||
@[extern "lean_string_is_valid_pos"]
|
||||
def Pos.isValid (s : @&String) (p : @& Pos) : Bool :=
|
||||
go s.data 0
|
||||
where
|
||||
go : List Char → Pos → Bool
|
||||
| [], i => i = p
|
||||
| c::cs, i => if i = p then true else go cs (i + c)
|
||||
|
||||
def utf8GetAux : List Char → Pos → Pos → Char
|
||||
| [], _, _ => default
|
||||
| c::cs, i, p => if i = p then c else utf8GetAux cs (i + c) p
|
||||
@@ -255,21 +245,12 @@ termination_by s.endPos.1 - i.1
|
||||
@[specialize] def split (s : String) (p : Char → Bool) : List String :=
|
||||
splitAux s p 0 0 []
|
||||
|
||||
/--
|
||||
Auxiliary for `splitOn`. Preconditions:
|
||||
* `sep` is not empty
|
||||
* `b <= i` are indexes into `s`
|
||||
* `j` is an index into `sep`, and not at the end
|
||||
|
||||
It represents the state where we have currently parsed some split parts into `r` (in reverse order),
|
||||
`b` is the beginning of the string / the end of the previous match of `sep`, and the first `j` bytes
|
||||
of `sep` match the bytes `i-j .. i` of `s`.
|
||||
-/
|
||||
def splitOnAux (s sep : String) (b : Pos) (i : Pos) (j : Pos) (r : List String) : List String :=
|
||||
if s.atEnd i then
|
||||
if h : s.atEnd i then
|
||||
let r := (s.extract b i)::r
|
||||
r.reverse
|
||||
else
|
||||
have := Nat.sub_lt_sub_left (Nat.gt_of_not_le (mt decide_eq_true h)) (lt_next s _)
|
||||
if s.get i == sep.get j then
|
||||
let i := s.next i
|
||||
let j := sep.next j
|
||||
@@ -278,42 +259,9 @@ def splitOnAux (s sep : String) (b : Pos) (i : Pos) (j : Pos) (r : List String)
|
||||
else
|
||||
splitOnAux s sep b i j r
|
||||
else
|
||||
splitOnAux s sep b (s.next (i - j)) 0 r
|
||||
termination_by (s.endPos.1 - (i - j).1, sep.endPos.1 - j.1)
|
||||
decreasing_by
|
||||
all_goals simp_wf
|
||||
focus
|
||||
rename_i h _ _
|
||||
left; exact Nat.sub_lt_sub_left
|
||||
(Nat.lt_of_le_of_lt (Nat.sub_le ..) (Nat.gt_of_not_le (mt decide_eq_true h)))
|
||||
(Nat.lt_of_le_of_lt (Nat.sub_le ..) (lt_next s _))
|
||||
focus
|
||||
rename_i i₀ j₀ _ eq h'
|
||||
rw [show (s.next i₀ - sep.next j₀).1 = (i₀ - j₀).1 by
|
||||
show (_ + csize _) - (_ + csize _) = _
|
||||
rw [(beq_iff_eq ..).1 eq, Nat.add_sub_add_right]; rfl]
|
||||
right; exact Nat.sub_lt_sub_left
|
||||
(Nat.lt_of_le_of_lt (Nat.le_add_right ..) (Nat.gt_of_not_le (mt decide_eq_true h')))
|
||||
(lt_next sep _)
|
||||
focus
|
||||
rename_i h _
|
||||
left; exact Nat.sub_lt_sub_left
|
||||
(Nat.lt_of_le_of_lt (Nat.sub_le ..) (Nat.gt_of_not_le (mt decide_eq_true h)))
|
||||
(lt_next s _)
|
||||
splitOnAux s sep b (s.next i) 0 r
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
/--
|
||||
Splits a string `s` on occurrences of the separator `sep`. When `sep` is empty, it returns `[s]`;
|
||||
when `sep` occurs in overlapping patterns, the first match is taken. There will always be exactly
|
||||
`n+1` elements in the returned list if there were `n` nonoverlapping matches of `sep` in the string.
|
||||
The default separator is `" "`. The separators are not included in the returned substrings.
|
||||
|
||||
```
|
||||
"here is some text ".splitOn = ["here", "is", "some", "text", ""]
|
||||
"here is some text ".splitOn "some" = ["here is ", " text "]
|
||||
"here is some text ".splitOn "" = ["here is some text "]
|
||||
"ababacabac".splitOn "aba" = ["", "bac", "c"]
|
||||
```
|
||||
-/
|
||||
def splitOn (s : String) (sep : String := " ") : List String :=
|
||||
if sep == "" then [s] else splitOnAux s sep 0 0 0 []
|
||||
|
||||
|
||||
@@ -17,116 +17,20 @@ def toNat! (s : String) : Nat :=
|
||||
else
|
||||
panic! "Nat expected"
|
||||
|
||||
def utf8DecodeChar? (a : ByteArray) (i : Nat) : Option Char := do
|
||||
let c ← a[i]?
|
||||
if c &&& 0x80 == 0 then
|
||||
some ⟨c.toUInt32, .inl (Nat.lt_trans c.1.2 (by decide))⟩
|
||||
else if c &&& 0xe0 == 0xc0 then
|
||||
let c1 ← a[i+1]?
|
||||
guard (c1 &&& 0xc0 == 0x80)
|
||||
let r := ((c &&& 0x1f).toUInt32 <<< 6) ||| (c1 &&& 0x3f).toUInt32
|
||||
guard (0x80 ≤ r)
|
||||
-- TODO: Prove h from the definition of r once we have the necessary lemmas
|
||||
if h : r < 0xd800 then some ⟨r, .inl h⟩ else none
|
||||
else if c &&& 0xf0 == 0xe0 then
|
||||
let c1 ← a[i+1]?
|
||||
let c2 ← a[i+2]?
|
||||
guard (c1 &&& 0xc0 == 0x80 && c2 &&& 0xc0 == 0x80)
|
||||
let r :=
|
||||
((c &&& 0x0f).toUInt32 <<< 12) |||
|
||||
((c1 &&& 0x3f).toUInt32 <<< 6) |||
|
||||
(c2 &&& 0x3f).toUInt32
|
||||
guard (0x800 ≤ r)
|
||||
-- TODO: Prove `r < 0x110000` from the definition of r once we have the necessary lemmas
|
||||
if h : r < 0xd800 ∨ 0xdfff < r ∧ r < 0x110000 then some ⟨r, h⟩ else none
|
||||
else if c &&& 0xf8 == 0xf0 then
|
||||
let c1 ← a[i+1]?
|
||||
let c2 ← a[i+2]?
|
||||
let c3 ← a[i+3]?
|
||||
guard (c1 &&& 0xc0 == 0x80 && c2 &&& 0xc0 == 0x80 && c3 &&& 0xc0 == 0x80)
|
||||
let r :=
|
||||
((c &&& 0x07).toUInt32 <<< 18) |||
|
||||
((c1 &&& 0x3f).toUInt32 <<< 12) |||
|
||||
((c2 &&& 0x3f).toUInt32 <<< 6) |||
|
||||
(c3 &&& 0x3f).toUInt32
|
||||
if h : 0x10000 ≤ r ∧ r < 0x110000 then
|
||||
some ⟨r, .inr ⟨Nat.lt_of_lt_of_le (by decide) h.1, h.2⟩⟩
|
||||
else none
|
||||
else
|
||||
none
|
||||
/--
|
||||
Convert a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`.
|
||||
The result is unspecified if `a` is not properly UTF-8 encoded.
|
||||
-/
|
||||
@[extern "lean_string_from_utf8_unchecked"]
|
||||
opaque fromUTF8Unchecked (a : @& ByteArray) : String
|
||||
|
||||
/-- Returns true if the given byte array consists of valid UTF-8. -/
|
||||
@[extern "lean_string_validate_utf8"]
|
||||
def validateUTF8 (a : @& ByteArray) : Bool :=
|
||||
(loop 0).isSome
|
||||
where
|
||||
loop (i : Nat) : Option Unit := do
|
||||
if i < a.size then
|
||||
let c ← utf8DecodeChar? a i
|
||||
loop (i + csize c)
|
||||
else pure ()
|
||||
termination_by a.size - i
|
||||
decreasing_by exact Nat.sub_lt_sub_left ‹_› (Nat.lt_add_of_pos_right (one_le_csize c))
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`. -/
|
||||
@[extern "lean_string_from_utf8"]
|
||||
def fromUTF8 (a : @& ByteArray) (h : validateUTF8 a) : String :=
|
||||
loop 0 ""
|
||||
where
|
||||
loop (i : Nat) (acc : String) : String :=
|
||||
if i < a.size then
|
||||
let c := (utf8DecodeChar? a i).getD default
|
||||
loop (i + csize c) (acc.push c)
|
||||
else acc
|
||||
termination_by a.size - i
|
||||
decreasing_by exact Nat.sub_lt_sub_left ‹_› (Nat.lt_add_of_pos_right (one_le_csize c))
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
|
||||
or returns `none` if `a` is not properly UTF-8 encoded. -/
|
||||
@[inline] def fromUTF8? (a : ByteArray) : Option String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else none
|
||||
|
||||
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`,
|
||||
or panics if `a` is not properly UTF-8 encoded. -/
|
||||
@[inline] def fromUTF8! (a : ByteArray) : String :=
|
||||
if h : validateUTF8 a then fromUTF8 a h else panic! "invalid UTF-8 string"
|
||||
|
||||
def utf8EncodeChar (c : Char) : List UInt8 :=
|
||||
let v := c.val
|
||||
if v ≤ 0x7f then
|
||||
[v.toUInt8]
|
||||
else if v ≤ 0x7ff then
|
||||
[(v >>> 6).toUInt8 &&& 0x1f ||| 0xc0,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
else if v ≤ 0xffff then
|
||||
[(v >>> 12).toUInt8 &&& 0x0f ||| 0xe0,
|
||||
(v >>> 6).toUInt8 &&& 0x3f ||| 0x80,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
else
|
||||
[(v >>> 18).toUInt8 &&& 0x07 ||| 0xf0,
|
||||
(v >>> 12).toUInt8 &&& 0x3f ||| 0x80,
|
||||
(v >>> 6).toUInt8 &&& 0x3f ||| 0x80,
|
||||
v.toUInt8 &&& 0x3f ||| 0x80]
|
||||
|
||||
@[simp] theorem length_utf8EncodeChar (c : Char) : (utf8EncodeChar c).length = csize c := by
|
||||
simp [csize, utf8EncodeChar, Char.utf8Size]
|
||||
cases Decidable.em (c.val ≤ 0x7f) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0x7ff) <;> simp [*]
|
||||
cases Decidable.em (c.val ≤ 0xffff) <;> simp [*]
|
||||
|
||||
/-- Converts the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
|
||||
/-- Convert the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
|
||||
@[extern "lean_string_to_utf8"]
|
||||
def toUTF8 (a : @& String) : ByteArray :=
|
||||
⟨⟨a.data.bind utf8EncodeChar⟩⟩
|
||||
|
||||
@[simp] theorem size_toUTF8 (s : String) : s.toUTF8.size = s.utf8ByteSize := by
|
||||
simp [toUTF8, ByteArray.size, Array.size, utf8ByteSize, List.bind]
|
||||
induction s.data <;> simp [List.map, List.join, utf8ByteSize.go, Nat.add_comm, *]
|
||||
opaque toUTF8 (a : @& String) : ByteArray
|
||||
|
||||
/-- Accesses a byte in the UTF-8 encoding of the `String`. O(1) -/
|
||||
@[extern "lean_string_get_byte_fast"]
|
||||
def getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8 :=
|
||||
(toUTF8 s).get ⟨n, size_toUTF8 _ ▸ h⟩
|
||||
opaque getUtf8Byte (s : @& String) (n : Nat) (h : n < s.utf8ByteSize) : UInt8
|
||||
|
||||
theorem Iterator.sizeOf_next_lt_of_hasNext (i : String.Iterator) (h : i.hasNext) : sizeOf i.next < sizeOf i := by
|
||||
cases i; rename_i s pos; simp [Iterator.next, Iterator.sizeOf_eq]; simp [Iterator.hasNext] at h
|
||||
|
||||
@@ -103,7 +103,7 @@ def UInt16.shiftLeft (a b : UInt16) : UInt16 := ⟨a.val <<< (modn b 16).val⟩
|
||||
@[extern "lean_uint16_to_uint8"]
|
||||
def UInt16.toUInt8 (a : UInt16) : UInt8 := a.toNat.toUInt8
|
||||
@[extern "lean_uint8_to_uint16"]
|
||||
def UInt8.toUInt16 (a : UInt8) : UInt16 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
def UInt8.toUInt16 (a : UInt8) : UInt16 := a.toNat.toUInt16
|
||||
@[extern "lean_uint16_shift_right"]
|
||||
def UInt16.shiftRight (a b : UInt16) : UInt16 := ⟨a.val >>> (modn b 16).val⟩
|
||||
def UInt16.lt (a b : UInt16) : Prop := a.val < b.val
|
||||
@@ -186,9 +186,9 @@ def UInt32.toUInt8 (a : UInt32) : UInt8 := a.toNat.toUInt8
|
||||
@[extern "lean_uint32_to_uint16"]
|
||||
def UInt32.toUInt16 (a : UInt32) : UInt16 := a.toNat.toUInt16
|
||||
@[extern "lean_uint8_to_uint32"]
|
||||
def UInt8.toUInt32 (a : UInt8) : UInt32 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
def UInt8.toUInt32 (a : UInt8) : UInt32 := a.toNat.toUInt32
|
||||
@[extern "lean_uint16_to_uint32"]
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := a.toNat.toUInt32
|
||||
|
||||
instance UInt32.instOfNat : OfNat UInt32 n := ⟨UInt32.ofNat n⟩
|
||||
instance : Add UInt32 := ⟨UInt32.add⟩
|
||||
@@ -244,11 +244,11 @@ def UInt64.toUInt16 (a : UInt64) : UInt16 := a.toNat.toUInt16
|
||||
@[extern "lean_uint64_to_uint32"]
|
||||
def UInt64.toUInt32 (a : UInt64) : UInt32 := a.toNat.toUInt32
|
||||
@[extern "lean_uint8_to_uint64"]
|
||||
def UInt8.toUInt64 (a : UInt8) : UInt64 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
def UInt8.toUInt64 (a : UInt8) : UInt64 := a.toNat.toUInt64
|
||||
@[extern "lean_uint16_to_uint64"]
|
||||
def UInt16.toUInt64 (a : UInt16) : UInt64 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
def UInt16.toUInt64 (a : UInt16) : UInt64 := a.toNat.toUInt64
|
||||
@[extern "lean_uint32_to_uint64"]
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := ⟨a.val, Nat.lt_trans a.1.2 (by decide)⟩
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := a.toNat.toUInt64
|
||||
|
||||
instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
instance : Add UInt64 := ⟨UInt64.add⟩
|
||||
@@ -321,7 +321,7 @@ def USize.shiftLeft (a b : USize) : USize := ⟨a.val <<< (modn b System.Platfor
|
||||
@[extern "lean_usize_shift_right"]
|
||||
def USize.shiftRight (a b : USize) : USize := ⟨a.val >>> (modn b System.Platform.numBits).val⟩
|
||||
@[extern "lean_uint32_to_usize"]
|
||||
def UInt32.toUSize (a : UInt32) : USize := USize.ofNat32 a.val a.1.2
|
||||
def UInt32.toUSize (a : UInt32) : USize := a.toNat.toUSize
|
||||
@[extern "lean_usize_to_uint32"]
|
||||
def USize.toUInt32 (a : USize) : UInt32 := a.toNat.toUInt32
|
||||
|
||||
|
||||
@@ -68,106 +68,38 @@ namespace Simp
|
||||
|
||||
def defaultMaxSteps := 100000
|
||||
|
||||
/--
|
||||
The configuration for `simp`.
|
||||
Passed to `simp` using, for example, the `simp (config := {contextual := true})` syntax.
|
||||
|
||||
See also `Lean.Meta.Simp.neutralConfig`.
|
||||
-/
|
||||
structure Config where
|
||||
/--
|
||||
The maximum number of subexpressions to visit when performing simplification.
|
||||
The default is 100000.
|
||||
-/
|
||||
maxSteps : Nat := defaultMaxSteps
|
||||
/--
|
||||
When simp discharges side conditions for conditional lemmas, it can recursively apply simplification.
|
||||
The `maxDischargeDepth` (default: 2) is the maximum recursion depth when recursively applying simplification to side conditions.
|
||||
-/
|
||||
maxDischargeDepth : Nat := 2
|
||||
/--
|
||||
When `contextual` is true (default: `false`) and simplification encounters an implication `p → q`
|
||||
it includes `p` as an additional simp lemma when simplifying `q`.
|
||||
-/
|
||||
contextual : Bool := false
|
||||
/--
|
||||
When true (default: `true`) then the simplifier caches the result of simplifying each subexpression, if possible.
|
||||
-/
|
||||
memoize : Bool := true
|
||||
/--
|
||||
When `singlePass` is `true` (default: `false`), the simplifier runs through a single round of simplification,
|
||||
which consists of running pre-methods, recursing using congruence lemmas, and then running post-methods.
|
||||
Otherwise, when it is `false`, it iteratively applies this simplification procedure.
|
||||
-/
|
||||
singlePass : Bool := false
|
||||
/--
|
||||
When `true` (default: `true`), performs zeta reduction of let expressions.
|
||||
That is, `let x := v; e[x]` reduces to `e[v]`.
|
||||
See also `zetaDelta`.
|
||||
-/
|
||||
/-- `let x := v; e[x]` reduces to `e[v]`. -/
|
||||
zeta : Bool := true
|
||||
/--
|
||||
When `true` (default: `true`), performs beta reduction of applications of `fun` expressions.
|
||||
That is, `(fun x => e[x]) v` reduces to `e[v]`.
|
||||
-/
|
||||
beta : Bool := true
|
||||
/--
|
||||
TODO (currently unimplemented). When `true` (default: `true`), performs eta reduction for `fun` expressions.
|
||||
That is, `(fun x => f x)` reduces to `f`.
|
||||
-/
|
||||
eta : Bool := true
|
||||
/--
|
||||
Configures how to determine definitional equality between two structure instances.
|
||||
See documentation for `Lean.Meta.EtaStructMode`.
|
||||
-/
|
||||
etaStruct : EtaStructMode := .all
|
||||
/--
|
||||
When `true` (default: `true`), reduces `match` expressions applied to constructors.
|
||||
-/
|
||||
iota : Bool := true
|
||||
/--
|
||||
When `true` (default: `true`), reduces projections of structure constructors.
|
||||
-/
|
||||
proj : Bool := true
|
||||
/--
|
||||
When `true` (default: `false`), rewrites a proposition `p` to `True` or `False` by inferring
|
||||
a `Decidable p` instance and reducing it.
|
||||
-/
|
||||
decide : Bool := false
|
||||
/-- When `true` (default: `false`), simplifies simple arithmetic expressions. -/
|
||||
arith : Bool := false
|
||||
/--
|
||||
When `true` (default: `false`), unfolds definitions.
|
||||
This can be enabled using the `simp!` syntax.
|
||||
-/
|
||||
autoUnfold : Bool := false
|
||||
/--
|
||||
When `true` (default: `true`) then switches to `dsimp` on dependent arguments
|
||||
if there is no congruence theorem that would allow `simp` to visit them.
|
||||
When `dsimp` is `false`, then the argument is not visited.
|
||||
If `dsimp := true`, then switches to `dsimp` on dependent arguments where there is no congruence theorem that allows
|
||||
`simp` to visit them. If `dsimp := false`, then argument is not visited.
|
||||
-/
|
||||
dsimp : Bool := true
|
||||
/--
|
||||
If `failIfUnchanged` is `true` (default: `true`), then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will fail if they do not make progress.
|
||||
-/
|
||||
/-- If `failIfUnchanged := true`, then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will fail if they do not make progress. -/
|
||||
failIfUnchanged : Bool := true
|
||||
/--
|
||||
If `ground` is `true` (default: `false`), then ground terms are reduced.
|
||||
A term is ground when it does not contain free or meta variables.
|
||||
Reduction is interrupted at a function application `f ...` if `f` is marked to not be unfolded.
|
||||
Ground term reduction applies `@[seval]` lemmas.
|
||||
-/
|
||||
/-- If `ground := true`, then ground terms are reduced. A term is ground when
|
||||
it does not contain free or meta variables. Reduction is interrupted at a function application `f ...`
|
||||
if `f` is marked to not be unfolded. -/
|
||||
ground : Bool := false
|
||||
/--
|
||||
If `unfoldPartialApp` is `true` (default: `false`), then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will unfold even partial applications of `f` when we request `f` to be unfolded.
|
||||
-/
|
||||
/-- If `unfoldPartialApp := true`, then calls to `simp`, `dsimp`, or `simp_all`
|
||||
will unfold even partial applications of `f` when we request `f` to be unfolded. -/
|
||||
unfoldPartialApp : Bool := false
|
||||
/--
|
||||
When `true` (default: `false`), local definitions are unfolded.
|
||||
That is, given a local context containing entry `x : t := e`, the free variable `x` reduces to `e`.
|
||||
-/
|
||||
/-- Given a local context containing entry `x : t := e`, free variable `x` reduces to `e`. -/
|
||||
zetaDelta : Bool := false
|
||||
deriving Inhabited, BEq
|
||||
|
||||
@@ -175,9 +107,6 @@ structure Config where
|
||||
structure ConfigCtx extends Config where
|
||||
contextual := true
|
||||
|
||||
/--
|
||||
A neutral configuration for `simp`, turning off all reductions and other built-in simplifications.
|
||||
-/
|
||||
def neutralConfig : Simp.Config := {
|
||||
zeta := false
|
||||
beta := false
|
||||
|
||||
@@ -552,52 +552,15 @@ except that it doesn't print an empty diagnostic.
|
||||
-/
|
||||
syntax (name := runMeta) "run_meta " doSeq : command
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsFilterSeverity := &"info" <|> &"warning" <|> &"error" <|> &"all"
|
||||
/-- Element that can be part of a `#guard_msgs` specification. -/
|
||||
syntax guardMsgsSpecElt := &"drop"? (&"info" <|> &"warning" <|> &"error" <|> &"all")
|
||||
|
||||
/--
|
||||
A message filter specification for `#guard_msgs`.
|
||||
- `info`, `warning`, `error`: capture messages with the given severity level.
|
||||
- `all`: capture all messages (the default).
|
||||
- `drop info`, `drop warning`, `drop error`: drop messages with the given severity level.
|
||||
- `drop all`: drop every message.
|
||||
These filters are processed in left-to-right order.
|
||||
-/
|
||||
syntax guardMsgsFilter := &"drop"? guardMsgsFilterSeverity
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsWhitespaceArg := &"exact" <|> &"normalized" <|> &"lax"
|
||||
|
||||
/--
|
||||
Whitespace handling for `#guard_msgs`:
|
||||
- `whitespace := exact` requires an exact whitespace match.
|
||||
- `whitespace := normalized` converts all newline characters to a space before matching
|
||||
(the default). This allows breaking long lines.
|
||||
- `whitespace := lax` collapses whitespace to a single space before matching.
|
||||
In all cases, leading and trailing whitespace is trimmed before matching.
|
||||
-/
|
||||
syntax guardMsgsWhitespace := &"whitespace" " := " guardMsgsWhitespaceArg
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsOrderingArg := &"exact" <|> &"sorted"
|
||||
|
||||
/--
|
||||
Message ordering for `#guard_msgs`:
|
||||
- `ordering := exact` uses the exact ordering of the messages (the default).
|
||||
- `ordering := sorted` sorts the messages in lexicographic order.
|
||||
This helps with testing commands that are non-deterministic in their ordering.
|
||||
-/
|
||||
syntax guardMsgsOrdering := &"ordering" " := " guardMsgsOrderingArg
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
syntax guardMsgsSpecElt := guardMsgsFilter <|> guardMsgsWhitespace <|> guardMsgsOrdering
|
||||
|
||||
set_option linter.missingDocs false in
|
||||
/-- Specification for `#guard_msgs` command. -/
|
||||
syntax guardMsgsSpec := "(" guardMsgsSpecElt,* ")"
|
||||
|
||||
/--
|
||||
`/-- ... -/ #guard_msgs in cmd` captures the messages generated by the command `cmd`
|
||||
and checks that they match the contents of the docstring.
|
||||
`#guard_msgs` captures the messages generated by another command and checks that they
|
||||
match the contents of the docstring attached to the `#guard_msgs` command.
|
||||
|
||||
Basic example:
|
||||
```lean
|
||||
@@ -607,10 +570,10 @@ error: unknown identifier 'x'
|
||||
#guard_msgs in
|
||||
example : α := x
|
||||
```
|
||||
This checks that there is such an error and then consumes the message.
|
||||
This checks that there is such an error and then consumes the message entirely.
|
||||
|
||||
By default, the command captures all messages, but the filter condition can be adjusted.
|
||||
For example, we can select only warnings:
|
||||
By default, the command intercepts all messages, but there is a way to specify which types
|
||||
of messages to consider. For example, we can select only warnings:
|
||||
```lean
|
||||
/--
|
||||
warning: declaration uses 'sorry'
|
||||
@@ -623,37 +586,29 @@ or only errors
|
||||
#guard_msgs(error) in
|
||||
example : α := sorry
|
||||
```
|
||||
In the previous example, since warnings are not captured there is a warning on `sorry`.
|
||||
In this last example, since the message is not intercepted there is a warning on `sorry`.
|
||||
We can drop the warning completely with
|
||||
```lean
|
||||
#guard_msgs(error, drop warning) in
|
||||
example : α := sorry
|
||||
```
|
||||
|
||||
In general, `#guard_msgs` accepts a comma-separated list of configuration clauses in parentheses:
|
||||
Syntax description:
|
||||
```
|
||||
#guard_msgs (configElt,*) in cmd
|
||||
#guard_msgs (drop? info|warning|error|all,*)? in cmd
|
||||
```
|
||||
By default, the configuration list is `(all, whitespace := normalized, ordering := exact)`.
|
||||
|
||||
Message filters (processed in left-to-right order):
|
||||
- `info`, `warning`, `error`: capture messages with the given severity level.
|
||||
- `all`: capture all messages (the default).
|
||||
- `drop info`, `drop warning`, `drop error`: drop messages with the given severity level.
|
||||
- `drop all`: drop every message.
|
||||
If there is no specification, `#guard_msgs` intercepts all messages.
|
||||
Otherwise, if there is one, the specification is considered in left-to-right order, and the first
|
||||
that applies chooses the outcome of the message:
|
||||
- `info`, `warning`, `error`: intercept a message with the given severity level.
|
||||
- `all`: intercept any message (so `#guard_msgs in cmd` and `#guard_msgs (all) in cmd`
|
||||
are equivalent).
|
||||
- `drop info`, `drop warning`, `drop error`: intercept a message with the given severity
|
||||
level and then drop it. These messages are not checked.
|
||||
- `drop all`: intercept a message and drop it.
|
||||
|
||||
Whitespace handling (after trimming leading and trailing whitespace):
|
||||
- `whitespace := exact` requires an exact whitespace match.
|
||||
- `whitespace := normalized` converts all newline characters to a space before matching
|
||||
(the default). This allows breaking long lines.
|
||||
- `whitespace := lax` collapses whitespace to a single space before matching.
|
||||
|
||||
Message ordering:
|
||||
- `ordering := exact` uses the exact ordering of the messages (the default).
|
||||
- `ordering := sorted` sorts the messages in lexicographic order.
|
||||
This helps with testing commands that are non-deterministic in their ordering.
|
||||
|
||||
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and drop
|
||||
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and then drop
|
||||
everything else.
|
||||
-/
|
||||
syntax (name := guardMsgsCmd)
|
||||
|
||||
@@ -477,8 +477,6 @@ and `Prod.snd p` respectively. You can also write `p.fst` and `p.snd`.
|
||||
For more information: [Constructors with Arguments](https://lean-lang.org/theorem_proving_in_lean4/inductive_types.html?highlight=Prod#constructors-with-arguments)
|
||||
-/
|
||||
structure Prod (α : Type u) (β : Type v) where
|
||||
/-- Constructs a pair from two terms. -/
|
||||
mk ::
|
||||
/-- The first projection out of a pair. if `p : α × β` then `p.1 : α`. -/
|
||||
fst : α
|
||||
/-- The second projection out of a pair. if `p : α × β` then `p.2 : β`. -/
|
||||
@@ -1098,7 +1096,7 @@ class OfNat (α : Type u) (_ : Nat) where
|
||||
ofNat : α
|
||||
|
||||
@[default_instance 100] /- low prio -/
|
||||
instance instOfNatNat (n : Nat) : OfNat Nat n where
|
||||
instance (n : Nat) : OfNat Nat n where
|
||||
ofNat := n
|
||||
|
||||
/-- `LE α` is the typeclass which supports the notation `x ≤ y` where `x y : α`.-/
|
||||
@@ -1432,31 +1430,31 @@ class ShiftRight (α : Type u) where
|
||||
shiftRight : α → α → α
|
||||
|
||||
@[default_instance]
|
||||
instance instHAdd [Add α] : HAdd α α α where
|
||||
instance [Add α] : HAdd α α α where
|
||||
hAdd a b := Add.add a b
|
||||
|
||||
@[default_instance]
|
||||
instance instHSub [Sub α] : HSub α α α where
|
||||
instance [Sub α] : HSub α α α where
|
||||
hSub a b := Sub.sub a b
|
||||
|
||||
@[default_instance]
|
||||
instance instHMul [Mul α] : HMul α α α where
|
||||
instance [Mul α] : HMul α α α where
|
||||
hMul a b := Mul.mul a b
|
||||
|
||||
@[default_instance]
|
||||
instance instHDiv [Div α] : HDiv α α α where
|
||||
instance [Div α] : HDiv α α α where
|
||||
hDiv a b := Div.div a b
|
||||
|
||||
@[default_instance]
|
||||
instance instHMod [Mod α] : HMod α α α where
|
||||
instance [Mod α] : HMod α α α where
|
||||
hMod a b := Mod.mod a b
|
||||
|
||||
@[default_instance]
|
||||
instance instHPow [Pow α β] : HPow α β α where
|
||||
instance [Pow α β] : HPow α β α where
|
||||
hPow a b := Pow.pow a b
|
||||
|
||||
@[default_instance]
|
||||
instance instPowNat [NatPow α] : Pow α Nat where
|
||||
instance [NatPow α] : Pow α Nat where
|
||||
pow a n := NatPow.pow a n
|
||||
|
||||
@[default_instance]
|
||||
@@ -1523,7 +1521,7 @@ protected def Nat.add : (@& Nat) → (@& Nat) → Nat
|
||||
| a, Nat.zero => a
|
||||
| a, Nat.succ b => Nat.succ (Nat.add a b)
|
||||
|
||||
instance instAddNat : Add Nat where
|
||||
instance : Add Nat where
|
||||
add := Nat.add
|
||||
|
||||
/- We mark the following definitions as pattern to make sure they can be used in recursive equations,
|
||||
@@ -1543,7 +1541,7 @@ protected def Nat.mul : (@& Nat) → (@& Nat) → Nat
|
||||
| _, 0 => 0
|
||||
| a, Nat.succ b => Nat.add (Nat.mul a b) a
|
||||
|
||||
instance instMulNat : Mul Nat where
|
||||
instance : Mul Nat where
|
||||
mul := Nat.mul
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
@@ -1559,7 +1557,7 @@ protected def Nat.pow (m : @& Nat) : (@& Nat) → Nat
|
||||
| 0 => 1
|
||||
| succ n => Nat.mul (Nat.pow m n) m
|
||||
|
||||
instance instNatPowNat : NatPow Nat := ⟨Nat.pow⟩
|
||||
instance : NatPow Nat := ⟨Nat.pow⟩
|
||||
|
||||
set_option bootstrap.genMatcherCode false in
|
||||
/--
|
||||
@@ -1636,14 +1634,14 @@ protected inductive Nat.le (n : Nat) : Nat → Prop
|
||||
/-- If `n ≤ m`, then `n ≤ m + 1`. -/
|
||||
| step {m} : Nat.le n m → Nat.le n (succ m)
|
||||
|
||||
instance instLENat : LE Nat where
|
||||
instance : LE Nat where
|
||||
le := Nat.le
|
||||
|
||||
/-- The strict less than relation on natural numbers is defined as `n < m := n + 1 ≤ m`. -/
|
||||
protected def Nat.lt (n m : Nat) : Prop :=
|
||||
Nat.le (succ n) m
|
||||
|
||||
instance instLTNat : LT Nat where
|
||||
instance : LT Nat where
|
||||
lt := Nat.lt
|
||||
|
||||
theorem Nat.not_succ_le_zero : ∀ (n : Nat), LE.le (succ n) 0 → False
|
||||
@@ -1795,7 +1793,7 @@ protected def Nat.sub : (@& Nat) → (@& Nat) → Nat
|
||||
| a, 0 => a
|
||||
| a, succ b => pred (Nat.sub a b)
|
||||
|
||||
instance instSubNat : Sub Nat where
|
||||
instance : Sub Nat where
|
||||
sub := Nat.sub
|
||||
|
||||
/--
|
||||
@@ -1822,8 +1820,6 @@ It is the "canonical type with `n` elements".
|
||||
-/
|
||||
@[pp_using_anonymous_constructor]
|
||||
structure Fin (n : Nat) where
|
||||
/-- Creates a `Fin n` from `i : Nat` and a proof that `i < n`. -/
|
||||
mk ::
|
||||
/-- If `i : Fin n`, then `i.val : ℕ` is the described number. It can also be
|
||||
written as `i.1` or just `i` when the target type is known. -/
|
||||
val : Nat
|
||||
@@ -3361,7 +3357,7 @@ protected def seqRight (x : EStateM ε σ α) (y : Unit → EStateM ε σ β) :
|
||||
| Result.error e s => Result.error e s
|
||||
|
||||
@[always_inline]
|
||||
instance instMonad : Monad (EStateM ε σ) where
|
||||
instance : Monad (EStateM ε σ) where
|
||||
bind := EStateM.bind
|
||||
pure := EStateM.pure
|
||||
map := EStateM.map
|
||||
|
||||
@@ -311,8 +311,6 @@ Note that EOF does not actually close a stream, so further reads may block and r
|
||||
-/
|
||||
getLine : IO String
|
||||
putStr : String → IO Unit
|
||||
/-- Returns true if a stream refers to a Windows console or Unix terminal. -/
|
||||
isTty : BaseIO Bool
|
||||
deriving Inhabited
|
||||
|
||||
open FS
|
||||
@@ -362,9 +360,6 @@ Will succeed even if no lock has been acquired.
|
||||
-/
|
||||
@[extern "lean_io_prim_handle_unlock"] opaque unlock (h : @& Handle) : IO Unit
|
||||
|
||||
/-- Returns true if a handle refers to a Windows console or Unix terminal. -/
|
||||
@[extern "lean_io_prim_handle_is_tty"] opaque isTty (h : @& Handle) : BaseIO Bool
|
||||
|
||||
@[extern "lean_io_prim_handle_flush"] opaque flush (h : @& Handle) : IO Unit
|
||||
/-- Rewinds the read/write cursor to the beginning of the handle. -/
|
||||
@[extern "lean_io_prim_handle_rewind"] opaque rewind (h : @& Handle) : IO Unit
|
||||
@@ -748,41 +743,36 @@ namespace FS
|
||||
namespace Stream
|
||||
|
||||
@[export lean_stream_of_handle]
|
||||
def ofHandle (h : Handle) : Stream where
|
||||
flush := Handle.flush h
|
||||
read := Handle.read h
|
||||
write := Handle.write h
|
||||
getLine := Handle.getLine h
|
||||
putStr := Handle.putStr h
|
||||
isTty := Handle.isTty h
|
||||
def ofHandle (h : Handle) : Stream := {
|
||||
flush := Handle.flush h,
|
||||
read := Handle.read h,
|
||||
write := Handle.write h,
|
||||
getLine := Handle.getLine h,
|
||||
putStr := Handle.putStr h,
|
||||
}
|
||||
|
||||
structure Buffer where
|
||||
data : ByteArray := ByteArray.empty
|
||||
pos : Nat := 0
|
||||
|
||||
def ofBuffer (r : Ref Buffer) : Stream where
|
||||
flush := pure ()
|
||||
def ofBuffer (r : Ref Buffer) : Stream := {
|
||||
flush := pure (),
|
||||
read := fun n => r.modifyGet fun b =>
|
||||
let data := b.data.extract b.pos (b.pos + n.toNat)
|
||||
(data, { b with pos := b.pos + data.size })
|
||||
(data, { b with pos := b.pos + data.size }),
|
||||
write := fun data => r.modify fun b =>
|
||||
-- set `exact` to `false` so that repeatedly writing to the stream does not impose quadratic run time
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size }
|
||||
getLine := do
|
||||
let buf ← r.modifyGet fun b =>
|
||||
let pos := match b.data.findIdx? (start := b.pos) fun u => u == 0 || u = '\n'.toNat.toUInt8 with
|
||||
-- include '\n', but not '\0'
|
||||
| some pos => if b.data.get! pos == 0 then pos else pos + 1
|
||||
| none => b.data.size
|
||||
(b.data.extract b.pos pos, { b with pos := pos })
|
||||
match String.fromUTF8? buf with
|
||||
| some str => pure str
|
||||
| none => throw (.userError "invalid UTF-8")
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size },
|
||||
getLine := r.modifyGet fun b =>
|
||||
let pos := match b.data.findIdx? (start := b.pos) fun u => u == 0 || u = '\n'.toNat.toUInt8 with
|
||||
-- include '\n', but not '\0'
|
||||
| some pos => if b.data.get! pos == 0 then pos else pos + 1
|
||||
| none => b.data.size
|
||||
(String.fromUTF8Unchecked <| b.data.extract b.pos pos, { b with pos := pos }),
|
||||
putStr := fun s => r.modify fun b =>
|
||||
let data := s.toUTF8
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size }
|
||||
isTty := pure false
|
||||
|
||||
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size },
|
||||
}
|
||||
end Stream
|
||||
|
||||
/-- Run action with `stdin` emptied and `stdout+stderr` captured into a `String`. -/
|
||||
@@ -795,7 +785,7 @@ def withIsolatedStreams [Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (x : m
|
||||
(if isolateStderr then withStderr (Stream.ofBuffer bOut) else id) <|
|
||||
x
|
||||
let bOut ← liftM (m := BaseIO) bOut.get
|
||||
let out := String.fromUTF8! bOut.data
|
||||
let out := String.fromUTF8Unchecked bOut.data
|
||||
pure (out, r)
|
||||
|
||||
end FS
|
||||
@@ -812,7 +802,7 @@ class Eval (α : Type u) where
|
||||
-- We take `Unit → α` instead of `α` because ‵α` may contain effectful debugging primitives (e.g., `dbg_trace`)
|
||||
eval : (Unit → α) → (hideUnit : Bool := true) → IO Unit
|
||||
|
||||
instance instEval [ToString α] : Eval α where
|
||||
instance [ToString α] : Eval α where
|
||||
eval a _ := IO.println (toString (a ()))
|
||||
|
||||
instance [Repr α] : Eval α where
|
||||
|
||||
@@ -50,7 +50,7 @@ def decodeUri (uri : String) : String := Id.run do
|
||||
((decoded.push c).push h1, i + 2)
|
||||
else
|
||||
(decoded.push c, i + 1)
|
||||
return String.fromUTF8! decoded
|
||||
return String.fromUTF8Unchecked decoded
|
||||
where hexDigitToUInt8? (c : UInt8) : Option UInt8 :=
|
||||
if zero ≤ c ∧ c ≤ nine then some (c - zero)
|
||||
else if lettera ≤ c ∧ c ≤ letterf then some (c - lettera + 10)
|
||||
|
||||
@@ -1125,14 +1125,11 @@ normalizes `h` with `norm_cast` and tries to use that to close the goal. -/
|
||||
macro "assumption_mod_cast" : tactic => `(tactic| norm_cast0 at * <;> assumption)
|
||||
|
||||
/--
|
||||
The `norm_cast` family of tactics is used to normalize certain coercions (*casts*) in expressions.
|
||||
- `norm_cast` normalizes casts in the target.
|
||||
- `norm_cast at h` normalizes casts in hypothesis `h`.
|
||||
|
||||
The tactic is basically a version of `simp` with a specific set of lemmas to move casts
|
||||
The `norm_cast` family of tactics is used to normalize casts inside expressions.
|
||||
It is basically a `simp` tactic with a specific set of lemmas to move casts
|
||||
upwards in the expression.
|
||||
Therefore even in situations where non-terminal `simp` calls are discouraged (because of fragility),
|
||||
`norm_cast` is considered to be safe.
|
||||
`norm_cast` is considered safe.
|
||||
It also has special handling of numerals.
|
||||
|
||||
For instance, given an assumption
|
||||
@@ -1140,22 +1137,22 @@ For instance, given an assumption
|
||||
a b : ℤ
|
||||
h : ↑a + ↑b < (10 : ℚ)
|
||||
```
|
||||
|
||||
writing `norm_cast at h` will turn `h` into
|
||||
```lean
|
||||
h : a + b < 10
|
||||
```
|
||||
|
||||
There are also variants of basic tactics that use `norm_cast` to normalize expressions during
|
||||
their operation, to make them more flexible about the expressions they accept
|
||||
(we say that it is a tactic *modulo* the effects of `norm_cast`):
|
||||
- `exact_mod_cast` for `exact` and `apply_mod_cast` for `apply`.
|
||||
Writing `exact_mod_cast h` and `apply_mod_cast h` will normalize casts
|
||||
in the goal and `h` before using `exact h` or `apply h`.
|
||||
- `rw_mod_cast` for `rw`. It applies `norm_cast` between rewrites.
|
||||
- `assumption_mod_cast` for `assumption`.
|
||||
This is effectively `norm_cast at *; assumption`, but more efficient.
|
||||
It normalizes casts in the goal and, for every hypothesis `h` in the context,
|
||||
it will try to normalize casts in `h` and use `exact h`.
|
||||
There are also variants of `exact`, `apply`, `rw`, and `assumption` that
|
||||
work modulo `norm_cast` - in other words, they apply `norm_cast` to make
|
||||
them more flexible. They are called `exact_mod_cast`, `apply_mod_cast`,
|
||||
`rw_mod_cast`, and `assumption_mod_cast`, respectively.
|
||||
Writing `exact_mod_cast h` and `apply_mod_cast h` will normalize casts
|
||||
in the goal and `h` before using `exact h` or `apply h`.
|
||||
Writing `assumption_mod_cast` will normalize casts in the goal and, for
|
||||
every hypothesis `h` in the context, it will try to normalize casts in `h` and use
|
||||
`exact h`.
|
||||
`rw_mod_cast` acts like the `rw` tactic but it applies `norm_cast` between steps.
|
||||
|
||||
See also `push_cast`, which moves casts inwards rather than lifting them outwards.
|
||||
-/
|
||||
@@ -1163,37 +1160,22 @@ macro "norm_cast" loc:(location)? : tactic =>
|
||||
`(tactic| norm_cast0 $[$loc]? <;> try trivial)
|
||||
|
||||
/--
|
||||
`push_cast` rewrites the goal to move certain coercions (*casts*) inward, toward the leaf nodes.
|
||||
`push_cast` rewrites the goal to move casts inward, toward the leaf nodes.
|
||||
This uses `norm_cast` lemmas in the forward direction.
|
||||
For example, `↑(a + b)` will be written to `↑a + ↑b`.
|
||||
- `push_cast` moves casts inward in the goal.
|
||||
- `push_cast at h` moves casts inward in the hypothesis `h`.
|
||||
It can be used with extra simp lemmas with, for example, `push_cast [Int.add_zero]`.
|
||||
It is equivalent to `simp only with push_cast`.
|
||||
It can also be used at hypotheses with `push_cast at h`
|
||||
and with extra simp lemmas with `push_cast [int.add_zero]`.
|
||||
|
||||
Example:
|
||||
```lean
|
||||
example (a b : Nat)
|
||||
(h1 : ((a + b : Nat) : Int) = 10)
|
||||
(h2 : ((a + b + 0 : Nat) : Int) = 10) :
|
||||
((a + b : Nat) : Int) = 10 := by
|
||||
/-
|
||||
h1 : ↑(a + b) = 10
|
||||
h2 : ↑(a + b + 0) = 10
|
||||
⊢ ↑(a + b) = 10
|
||||
-/
|
||||
push_cast
|
||||
/- Now
|
||||
⊢ ↑a + ↑b = 10
|
||||
-/
|
||||
push_cast at h1
|
||||
push_cast [Int.add_zero] at h2
|
||||
/- Now
|
||||
h1 h2 : ↑a + ↑b = 10
|
||||
-/
|
||||
exact h1
|
||||
example (a b : ℕ) (h1 : ((a + b : ℕ) : ℤ) = 10) (h2 : ((a + b + 0 : ℕ) : ℤ) = 10) :
|
||||
((a + b : ℕ) : ℤ) = 10 :=
|
||||
begin
|
||||
push_cast,
|
||||
push_cast at h1,
|
||||
push_cast [int.add_zero] at h2,
|
||||
end
|
||||
```
|
||||
|
||||
See also `norm_cast`.
|
||||
-/
|
||||
syntax (name := pushCast) "push_cast" (config)? (discharger)? (&" only")?
|
||||
(" [" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
|
||||
@@ -9,18 +9,7 @@ import Init.Data.Nat.Basic
|
||||
|
||||
universe u v
|
||||
|
||||
/--
|
||||
`Acc` is the accessibility predicate. Given some relation `r` (e.g. `<`) and a value `x`,
|
||||
`Acc r x` means that `x` is accessible through `r`:
|
||||
|
||||
`x` is accessible if there exists no infinite sequence `... < y₂ < y₁ < y₀ < x`.
|
||||
-/
|
||||
inductive Acc {α : Sort u} (r : α → α → Prop) : α → Prop where
|
||||
/--
|
||||
A value is accessible if for all `y` such that `r y x`, `y` is also accessible.
|
||||
Note that if there exists no `y` such that `r y x`, then `x` is accessible. Such an `x` is called a
|
||||
_base case_.
|
||||
-/
|
||||
| intro (x : α) (h : (y : α) → r y x → Acc r y) : Acc r x
|
||||
|
||||
noncomputable abbrev Acc.ndrec.{u1, u2} {α : Sort u2} {r : α → α → Prop} {C : α → Sort u1}
|
||||
@@ -42,14 +31,6 @@ def inv {x y : α} (h₁ : Acc r x) (h₂ : r y x) : Acc r y :=
|
||||
|
||||
end Acc
|
||||
|
||||
/--
|
||||
A relation `r` is `WellFounded` if all elements of `α` are accessible within `r`.
|
||||
If a relation is `WellFounded`, it does not allow for an infinite descent along the relation.
|
||||
|
||||
If the arguments of the recursive calls in a function definition decrease according to
|
||||
a well founded relation, then the function terminates.
|
||||
Well-founded relations are sometimes called _Artinian_ or said to satisfy the “descending chain condition”.
|
||||
-/
|
||||
inductive WellFounded {α : Sort u} (r : α → α → Prop) : Prop where
|
||||
| intro (h : ∀ a, Acc r a) : WellFounded r
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 Mario Carneiro. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Lean.Compiler.InitAttr
|
||||
import Lean.DocString
|
||||
|
||||
namespace Lean
|
||||
|
||||
def declareBuiltinDocStringAndRanges (declName : Name) : AttrM Unit := do
|
||||
if let some doc ← findDocString? (← getEnv) declName (includeBuiltin := false) then
|
||||
declareBuiltin (declName ++ `docString) (mkAppN (mkConst ``addBuiltinDocString) #[toExpr declName, toExpr doc])
|
||||
if let some declRanges ← findDeclarationRanges? declName then
|
||||
declareBuiltin (declName ++ `declRange) (mkAppN (mkConst ``addBuiltinDeclarationRanges) #[toExpr declName, toExpr declRanges])
|
||||
|
||||
builtin_initialize
|
||||
registerBuiltinAttribute {
|
||||
name := `builtin_doc
|
||||
descr := "make the docs and location of this declaration available as a builtin"
|
||||
add := fun decl stx _ => do
|
||||
Attribute.Builtin.ensureNoArgs stx
|
||||
declareBuiltinDocStringAndRanges decl
|
||||
}
|
||||
|
||||
end Lean
|
||||
@@ -12,8 +12,7 @@ Run the code generation pipeline for all declarations in `declNames`
|
||||
that fulfill the requirements of `shouldGenerateCode`.
|
||||
-/
|
||||
def compile (declNames : Array Name) : CoreM Unit := do profileitM Exception "compiler new" (← getOptions) do
|
||||
withTraceNode `Compiler (fun _ => return m!"compiling: {declNames}") do
|
||||
discard <| LCNF.compile declNames
|
||||
discard <| LCNF.compile declNames
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Compiler
|
||||
|
||||
@@ -177,13 +177,6 @@ instance : MonadTrace CoreM where
|
||||
def restore (b : State) : CoreM Unit :=
|
||||
modify fun s => { s with env := b.env, messages := b.messages, infoState := b.infoState }
|
||||
|
||||
/--
|
||||
Restores full state including sources for unique identifiers. Only intended for incremental reuse
|
||||
between elaboration runs, not for backtracking within a single run.
|
||||
-/
|
||||
def restoreFull (b : State) : CoreM Unit :=
|
||||
set b
|
||||
|
||||
private def mkFreshNameImp (n : Name) : CoreM Name := do
|
||||
let fresh ← modifyGet fun s => (s.nextMacroScope, { s with nextMacroScope := s.nextMacroScope + 1 })
|
||||
return addMacroScope (← getEnv).mainModule n fresh
|
||||
@@ -252,13 +245,6 @@ def resetMessageLog : CoreM Unit :=
|
||||
def getMessageLog : CoreM MessageLog :=
|
||||
return (← get).messages
|
||||
|
||||
/--
|
||||
Returns the current log and then resets its messages but does NOT reset `MessageLog.hadErrors`. Used
|
||||
for incremental reporting during elaboration of a single command.
|
||||
-/
|
||||
def getAndEmptyMessageLog : CoreM MessageLog :=
|
||||
modifyGet fun log => ({ log with msgs := {} }, log)
|
||||
|
||||
instance : MonadLog CoreM where
|
||||
getRef := getRef
|
||||
getFileMap := return (← read).fileMap
|
||||
@@ -344,13 +330,10 @@ opaque compileDeclsNew (declNames : List Name) : CoreM Unit
|
||||
|
||||
def compileDecl (decl : Declaration) : CoreM Unit := do
|
||||
let opts ← getOptions
|
||||
let decls := Compiler.getDeclNamesForCodeGen decl
|
||||
if compiler.enableNew.get opts then
|
||||
compileDeclsNew decls
|
||||
let res ← withTraceNode `compiler (fun _ => return m!"compiling old: {decls}") do
|
||||
return (← getEnv).compileDecl opts decl
|
||||
match res with
|
||||
| Except.ok env => setEnv env
|
||||
compileDeclsNew (Compiler.getDeclNamesForCodeGen decl)
|
||||
match (← getEnv).compileDecl opts decl with
|
||||
| Except.ok env => setEnv env
|
||||
| Except.error (KernelException.other msg) =>
|
||||
checkUnsupported decl -- Generate nicer error message for unsupported recursors and axioms
|
||||
throwError msg
|
||||
|
||||
@@ -122,7 +122,7 @@ def expand [Hashable α] (size : Nat) (buckets : HashMapBucket α β) : HashMapI
|
||||
let ⟨i, h⟩ := mkIdx (hash a) buckets.property
|
||||
let bkt := buckets.val[i]
|
||||
if let some b := bkt.find? a then
|
||||
(⟨size, buckets⟩, some b)
|
||||
(m, some b)
|
||||
else
|
||||
let size' := size + 1
|
||||
let buckets' := buckets.update i (AssocList.cons a b bkt) h
|
||||
@@ -137,10 +137,8 @@ def erase [BEq α] [Hashable α] (m : HashMapImp α β) (a : α) : HashMapImp α
|
||||
| ⟨ size, buckets ⟩ =>
|
||||
let ⟨i, h⟩ := mkIdx (hash a) buckets.property
|
||||
let bkt := buckets.val[i]
|
||||
if bkt.contains a then
|
||||
⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else
|
||||
⟨size, buckets⟩
|
||||
if bkt.contains a then ⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else m
|
||||
|
||||
inductive WellFormed [BEq α] [Hashable α] : HashMapImp α β → Prop where
|
||||
| mkWff : ∀ n, WellFormed (mkHashMapImp n)
|
||||
|
||||
@@ -112,10 +112,8 @@ def erase [BEq α] [Hashable α] (m : HashSetImp α) (a : α) : HashSetImp α :=
|
||||
| ⟨ size, buckets ⟩ =>
|
||||
let ⟨i, h⟩ := mkIdx (hash a) buckets.property
|
||||
let bkt := buckets.val[i]
|
||||
if bkt.contains a then
|
||||
⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else
|
||||
⟨size, buckets⟩
|
||||
if bkt.contains a then ⟨size - 1, buckets.update i (bkt.erase a) h⟩
|
||||
else m
|
||||
|
||||
inductive WellFormed [BEq α] [Hashable α] : HashSetImp α → Prop where
|
||||
| mkWff : ∀ n, WellFormed (mkHashSetImp n)
|
||||
|
||||
@@ -18,7 +18,7 @@ open IO
|
||||
/-- Consumes `nBytes` bytes from the stream, interprets the bytes as a utf-8 string and the string as a valid JSON object. -/
|
||||
def readJson (h : FS.Stream) (nBytes : Nat) : IO Json := do
|
||||
let bytes ← h.read (USize.ofNat nBytes)
|
||||
let some s := String.fromUTF8? bytes | throw (IO.userError "invalid UTF-8")
|
||||
let s := String.fromUTF8Unchecked bytes
|
||||
ofExcept (Json.parse s)
|
||||
|
||||
def writeJson (h : FS.Stream) (j : Json) : IO Unit := do
|
||||
|
||||
@@ -324,7 +324,7 @@ inductive SemanticTokenType where
|
||||
| decorator
|
||||
-- Extensions
|
||||
| leanSorryLike
|
||||
deriving ToJson, FromJson, BEq, Hashable
|
||||
deriving ToJson, FromJson
|
||||
|
||||
-- must be in the same order as the constructors
|
||||
def SemanticTokenType.names : Array String :=
|
||||
|
||||
@@ -43,19 +43,11 @@ def fail (msg : String) : Parsec α := fun it =>
|
||||
error it msg
|
||||
|
||||
@[inline]
|
||||
def tryCatch (p : Parsec α)
|
||||
(csuccess : α → Parsec β)
|
||||
(cerror : Unit → Parsec β)
|
||||
: Parsec β := fun it =>
|
||||
def orElse (p : Parsec α) (q : Unit → Parsec α) : Parsec α := fun it =>
|
||||
match p it with
|
||||
| .success rem a => csuccess a rem
|
||||
| .error rem err =>
|
||||
-- We assume that it.s never changes as the `Parsec` monad only modifies `it.pos`.
|
||||
if it.pos = rem.pos then cerror () rem else .error rem err
|
||||
|
||||
@[inline]
|
||||
def orElse (p : Parsec α) (q : Unit → Parsec α) : Parsec α :=
|
||||
tryCatch p pure q
|
||||
| success rem a => success rem a
|
||||
| error rem err =>
|
||||
if it = rem then q () it else error rem err
|
||||
|
||||
@[inline]
|
||||
def attempt (p : Parsec α) : Parsec α := λ it =>
|
||||
@@ -82,7 +74,8 @@ def eof : Parsec Unit := fun it =>
|
||||
|
||||
@[specialize]
|
||||
partial def manyCore (p : Parsec α) (acc : Array α) : Parsec $ Array α :=
|
||||
tryCatch p (manyCore p <| acc.push ·) (fun _ => pure acc)
|
||||
(do manyCore p (acc.push $ ←p))
|
||||
<|> pure acc
|
||||
|
||||
@[inline]
|
||||
def many (p : Parsec α) : Parsec $ Array α := manyCore p #[]
|
||||
@@ -92,7 +85,8 @@ def many1 (p : Parsec α) : Parsec $ Array α := do manyCore p #[←p]
|
||||
|
||||
@[specialize]
|
||||
partial def manyCharsCore (p : Parsec Char) (acc : String) : Parsec String :=
|
||||
tryCatch p (manyCharsCore p <| acc.push ·) (fun _ => pure acc)
|
||||
(do manyCharsCore p (acc.push $ ←p))
|
||||
<|> pure acc
|
||||
|
||||
@[inline]
|
||||
def manyChars (p : Parsec Char) : Parsec String := manyCharsCore p ""
|
||||
|
||||
@@ -325,9 +325,6 @@ def map {α : Type u} {β : Type v} {σ : Type u} {_ : BEq α} {_ : Hashable α}
|
||||
def toList {_ : BEq α} {_ : Hashable α} (m : PersistentHashMap α β) : List (α × β) :=
|
||||
m.foldl (init := []) fun ps k v => (k, v) :: ps
|
||||
|
||||
def toArray {_ : BEq α} {_ : Hashable α} (m : PersistentHashMap α β) : Array (α × β) :=
|
||||
m.foldl (init := #[]) fun ps k v => ps.push (k, v)
|
||||
|
||||
structure Stats where
|
||||
numNodes : Nat := 0
|
||||
numNull : Nat := 0
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Data.Format
|
||||
import Lean.Data.Json
|
||||
import Lean.ToExpr
|
||||
|
||||
namespace Lean
|
||||
@@ -13,7 +12,7 @@ namespace Lean
|
||||
structure Position where
|
||||
line : Nat
|
||||
column : Nat
|
||||
deriving Inhabited, DecidableEq, Repr, ToJson, FromJson
|
||||
deriving Inhabited, DecidableEq, Repr
|
||||
|
||||
namespace Position
|
||||
protected def lt : Position → Position → Bool
|
||||
|
||||
@@ -49,26 +49,13 @@ def ReducibilityHints.getHeightEx (h : ReducibilityHints) : UInt32 :=
|
||||
|
||||
namespace ReducibilityHints
|
||||
|
||||
-- Recall that if `lt h₁ h₂`, we want to reduce declaration associated with `h₁`.
|
||||
def lt : ReducibilityHints → ReducibilityHints → Bool
|
||||
| .abbrev, .abbrev => false
|
||||
| .abbrev, _ => true
|
||||
| .regular d₁, .regular d₂ => d₁ > d₂
|
||||
| .regular d₁, .regular d₂ => d₁ < d₂
|
||||
| .regular _, .opaque => true
|
||||
| _, _ => false
|
||||
|
||||
protected def compare : ReducibilityHints → ReducibilityHints → Ordering
|
||||
| .abbrev, .abbrev => .eq
|
||||
| .abbrev, _ => .lt
|
||||
| .regular _, .abbrev => .gt
|
||||
| .regular d₁, .regular d₂ => Ord.compare d₂ d₁
|
||||
| .regular _, .opaque => .lt
|
||||
| .opaque, .opaque => .eq
|
||||
| .opaque, _ => .gt
|
||||
|
||||
instance : Ord ReducibilityHints where
|
||||
compare := ReducibilityHints.compare
|
||||
|
||||
def isAbbrev : ReducibilityHints → Bool
|
||||
| .abbrev => true
|
||||
| _ => false
|
||||
|
||||
@@ -16,12 +16,10 @@ private builtin_initialize docStringExt : MapDeclarationExtension String ← mkM
|
||||
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit :=
|
||||
builtinDocStrings.modify (·.insert declName docString.removeLeadingSpaces)
|
||||
|
||||
def addDocString [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString : String) : m Unit := do
|
||||
unless (← getEnv).getModuleIdxFor? declName |>.isNone do
|
||||
throwError s!"invalid doc string, declaration '{declName}' is in an imported module"
|
||||
def addDocString [MonadEnv m] (declName : Name) (docString : String) : m Unit :=
|
||||
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
|
||||
|
||||
def addDocString' [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString? : Option String) : m Unit :=
|
||||
def addDocString' [Monad m] [MonadEnv m] (declName : Name) (docString? : Option String) : m Unit :=
|
||||
match docString? with
|
||||
| some docString => addDocString declName docString
|
||||
| none => return ()
|
||||
|
||||
@@ -1194,24 +1194,13 @@ private def addLValArg (baseName : Name) (fullName : Name) (e : Expr) (args : Ar
|
||||
argIdx := argIdx + 1
|
||||
throwError "invalid field notation, function '{fullName}' does not have argument with type ({baseName} ...) that can be used, it must be explicit or implicit with a unique name"
|
||||
|
||||
/-- Adds the `TermInfo` for the field of a projection. See `Lean.Parser.Term.identProjKind`. -/
|
||||
private def addProjTermInfo
|
||||
(stx : Syntax)
|
||||
(e : Expr)
|
||||
(expectedType? : Option Expr := none)
|
||||
(lctx? : Option LocalContext := none)
|
||||
(elaborator : Name := Name.anonymous)
|
||||
(isBinder force : Bool := false)
|
||||
: TermElabM Expr :=
|
||||
addTermInfo (Syntax.node .none Parser.Term.identProjKind #[stx]) e expectedType? lctx? elaborator isBinder force
|
||||
|
||||
private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (expectedType? : Option Expr) (explicit ellipsis : Bool)
|
||||
(f : Expr) (lvals : List LVal) : TermElabM Expr :=
|
||||
let rec loop : Expr → List LVal → TermElabM Expr
|
||||
| f, [] => elabAppArgs f namedArgs args expectedType? explicit ellipsis
|
||||
| f, lval::lvals => do
|
||||
if let LVal.fieldName (fullRef := fullRef) .. := lval then
|
||||
addDotCompletionInfo fullRef f expectedType?
|
||||
if let LVal.fieldName (ref := fieldStx) (targetStx := targetStx) .. := lval then
|
||||
addDotCompletionInfo targetStx f expectedType? fieldStx
|
||||
let hasArgs := !namedArgs.isEmpty || !args.isEmpty
|
||||
let (f, lvalRes) ← resolveLVal f lval hasArgs
|
||||
match lvalRes with
|
||||
@@ -1225,7 +1214,7 @@ private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (exp
|
||||
if isPrivateNameFromImportedModule (← getEnv) info.projFn then
|
||||
throwError "field '{fieldName}' from structure '{structName}' is private"
|
||||
let projFn ← mkConst info.projFn
|
||||
let projFn ← addProjTermInfo lval.getRef projFn
|
||||
let projFn ← addTermInfo lval.getRef projFn
|
||||
if lvals.isEmpty then
|
||||
let namedArgs ← addNamedArg namedArgs { name := `self, val := Arg.expr f }
|
||||
elabAppArgs projFn namedArgs args expectedType? explicit ellipsis
|
||||
@@ -1237,7 +1226,7 @@ private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (exp
|
||||
| LValResolution.const baseStructName structName constName =>
|
||||
let f ← if baseStructName != structName then mkBaseProjections baseStructName structName f else pure f
|
||||
let projFn ← mkConst constName
|
||||
let projFn ← addProjTermInfo lval.getRef projFn
|
||||
let projFn ← addTermInfo lval.getRef projFn
|
||||
if lvals.isEmpty then
|
||||
let projFnType ← inferType projFn
|
||||
let (args, namedArgs) ← addLValArg baseStructName constName f args namedArgs projFnType
|
||||
@@ -1246,7 +1235,7 @@ private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (exp
|
||||
let f ← elabAppArgs projFn #[] #[Arg.expr f] (expectedType? := none) (explicit := false) (ellipsis := false)
|
||||
loop f lvals
|
||||
| LValResolution.localRec baseName fullName fvar =>
|
||||
let fvar ← addProjTermInfo lval.getRef fvar
|
||||
let fvar ← addTermInfo lval.getRef fvar
|
||||
if lvals.isEmpty then
|
||||
let fvarType ← inferType fvar
|
||||
let (args, namedArgs) ← addLValArg baseName fullName f args namedArgs fvarType
|
||||
@@ -1351,7 +1340,7 @@ private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Arra
|
||||
let elabFieldName (e field : Syntax) := do
|
||||
let newLVals := field.identComponents.map fun comp =>
|
||||
-- We use `none` in `suffix?` since `field` can't be part of a composite name
|
||||
LVal.fieldName comp comp.getId.getString! none f
|
||||
LVal.fieldName comp comp.getId.getString! none e
|
||||
elabAppFn e (newLVals ++ lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
|
||||
let elabFieldIdx (e idxStx : Syntax) := do
|
||||
let some idx := idxStx.isFieldIdx? | throwError "invalid field index"
|
||||
|
||||
@@ -119,6 +119,64 @@ private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM
|
||||
@[builtin_command_elab choice] def elabChoice : CommandElab := fun stx =>
|
||||
elabChoiceAux stx.getArgs 0
|
||||
|
||||
/-- Declares one or more universe variables.
|
||||
|
||||
`universe u v`
|
||||
|
||||
`Prop`, `Type`, `Type u` and `Sort u` are types that classify other types, also known as
|
||||
*universes*. In `Type u` and `Sort u`, the variable `u` stands for the universe's *level*, and a
|
||||
universe at level `u` can only classify universes that are at levels lower than `u`. For more
|
||||
details on type universes, please refer to [the relevant chapter of Theorem Proving in Lean][tpil
|
||||
universes].
|
||||
|
||||
Just as type arguments allow polymorphic definitions to be used at many different types, universe
|
||||
parameters, represented by universe variables, allow a definition to be used at any required level.
|
||||
While Lean mostly handles universe levels automatically, declaring them explicitly can provide more
|
||||
control when writing signatures. The `universe` keyword allows the declared universe variables to be
|
||||
used in a collection of definitions, and Lean will ensure that these definitions use them
|
||||
consistently.
|
||||
|
||||
[tpil universes]: https://lean-lang.org/theorem_proving_in_lean4/dependent_type_theory.html#types-as-objects
|
||||
(Type universes on Theorem Proving in Lean)
|
||||
|
||||
```lean
|
||||
/- Explicit type-universe parameter. -/
|
||||
def id₁.{u} (α : Type u) (a : α) := a
|
||||
|
||||
/- Implicit type-universe parameter, equivalent to `id₁`.
|
||||
Requires option `autoImplicit true`, which is the default. -/
|
||||
def id₂ (α : Type u) (a : α) := a
|
||||
|
||||
/- Explicit standalone universe variable declaration, equivalent to `id₁` and `id₂`. -/
|
||||
universe u
|
||||
def id₃ (α : Type u) (a : α) := a
|
||||
```
|
||||
|
||||
On a more technical note, using a universe variable only in the right-hand side of a definition
|
||||
causes an error if the universe has not been declared previously.
|
||||
|
||||
```lean
|
||||
def L₁.{u} := List (Type u)
|
||||
|
||||
-- def L₂ := List (Type u) -- error: `unknown universe level 'u'`
|
||||
|
||||
universe u
|
||||
def L₃ := List (Type u)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```lean
|
||||
universe u v w
|
||||
|
||||
structure Pair (α : Type u) (β : Type v) : Type (max u v) where
|
||||
a : α
|
||||
b : β
|
||||
|
||||
#check Pair.{v, w}
|
||||
-- Pair : Type v → Type w → Type (max v w)
|
||||
```
|
||||
-/
|
||||
@[builtin_command_elab «universe»] def elabUniverse : CommandElab := fun n => do
|
||||
n[1].forArgsM addUnivLevel
|
||||
|
||||
@@ -127,6 +185,30 @@ private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM
|
||||
| Except.ok env => setEnv env
|
||||
| Except.error ex => throwError (ex.toMessageData (← getOptions))
|
||||
|
||||
/-- Adds names from other namespaces to the current namespace.
|
||||
|
||||
The command `export Some.Namespace (name₁ name₂)` makes `name₁` and `name₂`:
|
||||
|
||||
- visible in the current namespace without prefix `Some.Namespace`, like `open`, and
|
||||
- visible from outside the current namespace `N` as `N.name₁` and `N.name₂`.
|
||||
|
||||
## Examples
|
||||
|
||||
```lean
|
||||
namespace Morning.Sky
|
||||
def star := "venus"
|
||||
end Morning.Sky
|
||||
|
||||
namespace Evening.Sky
|
||||
export Morning.Sky (star)
|
||||
-- `star` is now in scope
|
||||
#check star
|
||||
end Evening.Sky
|
||||
|
||||
-- `star` is visible in `Evening.Sky`
|
||||
#check Evening.Sky.star
|
||||
```
|
||||
-/
|
||||
@[builtin_command_elab «export»] def elabExport : CommandElab := fun stx => do
|
||||
let `(export $ns ($ids*)) := stx | throwUnsupportedSyntax
|
||||
let nss ← resolveNamespace ns
|
||||
@@ -141,6 +223,118 @@ private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM
|
||||
aliases := aliases.push (currNamespace ++ id, declName)
|
||||
modify fun s => { s with env := aliases.foldl (init := s.env) fun env p => addAlias env p.1 p.2 }
|
||||
|
||||
/-- Makes names from other namespaces visible without writing the namespace prefix.
|
||||
|
||||
Names that are made available with `open` are visible within the current `section` or `namespace`
|
||||
block. This makes referring to (type) definitions and theorems easier, but note that it can also
|
||||
make [scoped instances], notations, and attributes from a different namespace available.
|
||||
|
||||
The `open` command can be used in a few different ways:
|
||||
|
||||
* `open Some.Namespace.Path1 Some.Namespace.Path2` makes all non-protected names in
|
||||
`Some.Namespace.Path1` and `Some.Namespace.Path2` available without the prefix, so that
|
||||
`Some.Namespace.Path1.x` and `Some.Namespace.Path2.y` can be referred to by writing only `x` and
|
||||
`y`.
|
||||
|
||||
* `open Some.Namespace.Path hiding def1 def2` opens all non-protected names in `Some.Namespace.Path`
|
||||
except `def1` and `def2`.
|
||||
|
||||
* `open Some.Namespace.Path (def1 def2)` only makes `Some.Namespace.Path.def1` and
|
||||
`Some.Namespace.Path.def2` available without the full prefix, so `Some.Namespace.Path.def3` would
|
||||
be unaffected.
|
||||
|
||||
This works even if `def1` and `def2` are `protected`.
|
||||
|
||||
* `open Some.Namespace.Path renaming def1 → def1', def2 → def2'` same as `open Some.Namespace.Path
|
||||
(def1 def2)` but `def1`/`def2`'s names are changed to `def1'`/`def2'`.
|
||||
|
||||
This works even if `def1` and `def2` are `protected`.
|
||||
|
||||
* `open scoped Some.Namespace.Path1 Some.Namespace.Path2` **only** opens [scoped instances],
|
||||
notations, and attributes from `Namespace1` and `Namespace2`; it does **not** make any other name
|
||||
available.
|
||||
|
||||
* `open <any of the open shapes above> in` makes the names `open`-ed visible only in the next
|
||||
command or expression.
|
||||
|
||||
[scoped instance]: https://lean-lang.org/theorem_proving_in_lean4/type_classes.html#scoped-instances
|
||||
(Scoped instances in Theorem Proving in Lean)
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```lean
|
||||
/-- SKI combinators https://en.wikipedia.org/wiki/SKI_combinator_calculus -/
|
||||
namespace Combinator.Calculus
|
||||
def I (a : α) : α := a
|
||||
def K (a : α) : β → α := fun _ => a
|
||||
def S (x : α → β → γ) (y : α → β) (z : α) : γ := x z (y z)
|
||||
end Combinator.Calculus
|
||||
|
||||
section
|
||||
-- open everything under `Combinator.Calculus`, *i.e.* `I`, `K` and `S`,
|
||||
-- until the section ends
|
||||
open Combinator.Calculus
|
||||
|
||||
theorem SKx_eq_K : S K x = I := rfl
|
||||
end
|
||||
|
||||
-- open everything under `Combinator.Calculus` only for the next command (the next `theorem`, here)
|
||||
open Combinator.Calculus in
|
||||
theorem SKx_eq_K' : S K x = I := rfl
|
||||
|
||||
section
|
||||
-- open only `S` and `K` under `Combinator.Calculus`
|
||||
open Combinator.Calculus (S K)
|
||||
|
||||
theorem SKxy_eq_y : S K x y = y := rfl
|
||||
|
||||
-- `I` is not in scope, we have to use its full path
|
||||
theorem SKxy_eq_Iy : S K x y = Combinator.Calculus.I y := rfl
|
||||
end
|
||||
|
||||
section
|
||||
open Combinator.Calculus
|
||||
renaming
|
||||
I → identity,
|
||||
K → konstant
|
||||
|
||||
#check identity
|
||||
#check konstant
|
||||
end
|
||||
|
||||
section
|
||||
open Combinator.Calculus
|
||||
hiding S
|
||||
|
||||
#check I
|
||||
#check K
|
||||
end
|
||||
|
||||
section
|
||||
namespace Demo
|
||||
inductive MyType
|
||||
| val
|
||||
|
||||
namespace N1
|
||||
scoped infix:68 " ≋ " => BEq.beq
|
||||
|
||||
scoped instance : BEq MyType where
|
||||
beq _ _ := true
|
||||
|
||||
def Alias := MyType
|
||||
end N1
|
||||
end Demo
|
||||
|
||||
-- bring `≋` and the instance in scope, but not `Alias`
|
||||
open scoped Demo.N1
|
||||
|
||||
#check Demo.MyType.val == Demo.MyType.val
|
||||
#check Demo.MyType.val ≋ Demo.MyType.val
|
||||
-- #check Alias -- unknown identifier 'Alias'
|
||||
end
|
||||
```
|
||||
-/
|
||||
@[builtin_command_elab «open»] def elabOpen : CommandElab
|
||||
| `(open $decl:openDecl) => do
|
||||
let openDecls ← elabOpenDecl decl
|
||||
@@ -226,6 +420,102 @@ private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBin
|
||||
else
|
||||
return #[binder]
|
||||
|
||||
/-- Declares one or more typed variables, or modifies whether already-declared variables are
|
||||
implicit.
|
||||
|
||||
Introduces variables that can be used in definitions within the same `namespace` or `section` block.
|
||||
When a definition mentions a variable, Lean will add it as an argument of the definition. The
|
||||
`variable` command is also able to add typeclass parameters. This is useful in particular when
|
||||
writing many definitions that have parameters in common (see below for an example).
|
||||
|
||||
Variable declarations have the same flexibility as regular function paramaters. In particular they
|
||||
can be [explicit, implicit][binder docs], or [instance implicit][tpil classes] (in which case they
|
||||
can be anonymous). This can be changed, for instance one can turn explicit variable `x` into an
|
||||
implicit one with `variable {x}`. Note that currently, you should avoid changing how variables are
|
||||
bound and declare new variables at the same time; see [issue 2789] for more on this topic.
|
||||
|
||||
See [*Variables and Sections* from Theorem Proving in Lean][tpil vars] for a more detailed
|
||||
discussion.
|
||||
|
||||
[tpil vars]: https://lean-lang.org/theorem_proving_in_lean4/dependent_type_theory.html#variables-and-sections
|
||||
(Variables and Sections on Theorem Proving in Lean)
|
||||
[tpil classes]: https://lean-lang.org/theorem_proving_in_lean4/type_classes.html
|
||||
(Type classes on Theorem Proving in Lean)
|
||||
[binder docs]: https://leanprover-community.github.io/mathlib4_docs/Lean/Expr.html#Lean.BinderInfo
|
||||
(Documentation for the BinderInfo type)
|
||||
[issue 2789]: https://github.com/leanprover/lean4/issues/2789
|
||||
(Issue 2789 on github)
|
||||
|
||||
## Examples
|
||||
|
||||
```lean
|
||||
section
|
||||
variable
|
||||
{α : Type u} -- implicit
|
||||
(a : α) -- explicit
|
||||
[instBEq : BEq α] -- instance implicit, named
|
||||
[Hashable α] -- instance implicit, anonymous
|
||||
|
||||
def isEqual (b : α) : Bool :=
|
||||
a == b
|
||||
|
||||
#check isEqual
|
||||
-- isEqual.{u} {α : Type u} (a : α) [instBEq : BEq α] (b : α) : Bool
|
||||
|
||||
variable
|
||||
{a} -- `a` is implicit now
|
||||
|
||||
def eqComm {b : α} := a == b ↔ b == a
|
||||
|
||||
#check eqComm
|
||||
-- eqComm.{u} {α : Type u} {a : α} [instBEq : BEq α] {b : α} : Prop
|
||||
end
|
||||
```
|
||||
|
||||
The following shows a typical use of `variable` to factor out definition arguments:
|
||||
|
||||
```lean
|
||||
variable (Src : Type)
|
||||
|
||||
structure Logger where
|
||||
trace : List (Src × String)
|
||||
#check Logger
|
||||
-- Logger (Src : Type) : Type
|
||||
|
||||
namespace Logger
|
||||
-- switch `Src : Type` to be implicit until the `end Logger`
|
||||
variable {Src}
|
||||
|
||||
def empty : Logger Src where
|
||||
trace := []
|
||||
#check empty
|
||||
-- Logger.empty {Src : Type} : Logger Src
|
||||
|
||||
variable (log : Logger Src)
|
||||
|
||||
def len :=
|
||||
log.trace.length
|
||||
#check len
|
||||
-- Logger.len {Src : Type} (log : Logger Src) : Nat
|
||||
|
||||
variable (src : Src) [BEq Src]
|
||||
|
||||
-- at this point all of `log`, `src`, `Src` and the `BEq` instance can all become arguments
|
||||
|
||||
def filterSrc :=
|
||||
log.trace.filterMap
|
||||
fun (src', str') => if src' == src then some str' else none
|
||||
#check filterSrc
|
||||
-- Logger.filterSrc {Src : Type} (log : Logger Src) (src : Src) [inst✝ : BEq Src] : List String
|
||||
|
||||
def lenSrc :=
|
||||
log.filterSrc src |>.length
|
||||
#check lenSrc
|
||||
-- Logger.lenSrc {Src : Type} (log : Logger Src) (src : Src) [inst✝ : BEq Src] : Nat
|
||||
end Logger
|
||||
```
|
||||
|
||||
-/
|
||||
@[builtin_command_elab «variable»] def elabVariable : CommandElab
|
||||
| `(variable $binders*) => do
|
||||
-- Try to elaborate `binders` for sanity checking
|
||||
@@ -248,7 +538,10 @@ def elabCheckCore (ignoreStuckTC : Bool) : CommandElab
|
||||
try
|
||||
for c in (← realizeGlobalConstWithInfos term) do
|
||||
addCompletionInfo <| .id term id.getId (danglingDot := false) {} none
|
||||
logInfoAt tk <| .signature c
|
||||
logInfoAt tk <| .ofPPFormat { pp := fun
|
||||
| some ctx => ctx.runMetaM <| PrettyPrinter.ppSignature c
|
||||
| none => return f!"{c}" -- should never happen
|
||||
}
|
||||
return
|
||||
catch _ => pure () -- identifier might not be a constant but constant + projection
|
||||
let e ← Term.elabTerm term none
|
||||
@@ -456,7 +749,7 @@ def elabRunMeta : CommandElab := fun stx =>
|
||||
pure ()
|
||||
|
||||
@[builtin_command_elab «set_option»] def elabSetOption : CommandElab := fun stx => do
|
||||
let options ← Elab.elabSetOption stx[1] stx[3]
|
||||
let options ← Elab.elabSetOption stx[1] stx[2]
|
||||
modify fun s => { s with maxRecDepth := maxRecDepth.get options }
|
||||
modifyScope fun scope => { scope with opts := options }
|
||||
|
||||
|
||||
@@ -312,9 +312,9 @@ private def mkSilentAnnotationIfHole (e : Expr) : TermElabM Expr := do
|
||||
popScope
|
||||
|
||||
@[builtin_term_elab «set_option»] def elabSetOption : TermElab := fun stx expectedType? => do
|
||||
let options ← Elab.elabSetOption stx[1] stx[3]
|
||||
let options ← Elab.elabSetOption stx[1] stx[2]
|
||||
withTheReader Core.Context (fun ctx => { ctx with maxRecDepth := maxRecDepth.get options, options := options }) do
|
||||
elabTerm stx[5] expectedType?
|
||||
elabTerm stx[4] expectedType?
|
||||
|
||||
@[builtin_term_elab withAnnotateTerm] def elabWithAnnotateTerm : TermElab := fun stx expectedType? => do
|
||||
match stx with
|
||||
|
||||
@@ -7,7 +7,6 @@ prelude
|
||||
import Lean.Elab.Binders
|
||||
import Lean.Elab.SyntheticMVars
|
||||
import Lean.Elab.SetOption
|
||||
import Lean.Language.Basic
|
||||
|
||||
namespace Lean.Elab.Command
|
||||
|
||||
@@ -31,6 +30,7 @@ structure State where
|
||||
scopes : List Scope := [{ header := "" }]
|
||||
nextMacroScope : Nat := firstFrontendMacroScope + 1
|
||||
maxRecDepth : Nat
|
||||
nextInstIdx : Nat := 1 -- for generating anonymous instance names
|
||||
ngen : NameGenerator := {}
|
||||
infoState : InfoState := {}
|
||||
traceState : TraceState := {}
|
||||
@@ -45,16 +45,6 @@ structure Context where
|
||||
currMacroScope : MacroScope := firstFrontendMacroScope
|
||||
ref : Syntax := Syntax.missing
|
||||
tacticCache? : Option (IO.Ref Tactic.Cache)
|
||||
/--
|
||||
Snapshot for incremental reuse and reporting of command elaboration. Currently unused in Lean
|
||||
itself.
|
||||
|
||||
Definitely resolved in `Language.Lean.process.doElab`.
|
||||
|
||||
Invariant: if the bundle's `old?` is set, the context and state at the beginning of current and
|
||||
old elaboration are identical.
|
||||
-/
|
||||
snap? : Option (Language.SnapshotBundle Language.DynamicSnapshot)
|
||||
|
||||
abbrev CommandElabCoreM (ε) := ReaderT Context $ StateRefT State $ EIO ε
|
||||
abbrev CommandElabM := CommandElabCoreM Exception
|
||||
@@ -157,13 +147,10 @@ private def addTraceAsMessagesCore (ctx : Context) (log : MessageLog) (traceStat
|
||||
|
||||
private def addTraceAsMessages : CommandElabM Unit := do
|
||||
let ctx ← read
|
||||
-- do not add trace messages if `trace.profiler.output` is set as it would be redundant and
|
||||
-- pretty printing the trace messages is expensive
|
||||
if trace.profiler.output.get? (← getOptions) |>.isNone then
|
||||
modify fun s => { s with
|
||||
messages := addTraceAsMessagesCore ctx s.messages s.traceState
|
||||
traceState.traces := {}
|
||||
}
|
||||
modify fun s => { s with
|
||||
messages := addTraceAsMessagesCore ctx s.messages s.traceState
|
||||
traceState.traces := {}
|
||||
}
|
||||
|
||||
def liftCoreM (x : CoreM α) : CommandElabM α := do
|
||||
let s ← get
|
||||
@@ -220,8 +207,7 @@ def runLinters (stx : Syntax) : CommandElabM Unit := do
|
||||
let linters ← lintersRef.get
|
||||
unless linters.isEmpty do
|
||||
for linter in linters do
|
||||
withTraceNode `Elab.lint (fun _ => return m!"running linter: {linter.name}")
|
||||
(tag := linter.name.toString) do
|
||||
withTraceNode `Elab.lint (fun _ => return m!"running linter: {linter.name}") do
|
||||
let savedState ← get
|
||||
try
|
||||
linter.run stx
|
||||
@@ -293,9 +279,7 @@ partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
|
||||
-- list of commands => elaborate in order
|
||||
-- The parser will only ever return a single command at a time, but syntax quotations can return multiple ones
|
||||
args.forM elabCommand
|
||||
else withTraceNode `Elab.command (fun _ => return stx) (tag :=
|
||||
-- special case: show actual declaration kind for `declaration` commands
|
||||
(if stx.isOfKind ``Parser.Command.declaration then stx[1] else stx).getKind.toString) do
|
||||
else withTraceNode `Elab.command (fun _ => return stx) do
|
||||
let s ← get
|
||||
match (← liftMacroM <| expandMacroImpl? s.env stx) with
|
||||
| some (decl, stxNew?) =>
|
||||
@@ -531,7 +515,6 @@ def liftCommandElabM (cmd : CommandElabM α) : CoreM α := do
|
||||
fileMap := ← getFileMap
|
||||
ref := ← getRef
|
||||
tacticCache? := none
|
||||
snap? := none
|
||||
} |>.run {
|
||||
env := ← getEnv
|
||||
maxRecDepth := ← getMaxRecDepth
|
||||
@@ -552,9 +535,9 @@ first evaluates any local `set_option ... in ...` clauses and then invokes `cmd`
|
||||
partial def withSetOptionIn (cmd : CommandElab) : CommandElab := fun stx => do
|
||||
if stx.getKind == ``Lean.Parser.Command.in &&
|
||||
stx[0].getKind == ``Lean.Parser.Command.set_option then
|
||||
let opts ← Elab.elabSetOption stx[0][1] stx[0][3]
|
||||
let opts ← Elab.elabSetOption stx[0][1] stx[0][2]
|
||||
Command.withScope (fun scope => { scope with opts }) do
|
||||
withSetOptionIn cmd stx[2]
|
||||
withSetOptionIn cmd stx[1]
|
||||
else
|
||||
cmd stx
|
||||
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kyle Miller
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Command
|
||||
|
||||
/-!
|
||||
# Name generator for declarations
|
||||
|
||||
This module provides functionality to generate a name for a declaration using its binders and its type.
|
||||
This is used to generate names for anonymous instances.
|
||||
|
||||
It uses heuristics to generate an informative but terse name given its namespace, supplied binders, and type.
|
||||
It tries to make these relatively unique,
|
||||
and it uses suffixes derived from the module to ensure cross-project uniqueness
|
||||
when the instance doesn't refer to anything defined in the current project.
|
||||
|
||||
The name generator can be thought of as a kind of pretty printer, rendering an expression in textual form.
|
||||
The general structure of this generator is
|
||||
1. `Lean.Elab.Command.NameGen.winnowExpr` takes an expression and re-uses `Expr` as a data structure
|
||||
to record the "Syntax"-like structure.
|
||||
2. `Lean.Elab.Command.NameGen.mkBaseNameCore` formats the result of that as a string.
|
||||
It actually does a bit more computation than that, since it further removes duplicate expressions,
|
||||
reporting only the first occurrence of each subexpression.
|
||||
-/
|
||||
|
||||
namespace Lean.Elab.Command
|
||||
|
||||
open Meta
|
||||
|
||||
namespace NameGen
|
||||
|
||||
/--
|
||||
If `e` is an application of a projection to a parent structure, returns the term being projected.
|
||||
-/
|
||||
private def getParentProjArg (e : Expr) : MetaM (Option Expr) := do
|
||||
let .const c@(.str _ field) _ := e.getAppFn | return none
|
||||
let env ← getEnv
|
||||
let some info := env.getProjectionFnInfo? c | return none
|
||||
unless e.getAppNumArgs == info.numParams + 1 do return none
|
||||
let some (.ctorInfo cVal) := env.find? info.ctorName | return none
|
||||
if isSubobjectField? env cVal.induct (Name.mkSimple field) |>.isNone then return none
|
||||
return e.appArg!
|
||||
|
||||
/--
|
||||
Strips out universes and arguments we decide are unnecessary for naming.
|
||||
The resulting expression can have loose fvars and be mangled in other ways.
|
||||
Expressions can also be replaced by `.bvar 0` if they shouldn't be mentioned.
|
||||
-/
|
||||
private partial def winnowExpr (e : Expr) : MetaM Expr := do
|
||||
let rec visit (e : Expr) : MonadCacheT Expr Expr MetaM Expr := checkCache e fun _ => do
|
||||
if ← isProof e then
|
||||
return .bvar 0
|
||||
match e with
|
||||
| .app .. =>
|
||||
if let some e' ← getParentProjArg e then
|
||||
return (← visit e')
|
||||
e.withApp fun f args => do
|
||||
-- Visit only the explicit arguments to `f` and also its type (and type family) arguments.
|
||||
-- The reason we visit type arguments is so that, for example, `Decidable (_ < _)` has
|
||||
-- a chance to insert type information. Type families are for reporting things such as type constructors and monads.
|
||||
let mut fty ← inferType f
|
||||
let mut j := 0
|
||||
let mut e' ← visit f
|
||||
for i in [0:args.size] do
|
||||
unless fty.isForall do
|
||||
fty ← withTransparency .all <| whnf <| fty.instantiateRevRange j i args
|
||||
j := i
|
||||
let .forallE _ _ fty' bi := fty | failure
|
||||
fty := fty'
|
||||
let arg := args[i]!
|
||||
if ← pure bi.isExplicit <||> (pure !arg.isSort <&&> isTypeFormer arg) then
|
||||
unless (← isProof arg) do
|
||||
e' := .app e' (← visit arg)
|
||||
return e'
|
||||
| .forallE n ty body bi =>
|
||||
withLocalDecl n bi ty fun arg => do
|
||||
-- In a dependent forall the body implies `ty`, so we won't mention it.
|
||||
let ty' ← if body.hasLooseBVars then pure (.bvar 0) else visit ty
|
||||
return .forallE n ty' (← visit (body.instantiate1 arg)) bi
|
||||
| .lam n ty body bi =>
|
||||
if let some e := Expr.etaExpandedStrict? e then
|
||||
visit e
|
||||
else
|
||||
withLocalDecl n bi ty fun arg => do
|
||||
-- Don't record the `.lam` since `ty` should be recorded elsewhere in the expression.
|
||||
visit (body.instantiate1 arg)
|
||||
| .letE _n _t v b _ => visit (b.instantiate1 v)
|
||||
| .sort .. =>
|
||||
if e.isProp then return .sort levelZero
|
||||
else if e.isType then return .sort levelOne
|
||||
else return .sort (.param `u)
|
||||
| .const name .. => return .const name []
|
||||
| .mdata _ e' => visit e'
|
||||
| _ => return .bvar 0
|
||||
visit e |>.run
|
||||
|
||||
/--
|
||||
State for name generation.
|
||||
-/
|
||||
private structure MkNameState where
|
||||
/-- Keeps track of expressions already visited so that we do not include them again. -/
|
||||
seen : ExprSet := {}
|
||||
/-- Keeps track of constants that appear in the generated name. -/
|
||||
consts : NameSet := {}
|
||||
|
||||
/--
|
||||
Monad for name generation.
|
||||
-/
|
||||
private abbrev MkNameM := StateRefT MkNameState MetaM
|
||||
|
||||
/--
|
||||
Core algorithm for generating a name. The provided expression should be a winnowed expression.
|
||||
|
||||
- `omitTopForall` if true causes "Forall" to not be included if the binding type results in an empty string.
|
||||
-/
|
||||
private def mkBaseNameCore (e : Expr) (omitTopForall : Bool := false) : MkNameM String :=
|
||||
visit e omitTopForall
|
||||
where
|
||||
visit (e : Expr) (omitTopForall : Bool := false) : MkNameM String := do
|
||||
if (← get).seen.contains e then
|
||||
return ""
|
||||
else
|
||||
let s ← visit' e omitTopForall
|
||||
modify fun st => {st with seen := st.seen.insert e}
|
||||
return s
|
||||
visit' (e : Expr) (omitTopForall : Bool) : MkNameM String := do
|
||||
match e with
|
||||
| .const name .. =>
|
||||
modify (fun st => {st with consts := st.consts.insert name})
|
||||
return match name.eraseMacroScopes with
|
||||
| .str _ str => str.capitalize
|
||||
| _ => ""
|
||||
| .app f x => (· ++ ·) <$> visit f <*> visit x
|
||||
| .forallE _ ty body _ =>
|
||||
let sty ← visit ty
|
||||
if omitTopForall && sty == "" then
|
||||
visit body true
|
||||
else
|
||||
("Forall" ++ sty ++ ·) <$> visit body
|
||||
| .sort .zero => return "Prop"
|
||||
| .sort (.succ _) => return "Type"
|
||||
| .sort _ => return "Sort"
|
||||
| _ => return ""
|
||||
|
||||
/--
|
||||
Generate a name, while naming the top-level foralls using "Of".
|
||||
The provided expression should be a winnowed expression.
|
||||
-/
|
||||
private partial def mkBaseNameAux (e : Expr) : MkNameM String := do
|
||||
let (foralls, sb) ← visit e
|
||||
return sb ++ String.join foralls
|
||||
where
|
||||
visit (e : Expr) : MkNameM (List String × String) := do
|
||||
match e with
|
||||
| .forallE _ ty body _ =>
|
||||
let (foralls, sb) ← visit body
|
||||
let st ← mkBaseNameCore ty (omitTopForall := true)
|
||||
if st == "" then
|
||||
return (foralls, sb)
|
||||
else
|
||||
return (("Of" ++ st) :: foralls, sb)
|
||||
| _ => return ([], ← mkBaseNameCore e)
|
||||
|
||||
/--
|
||||
Adds all prefixes of `ns` as seen constants.
|
||||
-/
|
||||
private def visitNamespace (ns : Name) : MkNameM Unit := do
|
||||
match ns with
|
||||
| .anonymous => pure ()
|
||||
| .num ns' _ => visitNamespace ns'
|
||||
| .str ns' _ =>
|
||||
let env ← getEnv
|
||||
if env.contains ns then
|
||||
modify fun st => {st with seen := st.seen.insert (.const ns []), consts := st.consts.insert ns}
|
||||
visitNamespace ns'
|
||||
|
||||
/--
|
||||
Given an expression, generates a "base name" for a declaration.
|
||||
The top-level foralls in `e` are treated as being binders, so use the `...Of...` naming convention.
|
||||
The current namespace is used to seed the seen expressions with each prefix of the namespace that's a global constant.
|
||||
|
||||
Collects all constants that contribute to the name in the `MkInstM` state.
|
||||
This can be used to decide whether to further transform the generated name;
|
||||
in particular, this enables checking whether the generated name mentions declarations
|
||||
from the current module or project.
|
||||
-/
|
||||
def mkBaseName (e : Expr) : MkNameM String := do
|
||||
let e ← instantiateMVars e
|
||||
visitNamespace (← getCurrNamespace)
|
||||
mkBaseNameAux (← winnowExpr e)
|
||||
|
||||
/--
|
||||
Converts a module name into a suffix. Includes a leading `_`,
|
||||
so for example `Lean.Elab.DefView` becomes `_lean_elab_defView`.
|
||||
-/
|
||||
private def moduleToSuffix : Name → String
|
||||
| .anonymous => ""
|
||||
| .num n _ => moduleToSuffix n
|
||||
| .str n s => moduleToSuffix n ++ "_" ++ s.decapitalize
|
||||
|
||||
/--
|
||||
Uses heuristics to generate an informative but terse base name for a term of the given type, using `mkBaseName`.
|
||||
Makes use of the current namespace.
|
||||
It tries to make these names relatively unique ecosystem-wide,
|
||||
and it adds suffixes using the current module if the resulting name doesn't refer to anything defined in this module.
|
||||
-/
|
||||
def mkBaseNameWithSuffix (pre : String) (type : Expr) : MetaM String := do
|
||||
let (name, st) ← mkBaseName type |>.run {}
|
||||
let name := pre ++ name
|
||||
let project := (← getMainModule).getRoot
|
||||
-- Collect the modules for each constant that appeared.
|
||||
let modules ← st.consts.foldM (init := Array.mkEmpty st.consts.size) fun mods name => return mods.push (← findModuleOf? name)
|
||||
-- We can avoid adding the suffix if the instance refers to module-local names.
|
||||
let isModuleLocal := modules.any Option.isNone
|
||||
-- We can also avoid adding the full module suffix if the instance refers to "project"-local names.
|
||||
let isProjectLocal := isModuleLocal || modules.any fun mod? => mod?.map (·.getRoot) == project
|
||||
if !isProjectLocal then
|
||||
return s!"{name}{moduleToSuffix project}"
|
||||
else
|
||||
return name
|
||||
|
||||
/--
|
||||
Elaborates the binders and type and then uses `mkBaseNameWithSuffix` to generate a name.
|
||||
Furthermore, uses `mkUnusedBaseName` on the result.
|
||||
-/
|
||||
def mkBaseNameWithSuffix' (pre : String) (binders : Array Syntax) (type : Syntax) : TermElabM Name := do
|
||||
let name ←
|
||||
try
|
||||
Term.withAutoBoundImplicit <| Term.elabBinders binders fun binds => Term.withoutErrToSorry do
|
||||
let ty ← mkForallFVars binds (← Term.elabType type)
|
||||
mkBaseNameWithSuffix pre ty
|
||||
catch _ =>
|
||||
pure pre
|
||||
liftMacroM <| mkUnusedBaseName <| Name.mkSimple name
|
||||
|
||||
end NameGen
|
||||
|
||||
/--
|
||||
Generates an instance name for a declaration that has the given binders and type.
|
||||
It tries to make these names relatively unique ecosystem-wide.
|
||||
|
||||
Note that this elaborates the binders and the type.
|
||||
This means that when elaborating an instance declaration, we elaborate these twice.
|
||||
-/
|
||||
def mkInstanceName (binders : Array Syntax) (type : Syntax) : CommandElabM Name := do
|
||||
let savedState ← get
|
||||
try
|
||||
-- Unfortunately we can't include any of the binders from `runTermElabM` since, without
|
||||
-- elaborating the body of the instance, we have no idea which of these binders are
|
||||
-- actually used.
|
||||
runTermElabM fun _ => NameGen.mkBaseNameWithSuffix' "inst" binders type
|
||||
finally
|
||||
set savedState
|
||||
@@ -54,6 +54,14 @@ def expandDeclSig (stx : Syntax) : Syntax × Syntax :=
|
||||
let typeSpec := stx[1]
|
||||
(binders, typeSpec[1])
|
||||
|
||||
def mkFreshInstanceName (env : Environment) (nextIdx : Nat) : Name :=
|
||||
(env.mainModule ++ `_instance).appendIndexAfter nextIdx
|
||||
|
||||
def isFreshInstanceName (name : Name) : Bool :=
|
||||
match name with
|
||||
| .str _ s => "_instance".isPrefixOf s
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Sort the given list of `usedParams` using the following order:
|
||||
- If it is an explicit level `allUserParams`, then use user given order.
|
||||
|
||||
@@ -353,14 +353,14 @@ def elabMutual : CommandElab := fun stx => do
|
||||
for builtin simprocs.
|
||||
-/
|
||||
let declNames ←
|
||||
try
|
||||
realizeGlobalConstWithInfos ident
|
||||
catch _ =>
|
||||
let name := ident.getId.eraseMacroScopes
|
||||
if (← Simp.isBuiltinSimproc name) then
|
||||
pure [name]
|
||||
else
|
||||
throwUnknownConstant name
|
||||
try
|
||||
realizeGlobalConst ident
|
||||
catch _ =>
|
||||
let name := ident.getId.eraseMacroScopes
|
||||
if (← Simp.isBuiltinSimproc name) then
|
||||
pure [name]
|
||||
else
|
||||
throwUnknownConstant name
|
||||
let declName ← ensureNonAmbiguous ident declNames
|
||||
Term.applyAttributes declName attrs
|
||||
for attrName in toErase do
|
||||
|
||||
@@ -4,8 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
prelude
|
||||
import Lean.Meta.ForEachExpr
|
||||
import Lean.Elab.Command
|
||||
import Lean.Elab.DeclNameGen
|
||||
import Lean.Elab.DeclUtil
|
||||
|
||||
namespace Lean.Elab
|
||||
@@ -66,6 +66,41 @@ def mkDefViewOfTheorem (modifiers : Modifiers) (stx : Syntax) : DefView :=
|
||||
{ ref := stx, kind := DefKind.theorem, modifiers,
|
||||
declId := stx[1], binders, type? := some type, value := stx[3] }
|
||||
|
||||
def mkFreshInstanceName : CommandElabM Name := do
|
||||
let s ← get
|
||||
let idx := s.nextInstIdx
|
||||
modify fun s => { s with nextInstIdx := s.nextInstIdx + 1 }
|
||||
return Lean.Elab.mkFreshInstanceName s.env idx
|
||||
|
||||
/--
|
||||
Generate a name for an instance with the given type.
|
||||
Note that we elaborate the type twice. Once for producing the name, and another when elaborating the declaration. -/
|
||||
def mkInstanceName (binders : Array Syntax) (type : Syntax) : CommandElabM Name := do
|
||||
let savedState ← get
|
||||
try
|
||||
let result ← runTermElabM fun _ => Term.withAutoBoundImplicit <| Term.elabBinders binders fun _ => Term.withoutErrToSorry do
|
||||
let type ← instantiateMVars (← Term.elabType type)
|
||||
let ref ← IO.mkRef ""
|
||||
Meta.forEachExpr type fun e => do
|
||||
if e.isForall then ref.modify (· ++ "ForAll")
|
||||
else if e.isProp then ref.modify (· ++ "Prop")
|
||||
else if e.isType then ref.modify (· ++ "Type")
|
||||
else if e.isSort then ref.modify (· ++ "Sort")
|
||||
else if e.isConst then
|
||||
match e.constName!.eraseMacroScopes with
|
||||
| .str _ str =>
|
||||
if str.front.isLower then
|
||||
ref.modify (· ++ str.capitalize)
|
||||
else
|
||||
ref.modify (· ++ str)
|
||||
| _ => pure ()
|
||||
ref.get
|
||||
set savedState
|
||||
liftMacroM <| mkUnusedBaseName <| Name.mkSimple ("inst" ++ result)
|
||||
catch _ =>
|
||||
set savedState
|
||||
mkFreshInstanceName
|
||||
|
||||
def mkDefViewOfInstance (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView := do
|
||||
-- leading_parser Term.attrKind >> "instance " >> optNamedPrio >> optional declId >> declSig >> declVal
|
||||
let attrKind ← liftMacroM <| toAttributeKind stx[0]
|
||||
@@ -74,14 +109,9 @@ def mkDefViewOfInstance (modifiers : Modifiers) (stx : Syntax) : CommandElabM De
|
||||
let (binders, type) := expandDeclSig stx[4]
|
||||
let modifiers := modifiers.addAttribute { kind := attrKind, name := `instance, stx := attrStx }
|
||||
let declId ← match stx[3].getOptional? with
|
||||
| some declId =>
|
||||
if ← isTracingEnabledFor `Elab.instance.mkInstanceName then
|
||||
let id ← mkInstanceName binders.getArgs type
|
||||
trace[Elab.instance.mkInstanceName] "generated {(← getCurrNamespace) ++ id} for {declId}"
|
||||
pure declId
|
||||
| some declId => pure declId
|
||||
| none =>
|
||||
let id ← mkInstanceName binders.getArgs type
|
||||
trace[Elab.instance.mkInstanceName] "generated {(← getCurrNamespace) ++ id}"
|
||||
pure <| mkNode ``Parser.Command.declId #[mkIdentFrom stx id, mkNullNode]
|
||||
return {
|
||||
ref := stx, kind := DefKind.def, modifiers := modifiers,
|
||||
@@ -136,7 +166,6 @@ def mkDefView (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView :=
|
||||
throwError "unexpected kind of definition"
|
||||
|
||||
builtin_initialize registerTraceClass `Elab.definition
|
||||
builtin_initialize registerTraceClass `Elab.instance.mkInstanceName
|
||||
|
||||
end Command
|
||||
end Lean.Elab
|
||||
|
||||
@@ -49,7 +49,10 @@ where
|
||||
let b := mkIdent (← mkFreshUserName `b)
|
||||
ctorArgs1 := ctorArgs1.push a
|
||||
ctorArgs2 := ctorArgs2.push b
|
||||
rhsCont := fun rhs => `(Ordering.then (compare $a $b) $rhs) >>= rhsCont
|
||||
rhsCont := fun rhs => `(match compare $a $b with
|
||||
| Ordering.lt => Ordering.lt
|
||||
| Ordering.gt => Ordering.gt
|
||||
| Ordering.eq => $rhs) >>= rhsCont
|
||||
let lPat ← `(@$(mkIdent ctorName):ident $ctorArgs1:term*)
|
||||
let rPat ← `(@$(mkIdent ctorName):ident $ctorArgs2:term*)
|
||||
let patterns := indPatterns ++ #[lPat, rPat]
|
||||
|
||||
@@ -7,7 +7,6 @@ prelude
|
||||
import Lean.Language.Lean
|
||||
import Lean.Util.Profile
|
||||
import Lean.Server.References
|
||||
import Lean.Util.Profiler
|
||||
|
||||
namespace Lean.Elab.Frontend
|
||||
|
||||
@@ -33,7 +32,6 @@ def setCommandState (commandState : Command.State) : FrontendM Unit :=
|
||||
fileName := ctx.inputCtx.fileName
|
||||
fileMap := ctx.inputCtx.fileMap
|
||||
tacticCache? := none
|
||||
snap? := none
|
||||
}
|
||||
match (← liftM <| EIO.toIO' <| (x cmdCtx).run s.commandState) with
|
||||
| Except.error e => throw <| IO.Error.userError s!"unexpected internal error: {← e.toMessageData.toString}"
|
||||
@@ -109,9 +107,7 @@ def runFrontend
|
||||
(mainModuleName : Name)
|
||||
(trustLevel : UInt32 := 0)
|
||||
(ileanFileName? : Option String := none)
|
||||
(jsonOutput : Bool := false)
|
||||
: IO (Environment × Bool) := do
|
||||
let startTime := (← IO.monoNanosNow).toFloat / 1000000000
|
||||
let inputCtx := Parser.mkInputContext input fileName
|
||||
-- TODO: replace with `#lang` processing
|
||||
if /- Lean #lang? -/ true then
|
||||
@@ -123,14 +119,14 @@ def runFrontend
|
||||
let (env, messages) ← processHeader (leakEnv := true) header opts messages inputCtx trustLevel
|
||||
let env := env.setMainModule mainModuleName
|
||||
let mut commandState := Command.mkState env messages opts
|
||||
let elabStartTime := (← IO.monoNanosNow).toFloat / 1000000000
|
||||
|
||||
if ileanFileName?.isSome then
|
||||
-- Collect InfoTrees so we can later extract and export their info to the ilean file
|
||||
commandState := { commandState with infoState.enabled := true }
|
||||
|
||||
let s ← IO.processCommands inputCtx parserState commandState
|
||||
Language.reportMessages s.commandState.messages opts jsonOutput
|
||||
for msg in s.commandState.messages.toList do
|
||||
IO.print (← msg.toString (includeEndPos := Language.printMessageEndPos.get opts))
|
||||
|
||||
if let some ileanFileName := ileanFileName? then
|
||||
let trees := s.commandState.infoState.trees.toArray
|
||||
@@ -139,26 +135,13 @@ def runFrontend
|
||||
let ilean := { module := mainModuleName, references : Lean.Server.Ilean }
|
||||
IO.FS.writeFile ileanFileName $ Json.compress $ toJson ilean
|
||||
|
||||
if let some out := trace.profiler.output.get? opts then
|
||||
let traceState := s.commandState.traceState
|
||||
-- importing does not happen in an elaboration monad, add now
|
||||
let traceState := { traceState with
|
||||
traces := #[{
|
||||
ref := .missing,
|
||||
msg := .trace { cls := `Import, startTime, stopTime := elabStartTime }
|
||||
(.ofFormat "importing") #[]
|
||||
}].toPArray' ++ traceState.traces
|
||||
}
|
||||
let profile ← Firefox.Profile.export mainModuleName.toString startTime traceState opts
|
||||
IO.FS.writeFile ⟨out⟩ <| Json.compress <| toJson profile
|
||||
|
||||
return (s.commandState.env, !s.commandState.messages.hasErrors)
|
||||
|
||||
let ctx := { inputCtx with mainModuleName, opts, trustLevel }
|
||||
let processor := Language.Lean.process
|
||||
let snap ← processor none ctx
|
||||
let snaps := Language.toSnapshotTree snap
|
||||
snaps.runAndReport opts jsonOutput
|
||||
snaps.runAndReport opts
|
||||
if let some ileanFileName := ileanFileName? then
|
||||
let trees := snaps.getAll.concatMap (match ·.infoTree? with | some t => #[t] | _ => #[])
|
||||
let references := Lean.Server.findModuleRefs inputCtx.fileMap trees (localVars := false)
|
||||
|
||||
@@ -4,8 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kyle Miller
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Notation
|
||||
import Lean.Util.Diff
|
||||
import Lean.Server.CodeActions.Attr
|
||||
|
||||
/-! `#guard_msgs` command for testing commands
|
||||
@@ -16,12 +14,6 @@ See the docstring on the `#guard_msgs` command.
|
||||
|
||||
open Lean Parser.Tactic Elab Command
|
||||
|
||||
register_builtin_option guard_msgs.diff : Bool := {
|
||||
defValue := false
|
||||
descr := "When true, show a diff between expected and actual messages if they don't match. "
|
||||
}
|
||||
|
||||
|
||||
namespace Lean.Elab.Tactic.GuardMsgs
|
||||
|
||||
/-- Gives a string representation of a message without source position information.
|
||||
@@ -48,55 +40,31 @@ inductive SpecResult
|
||||
/-- Do not capture the message. -/
|
||||
| passthrough
|
||||
|
||||
/-- The method to use when normalizing whitespace, after trimming. -/
|
||||
inductive WhitespaceMode
|
||||
/-- Exact equality. -/
|
||||
| exact
|
||||
/-- Equality after normalizing newlines into spaces. -/
|
||||
| normalized
|
||||
/-- Equality after collapsing whitespace into single spaces. -/
|
||||
| lax
|
||||
|
||||
/-- Method to use when combining multiple messages. -/
|
||||
inductive MessageOrdering
|
||||
/-- Use the exact ordering of the produced messages. -/
|
||||
| exact
|
||||
/-- Sort the produced messages. -/
|
||||
| sorted
|
||||
|
||||
/-- Parses a `guardMsgsSpec`.
|
||||
- No specification: check everything.
|
||||
- With a specification: interpret the spec, and if nothing applies pass it through. -/
|
||||
def parseGuardMsgsSpec (spec? : Option (TSyntax ``guardMsgsSpec)) :
|
||||
CommandElabM (WhitespaceMode × MessageOrdering × (Message → SpecResult)) := do
|
||||
let elts ←
|
||||
if let some spec := spec? then
|
||||
match spec with
|
||||
| `(guardMsgsSpec| ($[$elts:guardMsgsSpecElt],*)) => pure elts
|
||||
| _ => throwUnsupportedSyntax
|
||||
else
|
||||
pure #[]
|
||||
let mut whitespace : WhitespaceMode := .normalized
|
||||
let mut ordering : MessageOrdering := .exact
|
||||
let mut p? : Option (Message → SpecResult) := none
|
||||
let pushP (s : MessageSeverity) (drop : Bool) (p? : Option (Message → SpecResult))
|
||||
(msg : Message) : SpecResult :=
|
||||
let p := p?.getD fun _ => .passthrough
|
||||
if msg.severity == s then if drop then .drop else .check
|
||||
else p msg
|
||||
for elt in elts.reverse do
|
||||
match elt with
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? info) => p? := pushP .information drop?.isSome p?
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? warning) => p? := pushP .warning drop?.isSome p?
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? error) => p? := pushP .error drop?.isSome p?
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? all) => p? := some fun _ => if drop?.isSome then .drop else .check
|
||||
| `(guardMsgsSpecElt| whitespace := exact) => whitespace := .exact
|
||||
| `(guardMsgsSpecElt| whitespace := normalized) => whitespace := .normalized
|
||||
| `(guardMsgsSpecElt| whitespace := lax) => whitespace := .lax
|
||||
| `(guardMsgsSpecElt| ordering := exact) => ordering := .exact
|
||||
| `(guardMsgsSpecElt| ordering := sorted) => ordering := .sorted
|
||||
| _ => throwUnsupportedSyntax
|
||||
return (whitespace, ordering, p?.getD fun _ => .check)
|
||||
CommandElabM (Message → SpecResult) := do
|
||||
if let some spec := spec? then
|
||||
match spec with
|
||||
| `(guardMsgsSpec| ($[$elts:guardMsgsSpecElt],*)) => do
|
||||
let mut p : Message → SpecResult := fun _ => .passthrough
|
||||
let pushP (s : MessageSeverity) (drop : Bool) (p : Message → SpecResult)
|
||||
(msg : Message) : SpecResult :=
|
||||
if msg.severity == s then if drop then .drop else .check
|
||||
else p msg
|
||||
for elt in elts.reverse do
|
||||
match elt with
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? info) => p := pushP .information drop?.isSome p
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? warning) => p := pushP .warning drop?.isSome p
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? error) => p := pushP .error drop?.isSome p
|
||||
| `(guardMsgsSpecElt| $[drop%$drop?]? all) =>
|
||||
p := fun _ => if drop?.isSome then .drop else .check
|
||||
| _ => throwErrorAt elt "Invalid #guard_msgs specification element"
|
||||
return p
|
||||
| _ => throwErrorAt spec "Invalid #guard_msgs specification"
|
||||
else
|
||||
return fun _ => .check
|
||||
|
||||
/-- An info tree node corresponding to a failed `#guard_msgs` invocation,
|
||||
used for code action support. -/
|
||||
@@ -118,27 +86,16 @@ def removeTrailingWhitespaceMarker (s : String) : String :=
|
||||
s.replace "⏎\n" "\n"
|
||||
|
||||
/--
|
||||
Applies a whitespace normalization mode.
|
||||
Strings are compared up to newlines, to allow users to break long lines.
|
||||
-/
|
||||
def WhitespaceMode.apply (mode : WhitespaceMode) (s : String) : String :=
|
||||
match mode with
|
||||
| .exact => s
|
||||
| .normalized => s.replace "\n" " "
|
||||
| .lax => String.intercalate " " <| (s.split Char.isWhitespace).filter (!·.isEmpty)
|
||||
|
||||
/--
|
||||
Applies a message ordering mode.
|
||||
-/
|
||||
def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List String :=
|
||||
match mode with
|
||||
| .exact => msgs
|
||||
| .sorted => msgs |>.toArray.qsort (· < ·) |>.toList
|
||||
def equalUpToNewlines (exp res : String) : Bool :=
|
||||
exp.replace "\n" " " == res.replace "\n" " "
|
||||
|
||||
@[builtin_command_elab Lean.guardMsgsCmd] def elabGuardMsgs : CommandElab
|
||||
| `(command| $[$dc?:docComment]? #guard_msgs%$tk $(spec?)? in $cmd) => do
|
||||
let expected : String := (← dc?.mapM (getDocStringText ·)).getD ""
|
||||
|>.trim |> removeTrailingWhitespaceMarker
|
||||
let (whitespace, ordering, specFn) ← parseGuardMsgsSpec spec?
|
||||
let specFn ← parseGuardMsgsSpec spec?
|
||||
let initMsgs ← modifyGet fun st => (st.messages, { st with messages := {} })
|
||||
elabCommandTopLevel cmd
|
||||
let msgs := (← get).messages
|
||||
@@ -149,21 +106,14 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
|
||||
| .check => toCheck := toCheck.add msg
|
||||
| .drop => pure ()
|
||||
| .passthrough => toPassthrough := toPassthrough.add msg
|
||||
let strings ← toCheck.toList.mapM (messageToStringWithoutPos ·)
|
||||
let strings := ordering.apply strings
|
||||
let res := "---\n".intercalate strings |>.trim
|
||||
if whitespace.apply expected == whitespace.apply res then
|
||||
let res := "---\n".intercalate (← toCheck.toList.mapM (messageToStringWithoutPos ·)) |>.trim
|
||||
if equalUpToNewlines expected res then
|
||||
-- Passed. Only put toPassthrough messages back on the message log
|
||||
modify fun st => { st with messages := initMsgs ++ toPassthrough }
|
||||
else
|
||||
-- Failed. Put all the messages back on the message log and add an error
|
||||
modify fun st => { st with messages := initMsgs ++ msgs }
|
||||
let feedback :=
|
||||
if (← getOptions).getBool `guard_msgs.diff false then
|
||||
let diff := Diff.diff (expected.split (· == '\n')).toArray (res.split (· == '\n')).toArray
|
||||
Diff.linesToString diff
|
||||
else res
|
||||
logErrorAt tk m!"❌ Docstring on `#guard_msgs` does not match generated message:\n\n{feedback}"
|
||||
logErrorAt tk m!"❌ Docstring on `#guard_msgs` does not match generated message:\n\n{res}"
|
||||
pushInfoLeaf (.ofCustomInfo { stx := ← getRef, value := Dynamic.mk (GuardMsgFailure.mk res) })
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
|
||||
@@ -25,11 +25,6 @@ open Meta
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.inductive
|
||||
|
||||
register_builtin_option inductive.autoPromoteIndices : Bool := {
|
||||
defValue := true
|
||||
descr := "Promote indices to parameters in inductive types whenever possible."
|
||||
}
|
||||
|
||||
def checkValidInductiveModifier [Monad m] [MonadError m] (modifiers : Modifiers) : m Unit := do
|
||||
if modifiers.isNoncomputable then
|
||||
throwError "invalid use of 'noncomputable' in inductive declaration"
|
||||
@@ -719,12 +714,10 @@ private def isDomainDefEq (arrowType : Expr) (type : Expr) : MetaM Bool := do
|
||||
Convert fixed indices to parameters.
|
||||
-/
|
||||
private partial def fixedIndicesToParams (numParams : Nat) (indTypes : Array InductiveType) (indFVars : Array Expr) : MetaM Nat := do
|
||||
if !inductive.autoPromoteIndices.get (← getOptions) then
|
||||
return numParams
|
||||
let masks ← indTypes.mapM (computeFixedIndexBitMask numParams · indFVars)
|
||||
trace[Elab.inductive] "masks: {masks}"
|
||||
if masks.all fun mask => !mask.contains true then
|
||||
return numParams
|
||||
trace[Elab.inductive] "masks: {masks}"
|
||||
-- We process just a non-fixed prefix of the indices for now. Reason: we don't want to change the order.
|
||||
-- TODO: extend it in the future. For example, it should be reasonable to change
|
||||
-- the order of indices generated by the auto implicit feature.
|
||||
|
||||
@@ -76,7 +76,7 @@ structure CommandInfo extends ElabInfo where
|
||||
/-- A completion is an item that appears in the [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense)
|
||||
box that appears as you type. -/
|
||||
inductive CompletionInfo where
|
||||
| dot (termInfo : TermInfo) (expectedType? : Option Expr)
|
||||
| dot (termInfo : TermInfo) (field? : Option Syntax) (expectedType? : Option Expr)
|
||||
| id (stx : Syntax) (id : Name) (danglingDot : Bool) (lctx : LocalContext) (expectedType? : Option Expr)
|
||||
| dotId (stx : Syntax) (id : Name) (lctx : LocalContext) (expectedType? : Option Expr)
|
||||
| fieldId (stx : Syntax) (id : Name) (lctx : LocalContext) (structName : Name)
|
||||
|
||||
@@ -7,7 +7,6 @@ prelude
|
||||
import Lean.Compiler.NoncomputableAttr
|
||||
import Lean.Util.CollectLevelParams
|
||||
import Lean.Meta.AbstractNestedProofs
|
||||
import Lean.Meta.ForEachExpr
|
||||
import Lean.Elab.RecAppSyntax
|
||||
import Lean.Elab.DefView
|
||||
import Lean.Elab.PreDefinition.WF.TerminationHint
|
||||
|
||||
@@ -15,7 +15,7 @@ def elabSetOption (id : Syntax) (val : Syntax) : m Options := do
|
||||
let ref ← getRef
|
||||
-- For completion purposes, we discard `val` and any later arguments.
|
||||
-- We include the first argument (the keyword) for position information in case `id` is `missing`.
|
||||
addCompletionInfo <| CompletionInfo.option (ref.setArgs (ref.getArgs[0:3]))
|
||||
addCompletionInfo <| CompletionInfo.option (ref.setArgs (ref.getArgs[0:2]))
|
||||
let optionName := id.getId.eraseMacroScopes
|
||||
let decl ← IO.toEIO (fun (ex : IO.Error) => Exception.error ref ex.toString) (getOptionDecl optionName)
|
||||
pushInfoLeaf <| .ofOptionInfo { stx := id, optionName, declName := decl.declName }
|
||||
|
||||
@@ -442,4 +442,7 @@ def strLitToPattern (stx: Syntax) : MacroM Syntax :=
|
||||
| some str => return mkAtomFrom stx str
|
||||
| none => Macro.throwUnsupported
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.syntax
|
||||
|
||||
end Lean.Elab.Command
|
||||
|
||||
@@ -147,7 +147,7 @@ partial def evalTactic (stx : Syntax) : TacticM Unit := do
|
||||
if k == nullKind then
|
||||
-- Macro writers create a sequence of tactics `t₁ ... tₙ` using `mkNullNode #[t₁, ..., tₙ]`
|
||||
stx.getArgs.forM evalTactic
|
||||
else withTraceNode `Elab.step (fun _ => return stx) (tag := stx.getKind.toString) do
|
||||
else withTraceNode `Elab.step (fun _ => return stx) do
|
||||
let evalFns := tacticElabAttribute.getEntries (← getEnv) stx.getKind
|
||||
let macros := macroAttribute.getEntries (← getEnv) stx.getKind
|
||||
if evalFns.isEmpty && macros.isEmpty then
|
||||
|
||||
@@ -162,9 +162,9 @@ private def getOptRotation (stx : Syntax) : Nat :=
|
||||
popScope
|
||||
|
||||
@[builtin_tactic Parser.Tactic.set_option] def elabSetOption : Tactic := fun stx => do
|
||||
let options ← Elab.elabSetOption stx[1] stx[3]
|
||||
let options ← Elab.elabSetOption stx[1] stx[2]
|
||||
withTheReader Core.Context (fun ctx => { ctx with maxRecDepth := maxRecDepth.get options, options := options }) do
|
||||
evalTactic stx[5]
|
||||
evalTactic stx[4]
|
||||
|
||||
@[builtin_tactic Parser.Tactic.allGoals] def evalAllGoals : Tactic := fun stx => do
|
||||
let mvarIds ← getGoals
|
||||
|
||||
@@ -36,9 +36,7 @@ partial def falseOrByContra (g : MVarId) (useClassical : Option Bool := none) :
|
||||
match ty with
|
||||
| .const ``False _ => pure g
|
||||
| .forallE _ _ _ _
|
||||
| .app (.const ``Not _) _ =>
|
||||
-- We set the transparency back to default; otherwise this breaks when run by a `simp` discharger.
|
||||
falseOrByContra (← withTransparency default g.intro1P).2 useClassical
|
||||
| .app (.const ``Not _) _ => falseOrByContra (← g.intro1).2
|
||||
| _ =>
|
||||
let gs ← if ← isProp ty then
|
||||
match useClassical with
|
||||
@@ -58,7 +56,7 @@ partial def falseOrByContra (g : MVarId) (useClassical : Option Bool := none) :
|
||||
let [g] ← g.applyConst ``False.elim | panic! "expected one sugoal"
|
||||
pure g
|
||||
|
||||
@[builtin_tactic Lean.Parser.Tactic.falseOrByContra]
|
||||
@[builtin_tactic falseOrByContra]
|
||||
def elabFalseOrByContra : Tactic
|
||||
| `(tactic| false_or_by_contra) => do liftMetaTactic1 (falseOrByContra ·)
|
||||
| _ => no_error_if_unused% throwUnsupportedSyntax
|
||||
|
||||
@@ -59,7 +59,7 @@ def evalAlt (mvarId : MVarId) (alt : Syntax) (addInfo : TermElabM Unit) (remaini
|
||||
withCaseRef (getAltDArrow alt) rhs do
|
||||
if isHoleRHS rhs then
|
||||
addInfo
|
||||
let gs' ← mvarId.withContext <| withTacticInfoContext rhs do
|
||||
let gs' ← mvarId.withContext <| withRef rhs do
|
||||
let mvarDecl ← mvarId.getDecl
|
||||
let val ← elabTermEnsuringType rhs mvarDecl.type
|
||||
mvarId.assign val
|
||||
|
||||
@@ -526,82 +526,6 @@ def cases₂ (mvarId : MVarId) (p : Expr) (hName : Name := `h) :
|
||||
| throwError "'cases' tactic failed, unexpected new hypothesis"
|
||||
return ((s₁.mvarId, f₁), (s₂.mvarId, f₂))
|
||||
|
||||
/--
|
||||
Helpful error message when omega cannot find a solution
|
||||
-/
|
||||
def formatErrorMessage (p : Problem) : OmegaM MessageData := do
|
||||
if p.possible then
|
||||
if p.isEmpty then
|
||||
return m!"it is false"
|
||||
else
|
||||
let as ← atoms
|
||||
let mask ← mentioned p.constraints
|
||||
let names ← varNames mask
|
||||
return m!"a possible counterexample may satisfy the constraints\n" ++
|
||||
m!"{prettyConstraints names p.constraints}\nwhere\n{prettyAtoms names as mask}"
|
||||
else
|
||||
-- formatErrorMessage should not be used in this case
|
||||
return "it is trivially solvable"
|
||||
where
|
||||
varNameOf (i : Nat) : String :=
|
||||
let c : Char := .ofNat ('a'.toNat + (i % 26))
|
||||
let suffix := if i < 26 then "" else s!"_{i / 26}"
|
||||
s!"{c}{suffix}"
|
||||
|
||||
inScope (s : String) : MetaM Bool := do
|
||||
let n := .mkSimple s
|
||||
if (← resolveGlobalName n).isEmpty then
|
||||
if ((← getLCtx).findFromUserName? n).isNone then
|
||||
return false
|
||||
return true
|
||||
|
||||
-- Assign ascending names a, b, c, …, z, a1 … to all atoms mentioned according to the mask
|
||||
-- but avoid names in the local or global scope
|
||||
varNames (mask : Array Bool) : MetaM (Array String) := do
|
||||
let mut names := #[]
|
||||
let mut next := 0
|
||||
for h : i in [:mask.size] do
|
||||
if mask[i] then
|
||||
while ← inScope (varNameOf next) do next := next + 1
|
||||
names := names.push (varNameOf next)
|
||||
next := next + 1
|
||||
else
|
||||
names := names.push "(masked)"
|
||||
return names
|
||||
|
||||
prettyConstraints (names : Array String) (constraints : HashMap Coeffs Fact) : String :=
|
||||
constraints.toList
|
||||
|>.map (fun ⟨coeffs, ⟨_, cst, _⟩⟩ => " " ++ prettyConstraint (prettyCoeffs names coeffs) cst)
|
||||
|> "\n".intercalate
|
||||
|
||||
prettyConstraint (e : String) : Constraint → String
|
||||
| ⟨none, none⟩ => s!"{e} is unconstrained" -- should not happen in error messages
|
||||
| ⟨none, some y⟩ => s!"{e} ≤ {y}"
|
||||
| ⟨some x, none⟩ => s!"{e} ≥ {x}"
|
||||
| ⟨some x, some y⟩ =>
|
||||
if y < x then "∅" else -- should not happen in error messages
|
||||
s!"{x} ≤ {e} ≤ {y}"
|
||||
|
||||
prettyCoeffs (names : Array String) (coeffs : Coeffs) : String :=
|
||||
coeffs.toList.enum
|
||||
|>.filter (fun (_,c) => c ≠ 0)
|
||||
|>.enum
|
||||
|>.map (fun (j, (i,c)) =>
|
||||
(if j > 0 then if c > 0 then " + " else " - " else if c > 0 then "" else "- ") ++
|
||||
(if Int.natAbs c = 1 then names[i]! else s!"{c.natAbs}*{names[i]!}"))
|
||||
|> String.join
|
||||
|
||||
mentioned (constraints : HashMap Coeffs Fact) : OmegaM (Array Bool) := do
|
||||
let initMask := Array.mkArray (← getThe State).atoms.size false
|
||||
return constraints.fold (init := initMask) fun mask coeffs _ =>
|
||||
coeffs.enum.foldl (init := mask) fun mask (i, c) =>
|
||||
if c = 0 then mask else mask.set! i true
|
||||
|
||||
prettyAtoms (names : Array String) (atoms : Array Expr) (mask : Array Bool) : MessageData :=
|
||||
(Array.zip names atoms).toList.enum
|
||||
|>.filter (fun (i, _) => mask.getD i false)
|
||||
|>.map (fun (_, (n, a)) => m!" {n} := {a}")
|
||||
|> m!"\n".joinSep
|
||||
|
||||
mutual
|
||||
|
||||
@@ -611,7 +535,7 @@ call `omegaImpl` in both branches.
|
||||
-/
|
||||
partial def splitDisjunction (m : MetaProblem) (g : MVarId) : OmegaM Unit := g.withContext do
|
||||
match m.disjunctions with
|
||||
| [] => throwError "omega could not prove the goal:\n{← formatErrorMessage m.problem}"
|
||||
| [] => throwError "omega did not find a contradiction:\n{m.problem}"
|
||||
| h :: t =>
|
||||
trace[omega] "Case splitting on {← inferType h}"
|
||||
let ctx ← getMCtx
|
||||
|
||||
@@ -348,7 +348,7 @@ partial def rcasesCore (g : MVarId) (fs : FVarSubst) (clears : Array FVarId) (e
|
||||
pure ([(n, ps)], #[⟨⟨g, #[mkFVar v], fs'⟩, n⟩])
|
||||
| ConstantInfo.inductInfo info, _ => do
|
||||
let (altVarNames, r) ← processConstructors pat.ref info.numParams #[] info.ctors pat.asAlts
|
||||
(r, ·) <$> g.cases e.fvarId! altVarNames (useNatCasesAuxOn := true)
|
||||
(r, ·) <$> g.cases e.fvarId! altVarNames
|
||||
| _, _ => failK ()
|
||||
(·.2) <$> subgoals.foldlM (init := (r, a)) fun (r, a) ⟨goal, ctorName⟩ => do
|
||||
let rec
|
||||
|
||||
@@ -21,12 +21,12 @@ open Meta
|
||||
throwErrorAt stx[2] "'split' tactic failed, select a single target to split"
|
||||
if simplifyTarget then
|
||||
liftMetaTactic fun mvarId => do
|
||||
let some mvarIds ← splitTarget? mvarId | Meta.throwTacticEx `split mvarId
|
||||
let some mvarIds ← splitTarget? mvarId | Meta.throwTacticEx `split mvarId ""
|
||||
return mvarIds
|
||||
else
|
||||
let fvarId ← getFVarId hyps[0]!
|
||||
liftMetaTactic fun mvarId => do
|
||||
let some mvarIds ← splitLocalDecl? mvarId fvarId | Meta.throwTacticEx `split mvarId
|
||||
let some mvarIds ← splitLocalDecl? mvarId fvarId | Meta.throwTacticEx `split mvarId ""
|
||||
return mvarIds
|
||||
| Location.wildcard =>
|
||||
liftMetaTactic fun mvarId => do
|
||||
@@ -34,7 +34,7 @@ open Meta
|
||||
for fvarId in fvarIds do
|
||||
if let some mvarIds ← splitLocalDecl? mvarId fvarId then
|
||||
return mvarIds
|
||||
let some mvarIds ← splitTarget? mvarId | Meta.throwTacticEx `split mvarId
|
||||
let some mvarIds ← splitTarget? mvarId | Meta.throwTacticEx `split mvarId ""
|
||||
return mvarIds
|
||||
|
||||
end Lean.Elab.Tactic
|
||||
|
||||
@@ -261,14 +261,6 @@ def SavedState.restore (s : SavedState) (restoreInfo : Bool := false) : TermElab
|
||||
unless restoreInfo do
|
||||
setInfoState infoState
|
||||
|
||||
/--
|
||||
Restores full state including sources for unique identifiers. Only intended for incremental reuse
|
||||
between elaboration runs, not for backtracking within a single run.
|
||||
-/
|
||||
def SavedState.restoreFull (s : SavedState) : TermElabM Unit := do
|
||||
s.meta.restoreFull
|
||||
set s.elab
|
||||
|
||||
instance : MonadBacktrack SavedState TermElabM where
|
||||
saveState := Term.saveState
|
||||
restoreState b := b.restore
|
||||
@@ -362,8 +354,8 @@ builtin_initialize termElabAttribute : KeyedDeclsAttribute TermElab ← mkTermEl
|
||||
inductive LVal where
|
||||
| fieldIdx (ref : Syntax) (i : Nat)
|
||||
/-- Field `suffix?` is for producing better error messages because `x.y` may be a field access or a hierarchical/composite name.
|
||||
`ref` is the syntax object representing the field. `fullRef` includes the LHS. -/
|
||||
| fieldName (ref : Syntax) (name : String) (suffix? : Option Name) (fullRef : Syntax)
|
||||
`ref` is the syntax object representing the field. `targetStx` is the target object being accessed. -/
|
||||
| fieldName (ref : Syntax) (name : String) (suffix? : Option Name) (targetStx : Syntax)
|
||||
|
||||
def LVal.getRef : LVal → Syntax
|
||||
| .fieldIdx ref _ => ref
|
||||
@@ -1387,8 +1379,7 @@ where
|
||||
private partial def elabTermAux (expectedType? : Option Expr) (catchExPostpone : Bool) (implicitLambda : Bool) : Syntax → TermElabM Expr
|
||||
| .missing => mkSyntheticSorryFor expectedType?
|
||||
| stx => withFreshMacroScope <| withIncRecDepth do
|
||||
withTraceNode `Elab.step (fun _ => return m!"expected type: {expectedType?}, term\n{stx}")
|
||||
(tag := stx.getKind.toString) do
|
||||
withTraceNode `Elab.step (fun _ => return m!"expected type: {expectedType?}, term\n{stx}") do
|
||||
checkSystem "elaborator"
|
||||
let env ← getEnv
|
||||
let result ← match (← liftMacroM (expandMacroImpl? env stx)) with
|
||||
@@ -1418,9 +1409,9 @@ private partial def elabTermAux (expectedType? : Option Expr) (catchExPostpone :
|
||||
trace[Elab.step.result] result
|
||||
pure result
|
||||
|
||||
/-- Store in the `InfoTree` that `e` is a "dot"-completion target. `stx` should cover the entire term. -/
|
||||
def addDotCompletionInfo (stx : Syntax) (e : Expr) (expectedType? : Option Expr) : TermElabM Unit := do
|
||||
addCompletionInfo <| CompletionInfo.dot { expr := e, stx, lctx := (← getLCtx), elaborator := .anonymous, expectedType? } (expectedType? := expectedType?)
|
||||
/-- Store in the `InfoTree` that `e` is a "dot"-completion target. -/
|
||||
def addDotCompletionInfo (stx : Syntax) (e : Expr) (expectedType? : Option Expr) (field? : Option Syntax := none) : TermElabM Unit := do
|
||||
addCompletionInfo <| CompletionInfo.dot { expr := e, stx, lctx := (← getLCtx), elaborator := .anonymous, expectedType? } (field? := field?) (expectedType? := expectedType?)
|
||||
|
||||
/--
|
||||
Main function for elaborating terms.
|
||||
@@ -1766,7 +1757,6 @@ builtin_initialize
|
||||
registerTraceClass `Elab.postpone
|
||||
registerTraceClass `Elab.coe
|
||||
registerTraceClass `Elab.debug
|
||||
registerTraceClass `Elab.reuse
|
||||
|
||||
export Term (TermElabM)
|
||||
|
||||
|
||||
@@ -114,7 +114,10 @@ unsafe def mkElabAttribute (γ) (attrBuiltinName attrName : Name) (parserNamespa
|
||||
return kind
|
||||
onAdded := fun builtin declName => do
|
||||
if builtin then
|
||||
declareBuiltinDocStringAndRanges declName
|
||||
if let some doc ← findDocString? (← getEnv) declName (includeBuiltin := false) then
|
||||
declareBuiltin (declName ++ `docString) (mkAppN (mkConst ``addBuiltinDocString) #[toExpr declName, toExpr doc])
|
||||
if let some declRanges ← findDeclarationRanges? declName then
|
||||
declareBuiltin (declName ++ `declRange) (mkAppN (mkConst ``addBuiltinDeclarationRanges) #[toExpr declName, toExpr declRanges])
|
||||
} attrDeclName
|
||||
|
||||
unsafe def mkMacroAttributeUnsafe (ref : Name) : IO (KeyedDeclsAttribute Macro) :=
|
||||
|
||||
@@ -6,7 +6,6 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Hashable
|
||||
import Lean.Data.KVMap
|
||||
import Lean.Data.SMap
|
||||
import Lean.Level
|
||||
|
||||
namespace Lean
|
||||
@@ -1390,8 +1389,6 @@ def mkDecIsFalse (pred proof : Expr) :=
|
||||
|
||||
abbrev ExprMap (α : Type) := HashMap Expr α
|
||||
abbrev PersistentExprMap (α : Type) := PHashMap Expr α
|
||||
abbrev SExprMap (α : Type) := SMap Expr α
|
||||
|
||||
abbrev ExprSet := HashSet Expr
|
||||
abbrev PersistentExprSet := PHashSet Expr
|
||||
abbrev PExprSet := PersistentExprSet
|
||||
@@ -2022,46 +2019,17 @@ def mkEM (p : Expr) : Expr := mkApp (mkConst ``Classical.em) p
|
||||
/-- Return `p ↔ q` -/
|
||||
def mkIff (p q : Expr) : Expr := mkApp2 (mkConst ``Iff) p q
|
||||
|
||||
/-! Constants for Nat typeclasses. -/
|
||||
namespace Nat
|
||||
|
||||
protected def mkType : Expr := mkConst ``Nat
|
||||
|
||||
def mkInstAdd : Expr := mkConst ``instAddNat
|
||||
def mkInstHAdd : Expr := mkApp2 (mkConst ``instHAdd [levelZero]) Nat.mkType mkInstAdd
|
||||
|
||||
def mkInstSub : Expr := mkConst ``instSubNat
|
||||
def mkInstHSub : Expr := mkApp2 (mkConst ``instHSub [levelZero]) Nat.mkType mkInstSub
|
||||
|
||||
def mkInstMul : Expr := mkConst ``instMulNat
|
||||
def mkInstHMul : Expr := mkApp2 (mkConst ``instHMul [levelZero]) Nat.mkType mkInstMul
|
||||
|
||||
def mkInstDiv : Expr := mkConst ``Nat.instDiv
|
||||
def mkInstHDiv : Expr := mkApp2 (mkConst ``instHDiv [levelZero]) Nat.mkType mkInstDiv
|
||||
|
||||
def mkInstMod : Expr := mkConst ``Nat.instMod
|
||||
def mkInstHMod : Expr := mkApp2 (mkConst ``instHMod [levelZero]) Nat.mkType mkInstMod
|
||||
|
||||
def mkInstNatPow : Expr := mkConst ``instNatPowNat
|
||||
def mkInstPow : Expr := mkApp2 (mkConst ``instPowNat [levelZero]) Nat.mkType mkInstNatPow
|
||||
def mkInstHPow : Expr := mkApp3 (mkConst ``instHPow [levelZero, levelZero]) Nat.mkType Nat.mkType mkInstPow
|
||||
|
||||
def mkInstLT : Expr := mkConst ``instLTNat
|
||||
def mkInstLE : Expr := mkConst ``instLENat
|
||||
|
||||
end Nat
|
||||
|
||||
private def natAddFn : Expr :=
|
||||
let nat := mkConst ``Nat
|
||||
mkApp4 (mkConst ``HAdd.hAdd [0, 0, 0]) nat nat nat Nat.mkInstHAdd
|
||||
mkApp4 (mkConst ``HAdd.hAdd [0, 0, 0]) nat nat nat (mkApp2 (mkConst ``instHAdd [0]) nat (mkConst ``instAddNat))
|
||||
|
||||
private def natSubFn : Expr :=
|
||||
let nat := mkConst ``Nat
|
||||
mkApp4 (mkConst ``HSub.hSub [0, 0, 0]) nat nat nat Nat.mkInstHSub
|
||||
mkApp4 (mkConst ``HSub.hSub [0, 0, 0]) nat nat nat (mkApp2 (mkConst ``instHSub [0]) nat (mkConst ``instSubNat))
|
||||
|
||||
private def natMulFn : Expr :=
|
||||
let nat := mkConst ``Nat
|
||||
mkApp4 (mkConst ``HMul.hMul [0, 0, 0]) nat nat nat Nat.mkInstHMul
|
||||
mkApp4 (mkConst ``HMul.hMul [0, 0, 0]) nat nat nat (mkApp2 (mkConst ``instHMul [0]) nat (mkConst ``instMulNat))
|
||||
|
||||
/-- Given `a : Nat`, returns `Nat.succ a` -/
|
||||
def mkNatSucc (a : Expr) : Expr :=
|
||||
@@ -2080,7 +2048,7 @@ def mkNatMul (a b : Expr) : Expr :=
|
||||
mkApp2 natMulFn a b
|
||||
|
||||
private def natLEPred : Expr :=
|
||||
mkApp2 (mkConst ``LE.le [0]) (mkConst ``Nat) Nat.mkInstLE
|
||||
mkApp2 (mkConst ``LE.le [0]) (mkConst ``Nat) (mkConst ``instLENat)
|
||||
|
||||
/-- Given `a b : Nat`, return `a ≤ b` -/
|
||||
def mkNatLE (a b : Expr) : Expr :=
|
||||
|
||||
@@ -9,7 +9,6 @@ Authors: Sebastian Ullrich
|
||||
-/
|
||||
|
||||
prelude
|
||||
import Init.System.Promise
|
||||
import Lean.Message
|
||||
import Lean.Parser.Types
|
||||
|
||||
@@ -59,26 +58,23 @@ deriving Inhabited
|
||||
-- cursor position. This may require starting the tasks suspended (e.g. in `Thunk`). The server may
|
||||
-- also need more dependency information for this in order to avoid priority inversion.
|
||||
structure SnapshotTask (α : Type) where
|
||||
/--
|
||||
Range that is marked as being processed by the server while the task is running. If `none`,
|
||||
the range of the outer task if some or else the entire file is reported.
|
||||
-/
|
||||
range? : Option String.Range
|
||||
/-- Range that is marked as being processed by the server while the task is running. -/
|
||||
range : String.Range
|
||||
/-- Underlying task producing the snapshot. -/
|
||||
task : Task α
|
||||
deriving Nonempty
|
||||
|
||||
/-- Creates a snapshot task from a reporting range and a `BaseIO` action. -/
|
||||
def SnapshotTask.ofIO (range? : Option String.Range) (act : BaseIO α) : BaseIO (SnapshotTask α) := do
|
||||
def SnapshotTask.ofIO (range : String.Range) (act : BaseIO α) : BaseIO (SnapshotTask α) := do
|
||||
return {
|
||||
range?
|
||||
range
|
||||
task := (← BaseIO.asTask act)
|
||||
}
|
||||
|
||||
/-- Creates a finished snapshot task. -/
|
||||
def SnapshotTask.pure (a : α) : SnapshotTask α where
|
||||
-- irrelevant when already finished
|
||||
range? := none
|
||||
range := default
|
||||
task := .pure a
|
||||
|
||||
/--
|
||||
@@ -88,26 +84,23 @@ def SnapshotTask.cancel (t : SnapshotTask α) : BaseIO Unit :=
|
||||
IO.cancel t.task
|
||||
|
||||
/-- Transforms a task's output without changing the reporting range. -/
|
||||
def SnapshotTask.map (t : SnapshotTask α) (f : α → β) (range? : Option String.Range := t.range?)
|
||||
def SnapshotTask.map (t : SnapshotTask α) (f : α → β) (range : String.Range := t.range)
|
||||
(sync := false) : SnapshotTask β :=
|
||||
{ range?, task := t.task.map (sync := sync) f }
|
||||
{ range, task := t.task.map (sync := sync) f }
|
||||
|
||||
/--
|
||||
Chains two snapshot tasks. The range is taken from the first task if not specified; the range of
|
||||
the second task is discarded. -/
|
||||
def SnapshotTask.bind (t : SnapshotTask α) (act : α → SnapshotTask β)
|
||||
(range? : Option String.Range := t.range?) (sync := false) : SnapshotTask β :=
|
||||
{ range?, task := t.task.bind (sync := sync) (act · |>.task) }
|
||||
(range : String.Range := t.range) (sync := false) : SnapshotTask β :=
|
||||
{ range, task := t.task.bind (sync := sync) (act · |>.task) }
|
||||
|
||||
/--
|
||||
Chains two snapshot tasks. The range is taken from the first task if not specified; the range of
|
||||
the second task is discarded. -/
|
||||
def SnapshotTask.bindIO (t : SnapshotTask α) (act : α → BaseIO (SnapshotTask β))
|
||||
(range? : Option String.Range := t.range?) (sync := false) : BaseIO (SnapshotTask β) :=
|
||||
return {
|
||||
range?
|
||||
task := (← BaseIO.bindTask (sync := sync) t.task fun a => (·.task) <$> (act a))
|
||||
}
|
||||
(range : String.Range := t.range) (sync := false) : BaseIO (SnapshotTask β) :=
|
||||
return { range, task := (← BaseIO.bindTask (sync := sync) t.task fun a => (·.task) <$> (act a)) }
|
||||
|
||||
/-- Synchronously waits on the result of the task. -/
|
||||
def SnapshotTask.get (t : SnapshotTask α) : α :=
|
||||
@@ -117,40 +110,6 @@ def SnapshotTask.get (t : SnapshotTask α) : α :=
|
||||
def SnapshotTask.get? (t : SnapshotTask α) : BaseIO (Option α) :=
|
||||
return if (← IO.hasFinished t.task) then some t.task.get else none
|
||||
|
||||
/--
|
||||
Arbitrary value paired with a syntax that should be inspected when considering the value for reuse.
|
||||
-/
|
||||
structure SyntaxGuarded (α : Type) where
|
||||
/-- Syntax to be inspected for reuse. -/
|
||||
stx : Syntax
|
||||
/-- Potentially reusable value. -/
|
||||
val : α
|
||||
|
||||
/--
|
||||
Pair of (optional) old snapshot task usable for incremental reuse and new snapshot promise for
|
||||
incremental reporting. Inside the elaborator, we build snapshots by carrying such bundles and then
|
||||
checking if we can reuse `old?` if set or else redoing the corresponding elaboration step. In either
|
||||
case, we derive new bundles for nested snapshots, if any, and finally `resolve` `new` to the result.
|
||||
|
||||
Note that failing to `resolve` a created promise will block the language server indefinitely!
|
||||
Corresponding `IO.Promise.new` calls should come with a "definitely resolved in ..." comment
|
||||
explaining how this is avoided in each case.
|
||||
|
||||
In the future, the 1-element history `old?` may be replaced with a global cache indexed by strong
|
||||
hashes but the promise will still need to be passed through the elaborator.
|
||||
-/
|
||||
structure SnapshotBundle (α : Type) where
|
||||
/--
|
||||
Snapshot task of corresponding elaboration in previous document version if any, paired with its
|
||||
old syntax to be considered for reuse. Should be set to `none` as soon as reuse can be ruled out.
|
||||
-/
|
||||
old? : Option (SyntaxGuarded (SnapshotTask α))
|
||||
/--
|
||||
Promise of snapshot value for the current document. When resolved, the language server will
|
||||
report its result even before the current elaborator invocation has finished.
|
||||
-/
|
||||
new : IO.Promise α
|
||||
|
||||
/--
|
||||
Tree of snapshots where each snapshot comes with an array of asynchronous further subtrees. Used
|
||||
for asynchronously collecting information about the entirety of snapshots in the language server.
|
||||
@@ -159,7 +118,7 @@ structure SnapshotBundle (α : Type) where
|
||||
inductive SnapshotTree where
|
||||
/-- Creates a snapshot tree node. -/
|
||||
| mk (element : Snapshot) (children : Array (SnapshotTask SnapshotTree))
|
||||
deriving Inhabited
|
||||
deriving Nonempty
|
||||
|
||||
/-- The immediately available element of the snapshot tree node. -/
|
||||
abbrev SnapshotTree.element : SnapshotTree → Snapshot
|
||||
@@ -176,49 +135,6 @@ class ToSnapshotTree (α : Type) where
|
||||
toSnapshotTree : α → SnapshotTree
|
||||
export ToSnapshotTree (toSnapshotTree)
|
||||
|
||||
instance [ToSnapshotTree α] : ToSnapshotTree (Option α) where
|
||||
toSnapshotTree
|
||||
| some a => toSnapshotTree a
|
||||
| none => default
|
||||
|
||||
/-- Snapshot type without child nodes. -/
|
||||
structure SnapshotLeaf extends Snapshot
|
||||
deriving Nonempty, TypeName
|
||||
|
||||
instance : ToSnapshotTree SnapshotLeaf where
|
||||
toSnapshotTree s := SnapshotTree.mk s.toSnapshot #[]
|
||||
|
||||
/-- Arbitrary snapshot type, used for extensibility. -/
|
||||
structure DynamicSnapshot where
|
||||
/-- Concrete snapshot value as `Dynamic`. -/
|
||||
val : Dynamic
|
||||
/-- Snapshot tree retrieved from `val` before erasure. -/
|
||||
tree : SnapshotTree
|
||||
deriving Nonempty
|
||||
|
||||
instance : ToSnapshotTree DynamicSnapshot where
|
||||
toSnapshotTree s := s.tree
|
||||
|
||||
/-- Creates a `DynamicSnapshot` from a typed snapshot value. -/
|
||||
def DynamicSnapshot.ofTyped [TypeName α] [ToSnapshotTree α] (val : α) : DynamicSnapshot where
|
||||
val := .mk val
|
||||
tree := ToSnapshotTree.toSnapshotTree val
|
||||
|
||||
/-- Returns the original snapshot value if it is of the given type. -/
|
||||
def DynamicSnapshot.toTyped? (α : Type) [TypeName α] (snap : DynamicSnapshot) :
|
||||
Option α :=
|
||||
snap.val.get? α
|
||||
|
||||
/--
|
||||
Runs a tree of snapshots to conclusion, incrementally performing `f` on each snapshot in tree
|
||||
preorder. -/
|
||||
@[specialize] partial def SnapshotTree.forM [Monad m] (s : SnapshotTree)
|
||||
(f : Snapshot → m PUnit) : m PUnit := do
|
||||
match s with
|
||||
| mk element children =>
|
||||
f element
|
||||
children.forM (·.get.forM f)
|
||||
|
||||
/--
|
||||
Option for printing end position of each message in addition to start position. Used for testing
|
||||
message ranges in the test suite. -/
|
||||
@@ -226,24 +142,25 @@ register_builtin_option printMessageEndPos : Bool := {
|
||||
defValue := false, descr := "print end position of each message in addition to start position"
|
||||
}
|
||||
|
||||
/-- Reports messages on stdout. If `json` is true, prints messages as JSON (one per line). -/
|
||||
def reportMessages (msgLog : MessageLog) (opts : Options) (json := false) : IO Unit := do
|
||||
if json then
|
||||
msgLog.forM (·.toJson <&> (·.compress) >>= IO.println)
|
||||
else
|
||||
msgLog.forM (·.toString (includeEndPos := printMessageEndPos.get opts) >>= IO.print)
|
||||
|
||||
/--
|
||||
Runs a tree of snapshots to conclusion and incrementally report messages on stdout. Messages are
|
||||
reported in tree preorder.
|
||||
This function is used by the cmdline driver; see `Lean.Server.FileWorker.reportSnapshots` for how
|
||||
the language server reports snapshots asynchronously. -/
|
||||
def SnapshotTree.runAndReport (s : SnapshotTree) (opts : Options) (json := false) : IO Unit := do
|
||||
s.forM (reportMessages ·.diagnostics.msgLog opts json)
|
||||
partial def SnapshotTree.runAndReport (s : SnapshotTree) (opts : Options) : IO Unit := do
|
||||
s.element.diagnostics.msgLog.forM
|
||||
(·.toString (includeEndPos := printMessageEndPos.get opts) >>= IO.print)
|
||||
for t in s.children do
|
||||
t.get.runAndReport opts
|
||||
|
||||
/-- Waits on and returns all snapshots in the tree. -/
|
||||
def SnapshotTree.getAll (s : SnapshotTree) : Array Snapshot :=
|
||||
s.forM (m := StateM _) (fun s => modify (·.push s)) |>.run #[] |>.2
|
||||
partial def SnapshotTree.getAll (s : SnapshotTree) : Array Snapshot :=
|
||||
go s |>.run #[] |>.2
|
||||
where
|
||||
go s : StateM (Array Snapshot) Unit := do
|
||||
modify (·.push s.element)
|
||||
for t in s.children do
|
||||
go t.get
|
||||
|
||||
/-- Metadata that does not change during the lifetime of the language processing process. -/
|
||||
structure ModuleProcessingContext where
|
||||
@@ -270,7 +187,7 @@ Creates snapshot message log from non-interactive message log, also allocating a
|
||||
that can be used by the server to memorize interactive diagnostics derived from the log.
|
||||
-/
|
||||
def Snapshot.Diagnostics.ofMessageLog (msgLog : Lean.MessageLog) :
|
||||
BaseIO Snapshot.Diagnostics := do
|
||||
ProcessingM Snapshot.Diagnostics := do
|
||||
return { msgLog, interactiveDiagsRef? := some (← IO.mkRef none) }
|
||||
|
||||
/-- Creates diagnostics from a single error message that should span the whole file. -/
|
||||
@@ -296,7 +213,7 @@ end Language
|
||||
/--
|
||||
Builds a function for processing a language using incremental snapshots by passing the previous
|
||||
snapshot to `Language.process` on subsequent invocations. -/
|
||||
def Language.mkIncrementalProcessor (process : Option InitSnap → ProcessingM InitSnap)
|
||||
partial def Language.mkIncrementalProcessor (process : Option InitSnap → ProcessingM InitSnap)
|
||||
(ctx : ModuleProcessingContext) : BaseIO (Parser.InputContext → BaseIO InitSnap) := do
|
||||
let oldRef ← IO.mkRef none
|
||||
return fun ictx => do
|
||||
|
||||
@@ -58,51 +58,6 @@ exist currently and likely it could at best be approximated by e.g. "furthest `t
|
||||
we remain at "go two commands up" at this point.
|
||||
-/
|
||||
|
||||
/-!
|
||||
# Note [Incremental Command Elaboration]
|
||||
|
||||
Because of Lean's use of persistent data structures, incremental reuse of fully elaborated commands
|
||||
is easy because we can simply snapshot the entire state after each command and then restart
|
||||
elaboration using the stored state at the point of change. However, incrementality within
|
||||
elaboration of a single command such as between tactic steps is much harder because we cannot simply
|
||||
return from those points to the language processor in a way that we can later resume from there.
|
||||
Instead, we exchange the need for continuations with some limited mutability: by allocating an
|
||||
`IO.Promise` "cell" in the language processor, we can both pass it to the elaborator to eventually
|
||||
fill it using `Promise.resolve` as well as convert it to a `Task` that will wait on that resolution
|
||||
using `Promise.result` and return it as part of the command snapshot created by the language
|
||||
processor. The elaborator can then create new promises itself and store their `result` when
|
||||
resolving an outer promise to create an arbitrary tree of promise-backed snapshot tasks. Thus, we
|
||||
can enable incremental reporting and reuse inside the elaborator using the same snapshot tree data
|
||||
structures as outside without having to change the elaborator's control flow.
|
||||
|
||||
While ideally we would decide what can be reused during command elaboration using strong hashes over
|
||||
the state and inputs, currently we rely on simpler syntactic checks: if all the syntax inspected up
|
||||
to a certain point is unchanged, we can assume that the old state can be reused. The central
|
||||
`SnapshotBundle` type passed inwards through the elaborator for this purpose combines the following
|
||||
data:
|
||||
* the `IO.Promise` to be resolved to an elaborator snapshot (whose type depends on the specific
|
||||
elaborator part we're in, e.g. `)
|
||||
* if there was a previous run:
|
||||
* a `SnapshotTask` holding the corresponding snapshot of the run
|
||||
* the relevant `Syntax` of the previous run to be compared before any reuse
|
||||
|
||||
Note that as we do not wait for the previous run to finish before starting to elaborate the next
|
||||
one, the `SnapshotTask` task may not be finished yet. Indeed, if we do find that we can reuse the
|
||||
contained state, we will want to explicitly wait for it instead of redoing the work. On the other
|
||||
hand, the `Syntax` is not surrounded by a task so that we can immediately access it for comparisons,
|
||||
even if the snapshot task may, eventually, give access to the same syntax tree.
|
||||
|
||||
TODO: tactic examples
|
||||
|
||||
While it is generally true that we can provide incremental reporting even without reuse, we
|
||||
generally want to avoid that when it would be confusing/annoying, e.g. when a tactic block is run
|
||||
multiple times because otherwise the progress bar would snap back and forth multiple times. For this
|
||||
purpose, we can disable both incremental modes using `Term.withoutTacticIncrementality`, assuming we
|
||||
opted into incrementality because of other parts of the combinator. `induction` is an example of
|
||||
this because there are some induction alternatives that are run multiple times, so we disable all of
|
||||
incrementality for them.
|
||||
-/
|
||||
|
||||
set_option linter.missingDocs true
|
||||
|
||||
namespace Lean.Language.Lean
|
||||
@@ -129,31 +84,34 @@ register_builtin_option showPartialSyntaxErrors : Bool := {
|
||||
|
||||
/-! The hierarchy of Lean snapshot types -/
|
||||
|
||||
/-- Snapshot after elaboration of the entire command. -/
|
||||
structure CommandFinishedSnapshot extends Language.Snapshot where
|
||||
/-- Final state of processing of a command. -/
|
||||
structure CommandFinishedSnapshot extends Snapshot where
|
||||
/-- Resulting elaboration state. -/
|
||||
cmdState : Command.State
|
||||
deriving Nonempty
|
||||
instance : ToSnapshotTree CommandFinishedSnapshot where
|
||||
toSnapshotTree s := ⟨s.toSnapshot, #[]⟩
|
||||
|
||||
/--
|
||||
State after processing a command's signature and before executing its tactic body, if any. Other
|
||||
commands should immediately proceed to `finished`. -/
|
||||
-- TODO: tactics
|
||||
structure CommandSignatureProcessedSnapshot extends Snapshot where
|
||||
/-- State after processing is finished. -/
|
||||
finishedSnap : SnapshotTask CommandFinishedSnapshot
|
||||
deriving Nonempty
|
||||
instance : ToSnapshotTree CommandSignatureProcessedSnapshot where
|
||||
toSnapshotTree s := ⟨s.toSnapshot, #[s.finishedSnap.map (sync := true) toSnapshotTree]⟩
|
||||
|
||||
/-- State after a command has been parsed. -/
|
||||
structure CommandParsedSnapshotData extends Snapshot where
|
||||
/-- Syntax tree of the command. -/
|
||||
stx : Syntax
|
||||
/-- Resulting parser state. -/
|
||||
parserState : Parser.ModuleParserState
|
||||
/--
|
||||
Snapshot for incremental reporting and reuse during elaboration, type dependent on specific
|
||||
elaborator.
|
||||
-/
|
||||
elabSnap : SnapshotTask DynamicSnapshot
|
||||
/-- State after processing is finished. -/
|
||||
finishedSnap : SnapshotTask CommandFinishedSnapshot
|
||||
/-- Cache for `save`; to be replaced with incrementality. -/
|
||||
tacticCache : IO.Ref Tactic.Cache
|
||||
/-- Signature processing task. -/
|
||||
sigSnap : SnapshotTask CommandSignatureProcessedSnapshot
|
||||
deriving Nonempty
|
||||
|
||||
/-- State after a command has been parsed. -/
|
||||
-- workaround for lack of recursive structures
|
||||
inductive CommandParsedSnapshot where
|
||||
@@ -165,23 +123,22 @@ deriving Nonempty
|
||||
abbrev CommandParsedSnapshot.data : CommandParsedSnapshot → CommandParsedSnapshotData
|
||||
| mk data _ => data
|
||||
/-- Next command, unless this is a terminal command. -/
|
||||
-- It would be really nice to not make this depend on `sig.finished` where possible
|
||||
abbrev CommandParsedSnapshot.next? : CommandParsedSnapshot →
|
||||
Option (SnapshotTask CommandParsedSnapshot)
|
||||
| mk _ next? => next?
|
||||
partial instance : ToSnapshotTree CommandParsedSnapshot where
|
||||
toSnapshotTree := go where
|
||||
go s := ⟨s.data.toSnapshot,
|
||||
#[s.data.elabSnap.map (sync := true) toSnapshotTree,
|
||||
s.data.finishedSnap.map (sync := true) toSnapshotTree] |>
|
||||
#[s.data.sigSnap.map (sync := true) toSnapshotTree] |>
|
||||
pushOpt (s.next?.map (·.map (sync := true) go))⟩
|
||||
|
||||
|
||||
/-- Cancels all significant computations from this snapshot onwards. -/
|
||||
partial def CommandParsedSnapshot.cancel (snap : CommandParsedSnapshot) : BaseIO Unit := do
|
||||
-- This is the only relevant computation right now, everything else is promises
|
||||
-- TODO: cancel additional elaboration tasks (which will be tricky with `DynamicSnapshot`) if we
|
||||
-- add them without switching to implicit cancellation
|
||||
snap.data.finishedSnap.cancel
|
||||
-- This is the only relevant computation right now
|
||||
-- TODO: cancel additional elaboration tasks if we add them without switching to implicit
|
||||
-- cancellation
|
||||
snap.data.sigSnap.cancel
|
||||
if let some next := snap.next? then
|
||||
-- recurse on next command (which may have been spawned just before we cancelled above)
|
||||
let _ ← IO.mapTask (sync := true) (·.cancel) next.task
|
||||
@@ -351,7 +308,7 @@ where
|
||||
processHeader (stx : Syntax) (parserState : Parser.ModuleParserState) :
|
||||
LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do
|
||||
let ctx ← read
|
||||
SnapshotTask.ofIO (some ⟨0, ctx.input.endPos⟩) <|
|
||||
SnapshotTask.ofIO ⟨0, ctx.input.endPos⟩ <|
|
||||
ReaderT.run (r := ctx) <| -- re-enter reader in new task
|
||||
withHeaderExceptions (α := HeaderProcessedSnapshot) ({ · with result? := none }) do
|
||||
let opts ← match (← setupImports stx) with
|
||||
@@ -405,16 +362,16 @@ where
|
||||
-- is not `Inhabited`
|
||||
return .pure <| .mk (nextCmdSnap? := none) {
|
||||
diagnostics := .empty, stx := .missing, parserState
|
||||
elabSnap := .pure <| .ofTyped { diagnostics := .empty : SnapshotLeaf }
|
||||
finishedSnap := .pure { diagnostics := .empty, cmdState }
|
||||
tacticCache := (← IO.mkRef {})
|
||||
}
|
||||
sigSnap := .pure {
|
||||
diagnostics := .empty
|
||||
finishedSnap := .pure { diagnostics := .empty, cmdState } } }
|
||||
|
||||
let unchanged old : BaseIO CommandParsedSnapshot :=
|
||||
-- when syntax is unchanged, reuse command processing task as is
|
||||
if let some oldNext := old.next? then
|
||||
return .mk (data := old.data)
|
||||
(nextCmdSnap? := (← old.data.finishedSnap.bindIO (sync := true) fun oldFinished =>
|
||||
(nextCmdSnap? := (← old.data.sigSnap.bindIO (sync := true) fun oldSig =>
|
||||
oldSig.finishedSnap.bindIO (sync := true) fun oldFinished =>
|
||||
-- also wait on old command parse snapshot as parsing is cheap and may allow for
|
||||
-- elaboration reuse
|
||||
oldNext.bindIO (sync := true) fun oldNext => do
|
||||
@@ -427,7 +384,7 @@ where
|
||||
if (← isBeforeEditPos nextCom.data.parserState.pos) then
|
||||
return .pure (← unchanged old)
|
||||
|
||||
SnapshotTask.ofIO (some ⟨parserState.pos, ctx.input.endPos⟩) do
|
||||
SnapshotTask.ofIO ⟨parserState.pos, ctx.input.endPos⟩ do
|
||||
let beginPos := parserState.pos
|
||||
let scope := cmdState.scopes.head!
|
||||
let pmctx := {
|
||||
@@ -444,31 +401,21 @@ where
|
||||
-- on first change, make sure to cancel all further old tasks
|
||||
old.cancel
|
||||
|
||||
-- definitely resolved in `doElab` task
|
||||
let elabPromise ← IO.Promise.new
|
||||
let tacticCache ← old?.map (·.data.tacticCache) |>.getDM (IO.mkRef {})
|
||||
let finishedSnap ←
|
||||
doElab stx cmdState msgLog.hasErrors beginPos
|
||||
{ old? := old?.map fun old => ⟨old.data.stx, old.data.elabSnap⟩, new := elabPromise }
|
||||
tacticCache
|
||||
ctx
|
||||
|
||||
let sigSnap ← processCmdSignature stx cmdState msgLog.hasErrors beginPos ctx
|
||||
let next? ← if Parser.isTerminalCommand stx then pure none
|
||||
-- for now, wait on "command finished" snapshot before parsing next command
|
||||
else some <$> finishedSnap.bindIO fun finished =>
|
||||
else some <$> (sigSnap.bind (·.finishedSnap)).bindIO fun finished =>
|
||||
parseCmd none parserState finished.cmdState ctx
|
||||
return .mk (nextCmdSnap? := next?) {
|
||||
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
|
||||
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog ctx.toProcessingContext)
|
||||
stx
|
||||
parserState
|
||||
elabSnap := { range? := finishedSnap.range?, task := elabPromise.result }
|
||||
finishedSnap
|
||||
tacticCache
|
||||
sigSnap
|
||||
}
|
||||
|
||||
doElab (stx : Syntax) (cmdState : Command.State) (hasParseError : Bool) (beginPos : String.Pos)
|
||||
(snap : SnapshotBundle DynamicSnapshot) (tacticCache : IO.Ref Tactic.Cache) :
|
||||
LeanProcessingM (SnapshotTask CommandFinishedSnapshot) := do
|
||||
processCmdSignature (stx : Syntax) (cmdState : Command.State) (hasParseError : Bool)
|
||||
(beginPos : String.Pos) :
|
||||
LeanProcessingM (SnapshotTask CommandSignatureProcessedSnapshot) := do
|
||||
let ctx ← read
|
||||
|
||||
-- signature elaboration task; for now, does full elaboration
|
||||
@@ -476,26 +423,13 @@ where
|
||||
SnapshotTask.ofIO (stx.getRange?.getD ⟨beginPos, beginPos⟩) do
|
||||
let scope := cmdState.scopes.head!
|
||||
let cmdStateRef ← IO.mkRef { cmdState with messages := .empty }
|
||||
/-
|
||||
The same snapshot may be executed by different tasks. So, to make sure `elabCommandTopLevel`
|
||||
has exclusive access to the cache, we create a fresh reference here. Before this change, the
|
||||
following `tacticCache.modify` would reset the tactic post cache while another snapshot was
|
||||
still using it.
|
||||
-/
|
||||
let tacticCacheNew ← IO.mkRef (← tacticCache.get)
|
||||
let cmdCtx : Elab.Command.Context := { ctx with
|
||||
cmdPos := beginPos
|
||||
tacticCache? := some tacticCacheNew
|
||||
snap? := some snap
|
||||
}
|
||||
let cmdCtx : Elab.Command.Context := { ctx with cmdPos := beginPos, tacticCache? := none }
|
||||
let (output, _) ←
|
||||
IO.FS.withIsolatedStreams (isolateStderr := stderrAsMessages.get scope.opts) do
|
||||
liftM (m := BaseIO) do
|
||||
Elab.Command.catchExceptions
|
||||
(getResetInfoTrees *> Elab.Command.elabCommandTopLevel stx)
|
||||
cmdCtx cmdStateRef
|
||||
let postNew := (← tacticCacheNew.get).post
|
||||
tacticCache.modify fun _ => { pre := postNew, post := {} }
|
||||
let cmdState ← cmdStateRef.get
|
||||
let mut messages := cmdState.messages
|
||||
-- `stx.hasMissing` should imply `hasParseError`, but the latter should be cheaper to check in
|
||||
@@ -515,12 +449,14 @@ where
|
||||
data := output
|
||||
}
|
||||
let cmdState := { cmdState with messages }
|
||||
-- definitely resolve eventually
|
||||
snap.new.resolve <| .ofTyped { diagnostics := .empty : SnapshotLeaf }
|
||||
return {
|
||||
diagnostics := (← Snapshot.Diagnostics.ofMessageLog cmdState.messages)
|
||||
infoTree? := some cmdState.infoState.trees[0]!
|
||||
cmdState
|
||||
diagnostics := .empty
|
||||
finishedSnap := .pure {
|
||||
diagnostics :=
|
||||
(← Snapshot.Diagnostics.ofMessageLog cmdState.messages ctx.toProcessingContext)
|
||||
infoTree? := some cmdState.infoState.trees[0]!
|
||||
cmdState
|
||||
}
|
||||
}
|
||||
|
||||
/-- Waits for and returns final environment, if importing was successful. -/
|
||||
@@ -532,6 +468,6 @@ where goCmd snap :=
|
||||
if let some next := snap.next? then
|
||||
goCmd next.get
|
||||
else
|
||||
snap.data.finishedSnap.get.cmdState.env
|
||||
snap.data.sigSnap.get.finishedSnap.get.cmdState.env
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -236,7 +236,7 @@ def checkRegisterSimpAttr : SimpleHandler := mkSimpleHandler "simp attr"
|
||||
@[builtin_missing_docs_handler «in»]
|
||||
def handleIn : Handler := fun _ stx => do
|
||||
if stx[0].getKind == ``«set_option» then
|
||||
let opts ← Elab.elabSetOption stx[0][1] stx[0][3]
|
||||
let opts ← Elab.elabSetOption stx[0][1] stx[0][2]
|
||||
withScope (fun scope => { scope with opts }) do
|
||||
missingDocs.run stx[2]
|
||||
else
|
||||
|
||||
@@ -255,10 +255,6 @@ builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack opts =>
|
||||
(stx.isOfKind ``Lean.Parser.Term.matchAlt && pos == 1) ||
|
||||
(stx.isOfKind ``Lean.Parser.Tactic.inductionAltLHS && pos == 2))
|
||||
|
||||
/-- `#guard_msgs in cmd` itself runs linters in `cmd` (via `elabCommandTopLevel`), so do not run them again. -/
|
||||
builtin_initialize addBuiltinUnusedVariablesIgnoreFn (fun _ stack _ =>
|
||||
stack.any fun (stx, _) => stx.isOfKind ``Lean.guardMsgsCmd)
|
||||
|
||||
/-- Get the current list of `IgnoreFunction`s. -/
|
||||
def getUnusedVariablesIgnoreFns : CommandElabM (Array IgnoreFunction) := do
|
||||
return (unusedVariablesIgnoreFnsExt.getState (← getEnv)).2
|
||||
|
||||
@@ -14,8 +14,7 @@ open Lean.Elab
|
||||
|
||||
def logLint [Monad m] [MonadLog m] [AddMessageContext m] [MonadOptions m]
|
||||
(linterOption : Lean.Option Bool) (stx : Syntax) (msg : MessageData) : m Unit :=
|
||||
let disable := m!"note: this linter can be disabled with `set_option {linterOption.name} false`"
|
||||
logWarningAt stx (.tagged linterOption.name m!"{msg}\n{disable}")
|
||||
logWarningAt stx (.tagged linterOption.name m!"{msg} [{linterOption.name}]")
|
||||
|
||||
/-- If `linterOption` is true, print a linter warning message at the position determined by `stx`.
|
||||
-/
|
||||
|
||||
@@ -23,7 +23,7 @@ def mkErrorStringWithPos (fileName : String) (pos : Position) (msg : String) (en
|
||||
|
||||
inductive MessageSeverity where
|
||||
| information | warning | error
|
||||
deriving Inhabited, BEq, ToJson, FromJson
|
||||
deriving Inhabited, BEq
|
||||
|
||||
structure MessageDataContext where
|
||||
env : Environment
|
||||
@@ -46,18 +46,6 @@ structure PPFormat where
|
||||
/-- Searches for synthetic sorries in original input. Used to filter out certain messages. -/
|
||||
hasSyntheticSorry : MetavarContext → Bool := fun _ => false
|
||||
|
||||
structure TraceData where
|
||||
/-- Trace class, e.g. `Elab.step`. -/
|
||||
cls : Name
|
||||
/-- Start time in seconds; 0 if unknown to avoid `Option` allocation. -/
|
||||
startTime : Float := 0
|
||||
/-- Stop time in seconds; 0 if unknown to avoid `Option` allocation. -/
|
||||
stopTime : Float := startTime
|
||||
/-- Whether trace node defaults to collapsed in the infoview. -/
|
||||
collapsed : Bool := true
|
||||
/-- Optional tag shown in `trace.profiler.output` output after the trace class name. -/
|
||||
tag : String := ""
|
||||
|
||||
/-- Structured message data. We use it for reporting errors, trace messages, etc. -/
|
||||
inductive MessageData where
|
||||
/-- Eagerly formatted text. We inspect this in various hacks, so it is not immediately subsumed by `ofPPFormat`. -/
|
||||
@@ -77,11 +65,22 @@ inductive MessageData where
|
||||
/-- Tagged sections. `Name` should be viewed as a "kind", and is used by `MessageData` inspector functions.
|
||||
Example: an inspector that tries to find "definitional equality failures" may look for the tag "DefEqFailure". -/
|
||||
| tagged : Name → MessageData → MessageData
|
||||
| trace (data : TraceData) (msg : MessageData) (children : Array MessageData)
|
||||
| trace (cls : Name) (msg : MessageData) (children : Array MessageData) (collapsed : Bool)
|
||||
deriving Inhabited
|
||||
|
||||
namespace MessageData
|
||||
|
||||
/-- Determines whether the message contains any content. -/
|
||||
def isEmpty : MessageData → Bool
|
||||
| ofFormat f => f.isEmpty
|
||||
| withContext _ m => m.isEmpty
|
||||
| withNamingContext _ m => m.isEmpty
|
||||
| nest _ m => m.isEmpty
|
||||
| group m => m.isEmpty
|
||||
| compose m₁ m₂ => m₁.isEmpty && m₂.isEmpty
|
||||
| tagged _ m => m.isEmpty
|
||||
| _ => false
|
||||
|
||||
variable (p : Name → Bool) in
|
||||
/-- Returns true when the message contains a `MessageData.tagged tag ..` constructor where `p tag` is true. -/
|
||||
partial def hasTag : MessageData → Bool
|
||||
@@ -91,7 +90,7 @@ partial def hasTag : MessageData → Bool
|
||||
| group msg => hasTag msg
|
||||
| compose msg₁ msg₂ => hasTag msg₁ || hasTag msg₂
|
||||
| tagged n msg => p n || hasTag msg
|
||||
| trace data msg msgs => p data.cls || hasTag msg || msgs.any hasTag
|
||||
| trace cls msg msgs _ => p cls || hasTag msg || msgs.any hasTag
|
||||
| _ => false
|
||||
|
||||
/-- An empty message. -/
|
||||
@@ -134,7 +133,7 @@ where
|
||||
| group msg => visit mctx? msg
|
||||
| compose msg₁ msg₂ => visit mctx? msg₁ || visit mctx? msg₂
|
||||
| tagged _ msg => visit mctx? msg
|
||||
| trace _ msg msgs => visit mctx? msg || msgs.any (visit mctx?)
|
||||
| trace _ msg msgs _ => visit mctx? msg || msgs.any (visit mctx?)
|
||||
| _ => false
|
||||
|
||||
partial def formatAux : NamingContext → Option MessageDataContext → MessageData → IO Format
|
||||
@@ -148,11 +147,8 @@ partial def formatAux : NamingContext → Option MessageDataContext → MessageD
|
||||
| nCtx, ctx, nest n d => Format.nest n <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, compose d₁ d₂ => return (← formatAux nCtx ctx d₁) ++ (← formatAux nCtx ctx d₂)
|
||||
| nCtx, ctx, group d => Format.group <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, trace data header children => do
|
||||
let mut msg := f!"[{data.cls}]"
|
||||
if data.startTime != 0 then
|
||||
msg := f!"{msg} [{data.stopTime - data.startTime}]"
|
||||
msg := f!"{msg} {(← formatAux nCtx ctx header).nest 2}"
|
||||
| nCtx, ctx, trace cls header children _ => do
|
||||
let msg := f!"[{cls}] {(← formatAux nCtx ctx header).nest 2}"
|
||||
let children ← children.mapM (formatAux nCtx ctx)
|
||||
return .nest 2 (.joinSep (msg::children.toList) "\n")
|
||||
|
||||
@@ -209,15 +205,9 @@ instance : Coe (List Expr) MessageData := ⟨fun es => ofList <| es.map ofExpr
|
||||
|
||||
end MessageData
|
||||
|
||||
/--
|
||||
A `BaseMessage` is a richly formatted piece of information emitted by Lean.
|
||||
They are rendered by client editors in the infoview and in diagnostic windows.
|
||||
There are two varieties in the Lean core:
|
||||
* `Message`: Uses structured, effectful `MessageData` for formatting content.
|
||||
* `SerialMessage`: Stores pure `String` data. Obtained by running the effectful
|
||||
`Message.serialize`.
|
||||
-/
|
||||
structure BaseMessage (α : Type u) where
|
||||
/-- A `Message` is a richly formatted piece of information emitted by Lean.
|
||||
They are rendered by client editors in the infoview and in diagnostic windows. -/
|
||||
structure Message where
|
||||
fileName : String
|
||||
pos : Position
|
||||
endPos : Option Position := none
|
||||
@@ -226,53 +216,24 @@ structure BaseMessage (α : Type u) where
|
||||
severity : MessageSeverity := MessageSeverity.error
|
||||
caption : String := ""
|
||||
/-- The content of the message. -/
|
||||
data : α
|
||||
deriving Inhabited, ToJson, FromJson
|
||||
data : MessageData
|
||||
deriving Inhabited
|
||||
|
||||
/-- A `Message` is a richly formatted piece of information emitted by Lean.
|
||||
They are rendered by client editors in the infoview and in diagnostic windows. -/
|
||||
abbrev Message := BaseMessage MessageData
|
||||
namespace Message
|
||||
|
||||
/-- A `SerialMessage` is a `Message` whose `MessageData` has been eagerly
|
||||
serialized and is thus appropriate for use in pure contexts where the effectful
|
||||
`MessageData.toString` cannot be used. -/
|
||||
abbrev SerialMessage := BaseMessage String
|
||||
|
||||
namespace SerialMessage
|
||||
|
||||
@[inline] def toMessage (msg : SerialMessage) : Message :=
|
||||
{msg with data := msg.data}
|
||||
|
||||
protected def toString (msg : SerialMessage) (includeEndPos := false) : String := Id.run do
|
||||
let mut str := msg.data
|
||||
protected def toString (msg : Message) (includeEndPos := false) : IO String := do
|
||||
let mut str ← msg.data.toString
|
||||
let endPos := if includeEndPos then msg.endPos else none
|
||||
unless msg.caption == "" do
|
||||
str := msg.caption ++ ":\n" ++ str
|
||||
match msg.severity with
|
||||
| .information => pure ()
|
||||
| .warning => str := mkErrorStringWithPos msg.fileName msg.pos (endPos := endPos) "warning: " ++ str
|
||||
| .error => str := mkErrorStringWithPos msg.fileName msg.pos (endPos := endPos) "error: " ++ str
|
||||
| MessageSeverity.information => pure ()
|
||||
| MessageSeverity.warning => str := mkErrorStringWithPos msg.fileName msg.pos (endPos := endPos) "warning: " ++ str
|
||||
| MessageSeverity.error => str := mkErrorStringWithPos msg.fileName msg.pos (endPos := endPos) "error: " ++ str
|
||||
if str.isEmpty || str.back != '\n' then
|
||||
str := str ++ "\n"
|
||||
return str
|
||||
|
||||
instance : ToString SerialMessage := ⟨SerialMessage.toString⟩
|
||||
|
||||
end SerialMessage
|
||||
|
||||
namespace Message
|
||||
|
||||
@[inline] def serialize (msg : Message) : IO SerialMessage := do
|
||||
return {msg with data := ← msg.data.toString}
|
||||
|
||||
protected def toString (msg : Message) (includeEndPos := false) : IO String := do
|
||||
-- Remark: The inline here avoids a new message allocation when `msg` is shared
|
||||
return inline <| (← msg.serialize).toString includeEndPos
|
||||
|
||||
protected def toJson (msg : Message) : IO Json := do
|
||||
-- Remark: The inline here avoids a new message allocation when `msg` is shared
|
||||
return inline <| toJson (← msg.serialize)
|
||||
|
||||
end Message
|
||||
|
||||
/-- A persistent array of messages. -/
|
||||
|
||||
@@ -50,7 +50,7 @@ mutual
|
||||
- We ignore metadata.
|
||||
- We ignore universe parameterst at constants.
|
||||
-/
|
||||
partial def main (a b : Expr) (mode : ReduceMode := .none) : MetaM Bool := do
|
||||
unsafe def main (a b : Expr) (mode : ReduceMode := .none) : MetaM Bool :=
|
||||
lt a b
|
||||
where
|
||||
reduce (e : Expr) : MetaM Expr := do
|
||||
@@ -66,9 +66,7 @@ where
|
||||
| .none => return e
|
||||
|
||||
lt (a b : Expr) : MetaM Bool := do
|
||||
if a == b then
|
||||
-- We used to have an "optimization" using only pointer equality.
|
||||
-- This was a bad idea, `==` is often much cheaper than `acLt`.
|
||||
if ptrAddrUnsafe a == ptrAddrUnsafe b then
|
||||
return false
|
||||
-- We ignore metadata
|
||||
else if a.isMData then
|
||||
@@ -86,16 +84,6 @@ where
|
||||
else
|
||||
lt a₂ b₂
|
||||
|
||||
getParamsInfo (f : Expr) (numArgs : Nat) : MetaM (Array ParamInfo) := do
|
||||
-- Ensure `f` does not have loose bound variables. This may happen in
|
||||
-- since we go inside binders without extending the local context.
|
||||
-- See `lexSameCtor` and `allChildrenLt`
|
||||
-- See issue #3705.
|
||||
if f.hasLooseBVars then
|
||||
return #[]
|
||||
else
|
||||
return (← getFunInfoNArgs f numArgs).paramInfo
|
||||
|
||||
ltApp (a b : Expr) : MetaM Bool := do
|
||||
let aFn := a.getAppFn
|
||||
let bFn := b.getAppFn
|
||||
@@ -111,7 +99,7 @@ where
|
||||
else if aArgs.size > bArgs.size then
|
||||
return false
|
||||
else
|
||||
let infos ← getParamsInfo aFn aArgs.size
|
||||
let infos := (← getFunInfoNArgs aFn aArgs.size).paramInfo
|
||||
for i in [:infos.size] do
|
||||
-- We ignore instance implicit arguments during comparison
|
||||
if !infos[i]!.isInstImplicit then
|
||||
@@ -149,7 +137,7 @@ where
|
||||
| .proj _ _ e .. => lt e b
|
||||
| .app .. =>
|
||||
a.withApp fun f args => do
|
||||
let infos ← getParamsInfo f args.size
|
||||
let infos := (← getFunInfoNArgs f args.size).paramInfo
|
||||
for i in [:infos.size] do
|
||||
-- We ignore instance implicit arguments during comparison
|
||||
if !infos[i]!.isInstImplicit then
|
||||
@@ -188,8 +176,7 @@ end
|
||||
|
||||
end ACLt
|
||||
|
||||
@[inherit_doc ACLt.main]
|
||||
def acLt (a b : Expr) (mode : ACLt.ReduceMode := .none) : MetaM Bool :=
|
||||
ACLt.main a b mode
|
||||
@[implemented_by ACLt.main, inherit_doc ACLt.main]
|
||||
opaque Expr.acLt : Expr → Expr → (mode : ACLt.ReduceMode := .none) → MetaM Bool
|
||||
|
||||
end Lean.Meta
|
||||
|
||||
@@ -164,7 +164,7 @@ def mkHEqTrans (h₁ h₂ : Expr) : MetaM Expr := do
|
||||
| none, _ => throwAppBuilderException ``HEq.trans ("heterogeneous equality proof expected" ++ hasTypeMsg h₁ hType₁)
|
||||
| _, none => throwAppBuilderException ``HEq.trans ("heterogeneous equality proof expected" ++ hasTypeMsg h₂ hType₂)
|
||||
|
||||
/-- Given `h : HEq a b` where `a` and `b` have the same type, returns a proof of `Eq a b`. -/
|
||||
/-- Given `h : Eq a b`, returns a proof of `HEq a b`. -/
|
||||
def mkEqOfHEq (h : Expr) : MetaM Expr := do
|
||||
let hType ← infer h
|
||||
match hType.heq? with
|
||||
@@ -174,7 +174,7 @@ def mkEqOfHEq (h : Expr) : MetaM Expr := do
|
||||
let u ← getLevel α
|
||||
return mkApp4 (mkConst ``eq_of_heq [u]) α a b h
|
||||
| _ =>
|
||||
throwAppBuilderException ``eq_of_heq m!"heterogeneous equality proof expected{indentExpr h}"
|
||||
throwAppBuilderException ``HEq.trans m!"heterogeneous equality proof expected{indentExpr h}"
|
||||
|
||||
/--
|
||||
If `e` is `@Eq.refl α a`, return `a`.
|
||||
@@ -189,7 +189,7 @@ def isRefl? (e : Expr) : Option Expr := do
|
||||
If `e` is `@congrArg α β a b f h`, return `α`, `f` and `h`.
|
||||
Also works if `e` can be turned into such an application (e.g. `congrFun`).
|
||||
-/
|
||||
def congrArg? (e : Expr) : MetaM (Option (Expr × Expr × Expr)) := do
|
||||
def congrArg? (e : Expr) : MetaM (Option (Expr × Expr × Expr )) := do
|
||||
if e.isAppOfArity ``congrArg 6 then
|
||||
let #[α, _β, _a, _b, f, h] := e.getAppArgs | unreachable!
|
||||
return some (α, f, h)
|
||||
|
||||
@@ -301,44 +301,6 @@ structure Context where
|
||||
Note that we do not cache results at `whnf` when `canUnfold?` is not `none`. -/
|
||||
canUnfold? : Option (Config → ConstantInfo → CoreM Bool) := none
|
||||
|
||||
/--
|
||||
The `MetaM` monad is a core component of Lean's metaprogramming framework, facilitating the
|
||||
construction and manipulation of expressions (`Lean.Expr`) within Lean.
|
||||
|
||||
It builds on top of `CoreM` and additionally provides:
|
||||
- A `LocalContext` for managing free variables.
|
||||
- A `MetavarContext` for managing metavariables.
|
||||
- A `Cache` for caching results of the key `MetaM` operations.
|
||||
|
||||
The key operations provided by `MetaM` are:
|
||||
- `inferType`, which attempts to automatically infer the type of a given expression.
|
||||
- `whnf`, which reduces an expression to the point where the outermost part is no longer reducible
|
||||
but the inside may still contain unreduced expression.
|
||||
- `isDefEq`, which determines whether two expressions are definitionally equal, possibly assigning
|
||||
meta variables in the process.
|
||||
- `forallTelescope` and `lambdaTelescope`, which make it possible to automatically move into
|
||||
(nested) binders while updating the local context.
|
||||
|
||||
The following is a small example that demonstrates how to obtain and manipulate the type of a
|
||||
`Fin` expression:
|
||||
```
|
||||
import Lean
|
||||
|
||||
open Lean Meta
|
||||
|
||||
def getFinBound (e : Expr) : MetaM (Option Expr) := do
|
||||
let type ← whnf (← inferType e)
|
||||
let_expr Fin bound := type | return none
|
||||
return bound
|
||||
|
||||
def a : Fin 100 := 42
|
||||
|
||||
run_meta
|
||||
match ← getFinBound (.const ``a []) with
|
||||
| some limit => IO.println (← ppExpr limit)
|
||||
| none => IO.println "no limit found"
|
||||
```
|
||||
-/
|
||||
abbrev MetaM := ReaderT Context $ StateRefT State CoreM
|
||||
|
||||
-- Make the compiler generate specialized `pure`/`bind` so we do not have to optimize through the
|
||||
@@ -371,14 +333,6 @@ def SavedState.restore (b : SavedState) : MetaM Unit := do
|
||||
Core.restore b.core
|
||||
modify fun s => { s with mctx := b.meta.mctx, zetaDeltaFVarIds := b.meta.zetaDeltaFVarIds, postponed := b.meta.postponed }
|
||||
|
||||
/--
|
||||
Restores full state including sources for unique identifiers. Only intended for incremental reuse
|
||||
between elaboration runs, not for backtracking within a single run.
|
||||
-/
|
||||
def SavedState.restoreFull (b : SavedState) : MetaM Unit := do
|
||||
Core.restoreFull b.core
|
||||
set b.meta
|
||||
|
||||
instance : MonadBacktrack SavedState MetaM where
|
||||
saveState := Meta.saveState
|
||||
restoreState s := s.restore
|
||||
|
||||
@@ -82,10 +82,8 @@ private partial def mkKey (e : Expr) : CanonM Key := do
|
||||
return key
|
||||
else
|
||||
let key ← match e with
|
||||
| .sort .. | .fvar .. | .bvar .. | .lit .. =>
|
||||
| .sort .. | .fvar .. | .bvar .. | .const .. | .lit .. =>
|
||||
pure { e := (← shareCommon e) }
|
||||
| .const n _ =>
|
||||
pure { e := (← shareCommon (.const n [])) }
|
||||
| .mvar .. =>
|
||||
-- We instantiate assigned metavariables because the
|
||||
-- pretty-printer also instantiates them.
|
||||
@@ -94,7 +92,7 @@ private partial def mkKey (e : Expr) : CanonM Key := do
|
||||
else mkKey eNew
|
||||
| .mdata _ a => mkKey a
|
||||
| .app .. =>
|
||||
let f := e.getAppFn
|
||||
let f := (← mkKey e.getAppFn).e
|
||||
if f.isMVar then
|
||||
let eNew ← instantiateMVars e
|
||||
unless eNew == e do
|
||||
@@ -109,8 +107,7 @@ private partial def mkKey (e : Expr) : CanonM Key := do
|
||||
pure (mkSort 0) -- some dummy value for erasing implicit
|
||||
else
|
||||
pure (← mkKey arg).e
|
||||
let f' := (← mkKey f).e
|
||||
pure { e := (← shareCommon (mkAppN f' args)) }
|
||||
pure { e := (← shareCommon (mkAppN f args)) }
|
||||
| .lam n t b i =>
|
||||
pure { e := (← shareCommon (.lam n (← mkKey t).e (← mkKey b).e i)) }
|
||||
| .forallE n t b i =>
|
||||
|
||||
@@ -1173,35 +1173,6 @@ private def isDefEqLeftRight (fn : Name) (t s : Expr) : MetaM LBool := do
|
||||
trace[Meta.isDefEq.delta.unfoldLeftRight] fn
|
||||
toLBoolM <| Meta.isExprDefEqAux t s
|
||||
|
||||
/-- Helper predicate for `tryHeuristic`. -/
|
||||
private def isNonTrivialRegular (info : DefinitionVal) : MetaM Bool := do
|
||||
match info.hints with
|
||||
| .regular d =>
|
||||
if (← isProjectionFn info.name) then
|
||||
-- All projections are considered trivial
|
||||
return false
|
||||
if d > 2 then
|
||||
-- If definition depth is greater than 2, we claim it is not a trivial definition
|
||||
return true
|
||||
-- After consuming the lambda expressions, we consider a regular definition non-trivial if it is not "simple".
|
||||
-- Where simple is a bvar/lit/sort/proj or a single application where all arguments are bvar/lit/sort/proj.
|
||||
let val := consumeDefnPreamble info.value
|
||||
return !isSimple val (allowApp := true)
|
||||
| _ => return false
|
||||
where
|
||||
consumeDefnPreamble (e : Expr) : Expr :=
|
||||
match e with
|
||||
| .mdata _ e => consumeDefnPreamble e
|
||||
| .lam _ _ b _ => consumeDefnPreamble b
|
||||
| _ => e
|
||||
isSimple (e : Expr) (allowApp : Bool) : Bool :=
|
||||
match e with
|
||||
| .bvar .. | .sort .. | .lit .. | .fvar .. | .mvar .. => true
|
||||
| .app f a => isSimple a false && isSimple f allowApp
|
||||
| .proj _ _ b => isSimple b false
|
||||
| .mdata _ b => isSimple b allowApp
|
||||
| .lam .. | .letE .. | .forallE .. | .const .. => false
|
||||
|
||||
/-- Try to solve `f a₁ ... aₙ =?= f b₁ ... bₙ` by solving `a₁ =?= b₁, ..., aₙ =?= bₙ`.
|
||||
|
||||
Auxiliary method for isDefEqDelta -/
|
||||
@@ -1210,32 +1181,19 @@ private def tryHeuristic (t s : Expr) : MetaM Bool := do
|
||||
let mut s := s
|
||||
let tFn := t.getAppFn
|
||||
let sFn := s.getAppFn
|
||||
-- If `f` (i.e., `tFn`) is not a definition, we do not apply the heuristic.
|
||||
let .defnInfo info ← getConstInfo tFn.constName! | return false
|
||||
/-
|
||||
We apply the heuristic in the following cases:
|
||||
1- `f` is a non-trivial regular definition (see predicate `isNonTrivialRegular`)
|
||||
2- `f` is `match` application.
|
||||
3- `t` or `s` contain meta-variables.
|
||||
let info ← getConstInfo tFn.constName!
|
||||
/- We only use the heuristic when `f` is a regular definition or an auxiliary `match` application.
|
||||
That is, it is not marked an abbreviation (e.g., a user-facing projection) or as opaque (e.g., proof).
|
||||
We check whether terms contain metavariables to make sure we can solve constraints such
|
||||
as `S.proj ?x =?= S.proj t` without performing delta-reduction.
|
||||
That is, we are assuming the heuristic implemented by this method is seldom effective
|
||||
when `t` and `s` do not have metavariables, are not structurally equal, and `f` is an abbreviation.
|
||||
On the other hand, by unfolding `f`, we often produce smaller terms.
|
||||
|
||||
The third case is important to make sure we can solve constraints such as
|
||||
`S.proj ?x =?= S.proj t` without performing delta-reduction.
|
||||
|
||||
When the conditions 1&2&3 do not hold, we are assuming the heuristic implemented by this method is seldom effective
|
||||
when `f` is not simple, `t` and `s` do not have metavariables, are not structurally equal.
|
||||
|
||||
Recall that auxiliary `match` definitions are marked as abbreviations, but we must use the heuristic on
|
||||
them since they will not be unfolded when smartUnfolding is turned on. The abbreviation annotation in this
|
||||
case is used to help the kernel type checker.
|
||||
|
||||
The `isNonTrivialRegular` predicate is also useful to avoid applying the heuristic to very simple definitions that
|
||||
have not been marked as abbreviations by the user. Example:
|
||||
```
|
||||
protected def Mem (a : α) (s : Set α) : Prop := s a
|
||||
```
|
||||
at test 3807.lean
|
||||
-/
|
||||
unless (← isNonTrivialRegular info) || isMatcherCore (← getEnv) tFn.constName! do
|
||||
Recall that auxiliary `match` definitions are marked as abbreviations, but we must use the heuristic on
|
||||
them since they will not be unfolded when smartUnfolding is turned on. The abbreviation annotation in this
|
||||
case is used to help the kernel type checker. -/
|
||||
unless info.hints.isRegular || isMatcherCore (← getEnv) tFn.constName! do
|
||||
unless t.hasExprMVar || s.hasExprMVar do
|
||||
return false
|
||||
withTraceNodeBefore `Meta.isDefEq.delta (return m!"{t} =?= {s}") do
|
||||
@@ -1731,72 +1689,8 @@ private def isDefEqOnFailure (t s : Expr) : MetaM Bool := do
|
||||
unstuckMVar s (fun s => Meta.isExprDefEqAux t s) <|
|
||||
tryUnificationHints t s <||> tryUnificationHints s t
|
||||
|
||||
/--
|
||||
Result type for `isDefEqDelta`
|
||||
-/
|
||||
inductive DeltaStepResult where
|
||||
| eq | unknown
|
||||
| cont (t s : Expr)
|
||||
| diff (t s : Expr)
|
||||
|
||||
/--
|
||||
Perform one step of lazy delta reduction. This function decides whether to perform delta-reduction on `t`, `s`, or both.
|
||||
It is currently used to solve contraints of the form `(f a).i =?= (g a).i` where `i` is a numeral at `isDefEqProjDelta`.
|
||||
It is also a simpler version of `isDefEqDelta`. In the future, we may decide to combine these two functions like we do
|
||||
in the kernel.
|
||||
-/
|
||||
private def isDefEqDeltaStep (t s : Expr) : MetaM DeltaStepResult := do
|
||||
let tInfo? ← isDeltaCandidate? t
|
||||
let sInfo? ← isDeltaCandidate? s
|
||||
match tInfo?, sInfo? with
|
||||
| none, none => return .unknown
|
||||
| some _, none => unfold t (return .unknown) (k · s)
|
||||
| none, some _ => unfold s (return .unknown) (k t ·)
|
||||
| some tInfo, some sInfo =>
|
||||
match compare tInfo.hints sInfo.hints with
|
||||
| .lt => unfold t (return .unknown) (k · s)
|
||||
| .gt => unfold s (return .unknown) (k t ·)
|
||||
| .eq =>
|
||||
unfold t
|
||||
(unfold s (return .unknown) (k t ·))
|
||||
(fun t => unfold s (k t s) (k t ·))
|
||||
where
|
||||
k (t s : Expr) : MetaM DeltaStepResult := do
|
||||
let t ← whnfCore t
|
||||
let s ← whnfCore s
|
||||
match (← isDefEqQuick t s) with
|
||||
| .true => return .eq
|
||||
| .false => return .diff t s
|
||||
| .undef => return .cont t s
|
||||
|
||||
/--
|
||||
Helper function for solving contraints of the form `t.i =?= s.i`.
|
||||
-/
|
||||
private partial def isDefEqProjDelta (t s : Expr) (i : Nat) : MetaM Bool := do
|
||||
let t ← whnfCore t
|
||||
let s ← whnfCore s
|
||||
match (← isDefEqQuick t s) with
|
||||
| .true => return true
|
||||
| .false | .undef => loop t s
|
||||
where
|
||||
loop (t s : Expr) : MetaM Bool := do
|
||||
match (← isDefEqDeltaStep t s) with
|
||||
| .cont t s => loop t s
|
||||
| .eq => return true
|
||||
| .unknown => tryReduceProjs t s
|
||||
| .diff t s => tryReduceProjs t s
|
||||
|
||||
tryReduceProjs (t s : Expr) : MetaM Bool := do
|
||||
match (← projectCore? t i), (← projectCore? s i) with
|
||||
| some t, some s => Meta.isExprDefEqAux t s
|
||||
| _, _ => Meta.isExprDefEqAux t s
|
||||
|
||||
private def isDefEqProj : Expr → Expr → MetaM Bool
|
||||
| .proj m i t, .proj n j s =>
|
||||
if i == j && m == n then
|
||||
isDefEqProjDelta t s i
|
||||
else
|
||||
return false
|
||||
| .proj m i t, .proj n j s => pure (i == j && m == n) <&&> Meta.isExprDefEqAux t s
|
||||
| .proj structName 0 s, v => isDefEqSingleton structName s v
|
||||
| v, .proj structName 0 s => isDefEqSingleton structName s v
|
||||
| _, _ => pure false
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user