Compare commits

..

338 Commits

Author SHA1 Message Date
Leonardo de Moura
0ade0e084c chore: address feedback 2023-12-29 16:22:51 -08:00
Leonardo de Moura
4caa2f42b2 refactor: simplify simpImpl 2023-12-29 16:15:08 -08:00
Leonardo de Moura
68f47e4e45 refactor: simplify match-expressions at pre simp method 2023-12-29 16:15:08 -08:00
Leonardo de Moura
23674845ad chore: simplify mutual at simpImpl 2023-12-29 16:15:08 -08:00
Leonardo de Moura
0ea2a6b8df refactor: use unsafe code to break recursion in simp implementation
Motivations:
- We can simplify the big mutual recursion and the implementation.
- We can implement the support for `match`-expressions in the `pre` method.
- It is easier to define and simplify `Simprocs`.
2023-12-29 16:15:07 -08:00
Leonardo de Moura
86884afadd chore: fix regression due to changes in previous commits
The example was looping with the new `simp` reduction strategy. Here
is the looping trace.
```
List.reverseAux (List.reverseAux as []) bs
==> rewrite using reverseAux_reverseAux
List.reverseAux [] (List.reverseAux (List.reverseAux as []) bs)
==> unfold reverseAux
List.reverseAux (List.reverseAux as []) bs
==> rewrite using reverseAux_reverseAux
List.reverseAux [] (List.reverseAux (List.reverseAux as []) bs)
==> ...
```
2023-12-29 16:15:07 -08:00
Leonardo de Moura
5b46fde02e feat: add pre simp lemmas for if-then-else terms
See new test for example that takes exponential time without new simp
theorems.
TODO: replace auxiliary theorems with simprocs as soon as we implement them.
2023-12-29 16:15:07 -08:00
Leonardo de Moura
0b02e43194 feat: better support for match-application in the simplifier
The new test exposes a performance problem found in software
verification applications.
2023-12-29 16:15:07 -08:00
Leonardo de Moura
ac4882e5da feat: add Expr.getAppArgsN 2023-12-29 16:15:07 -08:00
Leonardo de Moura
9f5723094c feat: add Expr.getAppPrefix 2023-12-29 16:15:07 -08:00
Leonardo de Moura
488ad9f6de feat: add reduceStep, and try pre simp steps again if term was reduced 2023-12-29 16:15:07 -08:00
Joachim Breitner
1145976ff9 test: test “motive is not type correct” (#3122) 2023-12-28 15:28:17 +00:00
Marcus Rossel
13d41f82d7 doc: fix typos (#3114) 2023-12-23 18:55:48 +00:00
Sebastian Ullrich
caf7a21c6f chore: include full build in stdlib benchmark (#3104) 2023-12-23 16:27:07 +00:00
Wojciech Nawrocki
7c38649527 chore: remove workaround in widgets (#3105)
This is a follow-up on #2964 that ~~updates stage0,~~ removes a
workaround ~~, and updates release notes.~~
2023-12-22 14:52:53 +00:00
Mario Carneiro
d1a15dea03 fix: hover info for cases h : ... (#3084)
This makes hover info, go to definition, etc work for the `h` in `cases
h : e`. The implementation is similar to that used for the `generalize h
: e = x` tactic.
2023-12-21 22:39:23 +00:00
Scott Morrison
f1f8db4856 chore: begin development cycle for v4.6.0 (#3109) 2023-12-21 22:39:04 +00:00
Scott Morrison
bcc49d1c5f chore: update tests for #2966 to use test_extern (#3092)
#2966 was the `@[extern]` bug that prompted development of the
`test_extern` command, but then we merged the fix to #2966 without
updating the tests to use `test_extern`.
2023-12-21 22:22:47 +00:00
Joachim Breitner
63d00ea3c2 doc: avoid universe issue in example type class code (#3098)
by allowing `Inhabited` to apply to any sort.

fixes #3096.
2023-12-21 16:57:26 +00:00
Lean stage0 autoupdater
fdc52e0ea9 chore: update stage0 2023-12-21 12:02:01 +00:00
Sebastian Ullrich
767139b235 chore: use all cores in stdlib benchmark 2023-12-21 10:37:18 +01:00
Sebastian Ullrich
bddb2152e5 chore: default compiler.enableNew to false until development restarts (#3034) 2023-12-21 07:48:25 +00:00
Wojciech Nawrocki
8d04ac171d feat: bundle of widget improvements (#2964)
Implements RFC #2963.

Leftover tasks:
- [x] Provide companion PR to vscode-lean4 (leanprover/vscode-lean4#376)
- [x] Companion PR to std4 (leanprover/std4#467)
- [x] Companion PR to ProofWidgets4
(leanprover-community/ProofWidgets4#36)
- [X] Companion commit to mathlib4
(0f4660f655)
- [ ] ~~Update the manual chapter~~ (will do in a follow-up)
2023-12-21 06:24:33 +00:00
Kyle Miller
ae6fe098cb feat: Rust-style raw string literals (#2929)
For example, `r"\n"` and `r#"The word "this" is in quotes."#`.

Implements #1422
2023-12-20 16:53:08 +00:00
Joachim Breitner
79c7b27034 chore: pr-release: Also work with older tags (#3097) 2023-12-20 10:11:05 +00:00
Wojciech Nawrocki
2644b239a3 feat: snippet extension (#3054)
# Summary

This makes a small addition to our take on the LSP protocol
in the form of supporting snippet text edits.
It has been discussed
[here](https://github.com/microsoft/language-server-protocol/issues/592)
on the LSP issue tracker for a while,
but seems unlikely to be added anytime soon.
This feature was requested by @PatrickMassot for the purposes
of supporting Lean code templates in code actions and widgets.

---------

Co-authored-by: David Thrane Christiansen <david@davidchristiansen.dk>
2023-12-20 09:29:19 +00:00
Mac Malone
eb432cd3b7 fix: lake: save config trace before elab (#3069)
Lake will now delete any old `.olean` and save the new trace before
elaborating a configuration file. This will enable the automatic
reconfiguration of the file if elaboration fails.

Fixes an issue that was [discussed on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Invalid.20lake.20configuration/near/406717198).
2023-12-19 21:29:41 +00:00
lu-bulhoes
312ea12bc2 fix: fixing path of the generated binary in documentation (#3093)
This PR fixes the documentation error in "Extended Setup Notes", where
the path of builded binary is pointed to
`./build/bin/foo`, but the truly path is `./lake/build/bin/foo`.

---

Closes #3094 (`RFC` or `bug` issue number fixed by this PR, if any)
2023-12-19 17:26:55 +00:00
Kyle Miller
67bfa19ce0 feat: add quot_precheck for expression tree elaborators (binop%, etc.) (#3078)
There were no `quot_precheck` instances registered for the expression
tree elaborators, which prevented them from being usable in a `notation`
expansion without turning off the quotation prechecker.

Users can evaluate whether `set_option quotPrecheck false` is still
necessary for their `notation` definitions.
2023-12-18 16:52:49 +00:00
Sebastian Ullrich
3335b2a01e perf: improve avoidance of repeated Expr visits in unused variables linter (#3076)
-43% linter run time in a big proof case
2023-12-18 15:56:58 +00:00
Joachim Breitner
78816a3ee7 chore: refine PR template (#3074)
given that we now use the PR description as the commit message, the PR
template should point that out. Also, a `# Summary` is relatively
strange in a commit message, so removed it.

---------

Co-authored-by: Scott Morrison <scott.morrison@gmail.com>
2023-12-18 13:47:04 +00:00
Joachim Breitner
7acbee8ae4 refactor: move unpackArg etc. to WF.PackDomain/WF.PackMutual (#3077)
extracted from #3040 to keep the diff smaller
2023-12-18 13:46:42 +00:00
Leonardo de Moura
4dd59690e0 refactor: generalize some simp methods (#3088) 2023-12-18 04:03:29 -08:00
Kyle Miller
a2226a43ac feat: encode let_fun using a letFun function (#2973)
Switches from encoding `let_fun` using an annotated `(fun x : t => b) v`
expression to a function application `letFun v (fun x : t => b)`.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2023-12-18 09:01:42 +00:00
Hunter Monroe
62c3e56247 doc: Bold "Diaconescu's theorem" (#3086) 2023-12-17 19:10:35 +00:00
Marcus Rossel
89d7eb8b78 doc: fix typos/indentation (#3085) 2023-12-17 18:41:46 +00:00
Scott Morrison
8475ec7e36 fix: reference implementation ByteArray.copySlice (#2967)
Fixes reference implementation of `ByteArray.copySlice`, as reported
https://github.com/leanprover/lean4/issues/2966.

Adds tests.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2023-12-16 20:26:16 +00:00
Scott Morrison
4497aba1a9 fix: don't panic in leanPosToLspPos (#3071)
Testing a problem in the REPL.
2023-12-16 04:20:45 +00:00
Joachim Breitner
cddc8089bc chore: pr-release: revert to originally used action to get PR number (#3072)
Getting the original PR number from a `workflow_run` cleanly and
reliably seems to be
basically impossible. See
<https://github.com/orgs/community/discussions/25220> for a discussion.
So for now let’s go back to the working state, even though it’s
deprecated and throws warnings.
2023-12-14 22:53:02 +00:00
Joachim Breitner
ce15b43798 chore: allow updating stage0 via workflow_dispatch (#3052)
follow-up to #3042
2023-12-14 22:46:32 +00:00
Eric Wieser
430f4d28e4 doc: mention x:h@e variant in docstring of x@e (#3073)
This was done in 1c1e6d79a7

[Zulip
thread](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Naming.20equality.20hypothesis.20in.20match.20branch/near/408016140)
2023-12-14 18:58:14 +00:00
Eric Wieser
d279a4871f chore: add the lean4 extension to the vscode workspace (#3059)
This prompts users opening the workspace (on a new device) for the first
time to install the lean extension

# Summary

Link to `RFC` or `bug` issue: N/A
2023-12-14 08:58:21 +00:00
Scott Morrison
f208d7b50f chore: refactor pr-release.yml to avoid 'await' (#3070)
#3066 is causing CI failures, e.g.
[here](https://github.com/leanprover/lean4/actions/runs/7202184616/job/19619827364).

Although there are plenty of examples of using `await` in a Github
workflow script block, the error *seems* to be about this. This refactor
hopefully works around that, but I'm still uncertain of a root cause.
2023-12-14 04:51:17 +00:00
Joachim Breitner
df18f3f1ff chore: pr-release.yml: use API to get pull request number (#3066)
partially reverting 6a629f7d7f. What a
mess.
2023-12-13 19:58:14 +00:00
Mac Malone
fbcfe6596e fix: lake: leave run options for script (#3064)
Options passed to `lake script run <name>` / `lake run <name>` after the
`<name>` will now be properly passed on through to the script rather
than being consumed by Lake.

The issue was reported [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Lake.20script.20flag.20.22passthrough.22.3F/near/407734447).
2023-12-13 17:45:30 +00:00
Joachim Breitner
b5b664e570 chore: pr-release.yaml: remove hardcoded date (#3061)
This fixe a surprisingly embarrassing bug introduced by me in
fa26d222cb (maybe while testing).

Enable more debug output while we are at it, to find out why sometimes
`context.payload.workflow_run.pull_requests[0]` is undefined.
2023-12-13 13:50:19 +00:00
Mac Malone
2f216b5255 fix: lake: re-elab if config olean is missing (#3036)
If a user deleted `lakefile.olean` manually without deleting
`lakefile.olean.lock`, Lake would still attempt to load it and thus
produce an error. Now it should properly re-elaborate the configuration
file.
2023-12-13 01:07:57 +00:00
Scott Morrison
d4dca3baac feat: test_extern command (#2970)
This adds a `test_extern` command.

Usage:
```
import Lean.Util.TestExtern

test_extern Nat.add 17 37
```

This:
* Checks that the head symbol has an `@[extern]` attribute.
* Writes down `t == t'`, where `t` is the term provided, and `t'` is the
reference implementation (specifically, `t` with the head symbol
unfolded).
* Tries to reduce this to `true`, and complains if this fails.

Note that the type of the term must have a `BEq` instance for this to
work: there's a self-explanatory error message if it isn't available.
2023-12-12 23:33:05 +00:00
Joachim Breitner
de7d78a9f1 chore: do not use actions-ecosystem/action-add-labels (#3055)
That action seems to be unmaintained and causes warnings
(https://github.com/actions-ecosystem/action-add-labels/issues/459).

Let's just use the API directly, like we already do in
`.github/workflows/labels-from-comments.yml`
2023-12-12 22:40:27 +00:00
Joachim Breitner
6a629f7d7f chore: robustify PR release workflow (#3051)
the workflow is triggered not only by pull-request-CI-runs but also by
others. These should be skipped.

Also, no need to query the Github API to get the pull request number and
head sha, they are part of the payload, it seems.
2023-12-12 11:23:22 +00:00
Marc Huisinga
f74516a032 doc: update quickstart guide to reference vs code setup guide (#2968)
Since the vscode-lean4 setup guide allows us to provide information on
setting up Lean 4 tailored to the user's operating system, this PR
adjusts the quickstart guide to reference the vscode-lean4 setup guide
instead.
2023-12-12 08:36:27 +00:00
Sebastian Ullrich
78200b309f fix: run_task/deactivate_task race condition on m_imp->m_closure (#2959)
Fixes #2853, unblocking my work before I get to refactoring this part of
the task manager.
2023-12-12 02:01:40 +00:00
Mario Carneiro
b120080b85 fix: move Lean.List.toSMap to List.toSMap (#3035)
This definition was clearly meant to be in the `List` namespace, but it
is also in a `namespace Lean` so it ended up as `Lean.List.toSMap`
instead of `List.toSMap`. It would be nice if #3031 made this
unnecessary, but for now this seems to be the convention.

I noticed this because of another side effect: it defines `Lean.List` as
a namespace, which means that
```lean
import Std

namespace Lean
open List

#check [1] <+ [2]
```
does not work as expected, it opens the `Lean.List` namespace instead of
the `List` namespace. Should there be a regression test to ensure that
the `Lean.List` namespace (and maybe others) are not accidentally
created? (Unfortunately this puts a bit of a damper on #3031.)
2023-12-12 01:01:24 +00:00
Scott Morrison
4b8c342833 chore: withLocation * should not fail if it closes the main goal (#2917)
Arising from discussion at
https://github.com/leanprover/lean4/pull/2909/files#r1398527730.
2023-12-12 00:45:13 +00:00
Joachim Breitner
fa26d222cb chore: refactor pr release workflow (#3020)
In particular:

* Do not use deprecated `potiuk/get-workflow-origin`.
* Use a bare checkout to push PR to `pr-releases`
* Replace `script/most-recent-nightly-tag.sh` by a one-liner inside the
workflow, so that th workflow is self-contained
2023-12-12 00:45:10 +00:00
Jannis Limperg
e2f957109f fix: omit fvars from simp_all? theorem list (#2969)
Removes local hypotheses from the simp theorem list generated by
`simp_all?`.

Fixes: #2953

---

Supersedes PR #1862
2023-12-12 00:45:07 +00:00
Scott Morrison
20dd63aabf chore: fix superfluous lemmas in simp.trace (#2923)
Fixes an issue reported on Zulip; see the test case.

* Modifies the `MonadBacktrack` instance for `SimpM` to also backtrack
the `UsedSimps` field.
* When calling the discharger, `saveState`, and then `restoreState` if
something goes wrong.

I'm not certain that it makes sense to restore the `MetaM` state if
discharging fails. I can easily change this to more conservatively just
backtrack the `UsedSimps` after failed discharging.
2023-12-11 23:51:31 +00:00
Scott Morrison
c656e71eb8 chore: make List.all and List.any short-circuit (#2972)
Changes the implementation of `List.all` and `List.any` so they
short-circuit. The implementations are tail-recursive.

This replaces https://github.com/leanprover/std4/pull/392, which was
going to do this with `@[csimp]`.
2023-12-11 23:48:15 +00:00
Lean stage0 autoupdater
104c92d4f3 chore: update stage0 2023-12-11 18:37:33 +00:00
Joachim Breitner
5cd90f5826 feat: drop support for termination_by' (#3033)
until around 7fe6881 the way to define well-founded recursions was to
specify a `WellFoundedRelation` on the argument explicitly. This was
rather low-level, for example one had to predict the packing of multiple
arguments into `PProd`s, the packing of mutual functions into `PSum`s,
and the cliques that were calculated.

Then the current `termination_by` syntax was introduced, where you
specify the termination argument at a higher level (one clause per
functions, unpacked arguments), and the `WellFoundedRelation` is found
using type class resolution.

The old syntax was kept around as `termination_by'`. This is not used
anywhere in the lean, std, mathlib or the theorem-proving-in-lean
repositories,
and three occurrences I found in the wild can do without

In particular, it should be possible to express anything that the old
syntax
supported also with the new one, possibly requiring a helper type with a
suitable instance, or the following generic wrapper that now lives in
std
```
def wrap {α : Sort u} {r : α → α → Prop} (h : WellFounded r) (x : α) : {x : α // Acc r x}
```

Since the old syntax is unused, has an unhelpful name and relies on
internals, this removes the support. Now is a good time before the
refactoring that's planned in #2921.

The test suite was updated without particular surprises.

The parametric `terminationHint` parser is gone, which means we can
match on syntax more easily now, in `expandDecreasingBy?`.
2023-12-11 17:33:17 +00:00
Mario Carneiro
178ab8ef2e fix: Option.getD eagerly evaluates dflt (#3043)
Reported [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/348111-std4/topic/Panics.20in.20Std.2EHashMap.2Efind!/near/406872395).
The `dflt` argument of `Option.getD` is not evaluated lazily, as the
documentation says, because even after `macro_inline` the expression
```lean
match opt, dflt with
| some x, _ => x
| none, e => e
```
still has the semantics of evaluating `dflt` when `opt` is `some x`.
2023-12-11 10:07:30 +00:00
Joachim Breitner
e6c0484074 chore: stage0 autoupdater action (#3042)
This Github action automatically updates `stage0` on `master` if
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h`
are out of sync there.

It bypasses the merge queue to be quick, this way, an out-of-date stage0
on on
master should only exist for a few minutes.

Needs access to a _deploy SSH key_ with write permission.
2023-12-11 09:50:27 +00:00
Eric Wieser
dd42a0919d doc: explain how to use custom lexers in the latest minted (#3047)
v3.0 is not yet released; in the meantime, the previous instructions did
not work in the latest version without some hacks.
[Zulip
thread](https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/XeLaTeX.20with.20minted.20error/near/406959183)
2023-12-11 09:16:40 +00:00
Joachim Breitner
1b2bbe717d chore: remove obsolete comment in test (#3044) 2023-12-09 13:20:58 +00:00
Joachim Breitner
00359a0347 chore: update stage0 (#3041) 2023-12-08 12:14:47 +00:00
Eric Wieser
c474dff38c doc: document constructors of TransparencyMode (#3037)
Taken from
https://github.com/leanprover-community/lean4-metaprogramming-book/blob/master/md/main/04_metam.md#transparency

I can never remember which way around `reducible` and `default` go, and
this avoids me needing to leave the editor to find out.
2023-12-07 17:04:40 +00:00
Joachim Breitner
f2a92f3331 fix: GuessLex: deduplicate recursive calls (#3004)
The elaborator is prone to duplicate terms, including recursive calls,
even if the user only wrote a single one. This duplication is wasteful
if we run the tactics on duplicated calls, and confusing in the output
of GuessLex. So prune the list of recursive calls, and remove those
where another call exists that has the same goal and context that is no
more specific.
2023-12-07 09:08:46 +00:00
Kyle Miller
bcbcf50442 feat: string gaps for continuing string literals across multiple lines (#2821)
Implements "gaps" in string literals. These are escape sequences of the
form `"\" newline whitespace+` that have the interpretation of an empty
string. For example,
```
  "this is \
     a string"
```
is equivalent to `"this is a string"`. These are modeled after string
continuations in
[Rust](https://doc.rust-lang.org/beta/reference/tokens.html#string-literals).

Implements RFC #2838
2023-12-07 08:17:00 +00:00
Joachim Breitner
ec8811a75a fix: WF.Fix: deduplicate subsumed goals before running tactic (#3024)
before code like

    def dup (a : Nat) (b : Nat := a) := a + b

    def rec : Nat → Nat
     | 0 => 1
     | n+1 => dup (dup (dup (rec n)))
    decreasing_by decreasing_tactic

would run the `decreasing_tactic` 8 tims, because the recursive call
`rec n` gets duplicate due to the default paramter. Similar effects can
be observed due to dependent types or tactics like `cases`.

This is wasteful, and is confusing to the user when they use
`decreasing_by` interactively. Therfore, we now go through the proof
obligations (MVars) and if solving one would imply solving another one,
we assign the mvars to each other accordingly.

This PR is a sibling of #3004.
2023-12-07 08:04:27 +00:00
Sebastian Ullrich
b3a85631d8 chore: set warningAsError in CI only (#3030)
Don't fail local builds because of this
2023-12-06 08:18:39 +00:00
Joachim Breitner
5d35e9496e doc: fix MetavarContext markdown (#3026)
I found the documentation page hard to parse, so I figured I should fix
this. It's mostly indentation (e.g. in lists), some line breaks and
making URLs clickable.
2023-12-06 08:15:45 +00:00
bc²
d4f10bc07e feat: detail error message about invalid mutual blocks (#2949)
To prevent user confusion as in this [Zulip
message](https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/Matching.20on.20prop/near/341456011)
2023-12-05 10:50:10 +00:00
Marc Huisinga
feb0cb6fc4 doc: add migration guide for per-package server options (#3025)
This PR adjusts `RELEASES.md` to match the recently adjusted release
notes.

---------

Co-authored-by: Mario Carneiro <di.gama@gmail.com>
2023-12-05 10:36:53 +00:00
Joachim Breitner
d6c81f8594 feat: GuessLex: print inferred termination argument (#3012)
With

    set_option showInferredTerminationBy true

this prints a message like

    Inferred termination argument:
    termination_by
      ackermann n m => (sizeOf n, sizeOf m)

it tries hard to use names that

 * match the names that the user used, if present
 * have no daggers (so that it can be copied)
 * do not shadow each other
 * do not shadow anything from the environment (just to be nice)

it does so by appending sufficient `'` to the name.

Some of the emitted `sizeOf` calls are unnecessary, but they are needed
sometimes with dependent parameters. A follow-up PR will not emit them
for non-dependent arguments, so that in most cases the output is pretty.

Somewhen down the road we also want a code action, maybe triggered by
`termination_by?`. This should come after #2921, as that simplifies that
feature (no need to merge termination arguments from different cliques
for example.)
2023-12-05 09:41:52 +00:00
Joachim Breitner
17825bf81d feat: GuessLex: if no measure is found, explain why (#2960)
by showing the matrix of calls and measures, and what we know about that
call (=, <, ≤, ?), e.g.

guessLexFailures.lean:27:0-33:31: error: Could not find a decreasing
measure.
    The arguments relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
               x1 x2 x3
    1) 29:6-25  =  =  =
    2) 30:6-23  =  ?  <
    3) 31:6-23  <  _  _
    Please use `termination_by` to specify a decreasing measure

It’s a bit more verbose for mutual functions.

It will use the user-specified argument names for functions written
```
foo (n : Nat) := …
```
but not with pattern matching like
```
foo : Nat → … 
  | n => …
```
This can be refined later and separately (and maybe right away in
`expandMatchAltsWhereDecls`).
2023-12-05 08:32:15 +00:00
Joachim Breitner
9290b491bb refactor: WF.Fix: gather subgoals (#3017)
This is pure refactoring: Instead of solving each subgoal as we
encounter it while traversing the syntax tree, we leave the `MVar`
there, at the end collect them all using `getMVarsNoDelayed`, and then
solve them.

This is a refactoring preparing for two upcoming changes:

 * removing unexpected duplicate goals that can arise from term
   duplication
 * running interactive tactics on all, not each goal (#2921)

In order to not regress with error locations, we have to associated the
`TermElabM`’s syntax refernce with the `MVar` somehow. I do this using
the existing `mkRecAppWithSyntax` expression annotation, on the `MVar`’s
type. Alternatives would be stack another `StateT` on the traversal
and accumulate `Array (MVarId, Syntax)` explicitly, but that did not
seem to be more appealing.
2023-12-04 21:42:24 +00:00
Joachim Breitner
c91ece4f58 doc: typo Runnign (#3018) 2023-12-04 16:55:07 +00:00
Eric Wieser
93a6279025 chore: add vscode cmake configuration (#3008)
This sets the build directory to `build/release` for the "CMake Tools
for Visual Studio Code" extension documented at
https://vector-of-bool.github.io/docs/vscode-cmake-tools/settings.html#cmake-builddirectory.

It also sets the generator to `make`, since otherwise it tries `Ninja`
which doesn't work.

Without these settings, the extension runs configure in a bad place at
startup.

This does *not* add the cmake tools extension to the default workspace
configuration; the goal is simply to prevent bad behavior for users who
already have the extension enabled.

# Summary

Screenshot of this in action:


![image](https://github.com/leanprover/lean4/assets/425260/6a08e5e5-77af-4ac5-85ed-a149a128e2ca)


Link to `RFC` or `bug` issue: N/A, this is not a bug nor a user-visible
feature.
2023-12-04 16:35:03 +00:00
Joachim Breitner
5c2292a923 doc: In testing doc, suggest make to pick up new tests (#2815) 2023-12-04 10:29:49 +00:00
Sebastian Ullrich
14296ae720 chore: Nix CI: update setup (#3015)
Now that we're, at least temporarily, relying more on the Nix CI,
replace some old hacks of mine with better solutions people have figured
out in the meantime.

Cachix support could probably be dropped at this point but it doesn't
really hurt.
2023-12-03 17:51:05 +00:00
Joachim Breitner
6d23450642 refactor: rewrite TerminationHint elaborators (#2958)
In order to familiarize myself with this code, and so that the next
person has an easier time, I

* added docstrings explaining what I found out these things to
* rewrote the syntax expansion functions using syntax pattern matches,
  to the extend possible
2023-12-02 10:08:07 +00:00
Joachim Breitner
92f1755e9b chore: run tests with full-ci (#3009)
it looks like inter-job outputs are just strings, not boolean values?
2023-12-01 21:14:48 +00:00
Joachim Breitner
465f0feb2d test: expand tests/lean/issue2981.lean a bit (#3007) 2023-12-01 17:52:34 +00:00
Sebastian Ullrich
24466a25f3 doc: widget code owner 2023-12-01 15:46:45 +00:00
Mac Malone
e4eff3bc6e doc: fix recent issue links in RELEASES.md (#3000)
These links were broken because the links used `issue` rather than
`issues`.
2023-12-01 14:48:24 +00:00
Arthur Adjedj
66cb44c53c fix: missing whnf in mkBelowBinder and mkMotiveBinder (#2991)
Closes #2990
2023-12-01 14:46:09 +00:00
Joachim Breitner
8be3897a8b chore: improve tests/lean/copy-produced (#3006)
* do not take an argument, no longer needed
* make it whitespace-in-filenames safe
* copy verbosely when there are changes, for better user feedback
2023-12-01 14:34:52 +00:00
Joachim Breitner
bd89787a87 chore: fix CPP warnings about static_assert (#3005)
else I see
```
[ 69%] Building CXX object runtime/CMakeFiles/leanrt.dir/platform.cpp.o
/home/jojo/build/lean/lean4/src/runtime/io.cpp:509:75: warning: 'static_assert' with no message is a C++17 extension [-Wc++17-extensions]
    static_assert(sizeof(std::chrono::milliseconds::rep) <= sizeof(uint64));
                                                                          ^
                                                                          , ""
/home/jojo/build/lean/lean4/src/runtime/io.cpp:517:74: warning: 'static_assert' with no message is a C++17 extension [-Wc++17-extensions]
    static_assert(sizeof(std::chrono::nanoseconds::rep) <= sizeof(uint64));
                                                                         ^
                                                                         , ""
2 warnings generated.
```
when building
2023-12-01 13:00:01 +00:00
Joachim Breitner
a5af90c724 chore: run CI on new labels (#3003)
CI will now run on _any_ manually added label; hard to avoid.

Fun fact: Because the `toolchain-available` label is added by a github
action with the default token, it will _not_ trigger the workflow. Lucky
coincidence.
2023-12-01 11:32:05 +00:00
Sebastian Ullrich
5937f4208a chore: CI: update github-script (#3002) 2023-12-01 08:39:51 +00:00
Sebastian Ullrich
ea5b55b8f2 doc: remove Nix docs 2023-12-01 08:32:20 +00:00
Sebastian Ullrich
0fca41ddb2 chore: CI: remove changelog job 2023-12-01 08:28:52 +00:00
Joachim Breitner
f356d8830e chore: CI: in quick mode, only Nix build runs the tests (#2998)
Following up on #2986, stop running the test suite in ci.yml in quick
mode; the test suite is run in the Nix job, and we do not need to run it
twice.

With a cold nix cache, when `lean` is rebuilt, not much changes, as both
jobs take ~20mins. But when `lean` is unchanged, the nix build should
be faster, and shaving off the (currently) 4mins in the CI.yaml run
should get us to a green PR sooner.

Another benefit is that we get the PR release sooner and even get it
when the test suite fails, which can be useful if you want to test
mathlib or other things before fixing the lean test suite.
2023-11-30 17:21:51 +00:00
Sebastian Ullrich
5b6e4faacd fix: find macOS system libraries in leanc (#2997)
Fixes #2971
2023-11-30 13:34:24 +00:00
Marcus Rossel
0ad611cf2f doc: fix typos (#2996) 2023-11-30 10:16:33 +00:00
Sebastian Ullrich
3a0edd05e6 doc: VS Code dev setup (#2961)
* multi-root workspace
* default settings including .lean line length
* tasks `build` and `test`

---------

Co-authored-by: mhuisi <mhuisi@protonmail.com>
2023-11-30 08:35:03 +00:00
Scott Morrison
99331219f9 chore: begin development cycle for v4.5.0 (#2995) 2023-11-30 01:27:33 +00:00
Joachim Breitner
18459cb537 refactor: CasesOnApp.refineThrough can return a lambda, not an open term (#2974)
which also removes an error condition at the use site.

While I am at it, I rename a parameter in `GuessLex` that I forgot to
rename earlier.

The effect will be user-visible (in obscure corner cases) with #2960, so
I’ll have the test there.

A few places would benefit from a `lambdaTelescopeBounded` that
garantees the result has the right length (eta-expanding when
necessary). I’ll look into that separately, and left TODOs here.
2023-11-29 15:58:03 +00:00
Joachim Breitner
e4f2c39ab2 test: termination checking and duplicated terms (#2993)
These tests came out of #2981 and #2982; let’s have them in master even
if the changes there will not happen right away.
2023-11-29 15:40:57 +00:00
Marc Huisinga
3025a4a9a1 chore: update stage0 (#2992)
Updates stage-0 so that we can use import auto-completion ourselves.
2023-11-29 15:26:12 +00:00
Joachim Breitner
367ac01279 chore: trim CI set by default (#2986)
The goal of this change is to run a trimmed-down CI on PRs by default,
but allows opt-in the full CI as necessary.

### Specification

The CI workflow runs in “quick” mode if it was triggered from a pull
request, and that pull request does not have the `full-ci` label set.

In “quick” mode the build matrix contains fewer jobs. At the moment
only:

* Linux-release, to get the PR releases.

In non-quick mode everything should be as before.

### Implementation notes

I created a `configure` job that combines all the previous `set-` jobs,
I guess this is faster than firing up separate jobs.

The matrix is calculated in this job; this seems to be the cleanest way
to get a dynamic matrix going (experiments using `exclude` failed). The
downside is that the matrix is now in JSON rather than Yaml syntax. The
upside is that we can (later) make it’s calculation simpler, e.g. set
default `shell` values etc.

I was not able to make it so that CI runs when the `full-ci` label is
added, but don’t do anything otherwise. I think it can be done with
another workflow listening to `labeled` and then triggering this one,
but let’s do that separately. For now, add the label and then push (or
close and reopen).

The checks
```
  if: matrix.build-stage2 || matrix.check-stage3
  if: matrix.check-stage3
```
were dead code, we did not have these fields in the matrix anymore, so I
replaced them with
```
  if: matrix.test-speedcenter
```
2023-11-29 13:24:45 +00:00
Joachim Breitner
4f2f704962 chore: make PR title check work as a merge_group check (#2987) 2023-11-29 12:03:20 +00:00
Joachim Breitner
34264a4b1d doc: Improve docstrings around Array.mk,.data,.toList (#2771)
following a discussion at

<https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Understanding.20the.20docstring.20for.20docs.23Array.2Edata/near/398705430>

---------

Co-authored-by: Mario Carneiro <di.gama@gmail.com>
2023-11-29 08:49:13 +00:00
Scott Morrison
5d22145b83 chore: remove supportInterpreter from lake template (#2984)
Now that there is a helpful message at the point of use when
`supportInterpreter` is required, we don't need to clutter every
`lakefile` with the advice.
2023-11-29 06:16:34 +00:00
Joachim Breitner
0a6aed61e9 chore: CI: Create an all-builds-ok job (#2983)
there is a little dance with `if: success()` because otherwise a failed
`build` job would make this new job skipped, not failed, and I fear
skipped means ok when it is a required job.
So let’s make sure this job actually fails.
2023-11-29 00:10:11 +00:00
Joachim Breitner
6c7a765abb chore: Check PR title, not commit, for commit convention (#2978)
Also turn this into a proper check, run when a PR is opened or edited.

I took the liberty to rename the workflow file and name, so that one
doesn't have to look inside to guess what the workflow is doing.
2023-11-28 17:48:09 +00:00
Scott Morrison
c1f6daf1ac fix: remove unnecessary step in pr-release.yml (#2976)
This step was unnecessary, as the script uses an unauthenticated https
URL anyway, and apparently was causing a [permissions
problem](https://github.com/leanprover/lean4/actions/runs/7005903162/job/19094622187#step:8:7).
2023-11-28 13:18:20 +00:00
Joachim Breitner
ffbea840bf feat: WF.GuessLex: If there is only one plausible measure, use it (#2954)
If here is only one plausible measure, there is no point having the
`GuessLex` code see if it
is terminating, running all the tactics, only for the `MkFix` code then
run the tactics again.

So if there is only one plausible measure (non-mutual recursion with
only one varying
parameter), just use that measure.

Side benefit: If the function isn’t terminating, more detailed error
messages are shown
(failing proof goals), located at the recursive calls.
2023-11-27 22:41:40 +00:00
Mac Malone
190ac50994 doc: release notes for recent lake changes (#2938)
Release notes for PRs #2928, #2930, #2932, and #2937.
2023-11-27 18:30:45 +00:00
Mac Malone
c20d65771c refactor: lake: simplify math template & test it (#2930)
Removes the `CI` option from the `math` template. Since the template
does not currently generate a GitHub workflow, it does not do anything
out of the box except add unnecessary complexity.

The `math` template is also now tested in `tests/init` (minus the
Mathlib `require`).
2023-11-27 18:14:00 +00:00
Joachim Breitner
cbba783bcf feat: Guess lexicographic order for well-founded recursion (#2874)
This improves Lean’s capabilities to guess the termination measure for
well-founded
recursion, by also trying lexicographic orders.  For example:

    def ackermann (n m : Nat) := match n, m with
      | 0, m => m + 1
      | .succ n, 0 => ackermann n 1
      | .succ n, .succ m => ackermann n (ackermann (n + 1) m)

now just works.

The module docstring of `Lean.Elab.PreDefinition.WF.GuessLex` tells the
technical story.
Fixes #2837
2023-11-27 16:30:20 +00:00
Mac Malone
a4aaabf396 refactor: reverse pkg/lib search & no exe roots in import (#2937)
Closes #2548.

Later packages and libraries in the dependency tree are now preferred
over earlier ones. That is, the later ones "shadow" the earlier ones.
Such an ordering is more consistent with how declarations generally work
in programming languages.

This will break any package that relied on the previous ordering.

Also includes a related fix to `findModule?` that mistakenly treated
executable roots as importable.
2023-11-27 16:12:11 +00:00
Mac Malone
984d55c962 fix: lake: proper exe targets & pkg generation (#2932)
Improves executable handling in `lake exe` and `lake init`:

* `lake exe <target>` now parses `target` like a build target (as the
help text states it should) rather than as a basic name.
* `lake new foo.bar [std]` now generates executables named `foo-bar`.
* `lake new foo.bar exe` now properly creates `foo/bar.lean`.
2023-11-27 16:11:12 +00:00
Mac Malone
0249a8c15e fix: untar cloud release if no build dir (#2928)
Cloud releases will now properly be re-unpacked if the build directory
is removed. This fixes [an issue reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Some.20files.20not.20found.20in.20the.20cache/near/402921424)
with the new `.lake` directory that broke Mathlib's ProofWidgets cache.
2023-11-27 16:09:58 +00:00
Joachim Breitner
6592df52cc feat: Add MatcherApp. and CasesOnApp.refineThrough (#2882)
these are compagnions to `MatcherApp.addArg` and `CasesOnApp.addArg`
when one only has an
expression (which may not be a type) to transform, but not a concret
values.

This is a prerequisite for guessing lexicographic order (#2874). Keeping
this on a separate PR because it’s sizable, and has a clear independent
specification.
2023-11-27 15:52:32 +00:00
Mario Carneiro
9769ad6572 fix: missing withContext in simp trace (#2053)
As [reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/simp.3F.20.5B*.5D/near/322724789).

---------

Co-authored-by: Scott Morrison <scott.morrison@gmail.com>
2023-11-27 12:02:38 +00:00
Sebastian Ullrich
79251f5fa2 feat: embed and check githash in .olean (#2766)
This is an additional safety net on top of #2749: it protects users that
circumvent the build system (e.g. with `lake env`) as well as obviates
the need for TOCTOU-like race condition checks in the build system.

The check is activated by `CHECK_OLEAN_VERSION=ON`, which now defaults
to `OFF` as the sensible default for local development. When activated,
`USE_GITHASH=ON` is also force-enabled for stage 0 in order to make sure
that stage 1 can load its own core library.
2023-11-27 10:24:43 +00:00
Sebastian Ullrich
f142d9f798 fix: ignore errors on IO.FS.Handle finalization (#2935) 2023-11-27 08:17:33 +00:00
Marc Huisinga
7ff7cf9b5a feat: per-package server options (#2858)
This PR adds per-package server options to resolve #2455. It is based on
the previous work in #2456, but takes a different approach: options are
loaded for the specific file in the file worker when `print-paths` is
called, instead of loading them in the watchdog with a separate Lake
command. This change addresses review comments made in #2456.

In doing so, it introduces two new Lake config fields: 
- `leanOptions`: `-D` flag options that are passed to both the language
server and `lean` when building.
- `moreServerOptions`: `-D` flag options that are passed to the language
server.

Since `print-paths` must also accept a file path to compute the options
for that file, this PR is changing the API for `print-paths`. As there
have been numerous complaints about the name `print-paths`, I also
decided to change it to `setup-file` in this PR, since it would break
compatibility with the old Lake API anyways.

This PR deprecates the Lakefile field `moreServerArgs` in favor of
`moreGlobalServerArgs`, as suggested in the review for #2456.

Fixes #2455

---------

Co-authored-by: digama0 <mcarneir@andrew.cmu.edu>
2023-11-26 13:42:38 +00:00
Kyle Miller
5639302989 feat: pp.beta to apply beta reduction when pretty printing (#2864)
This was a Lean 3 pretty printer option. While this pretty printer
option tends to lead to confusing situations when set, it has been
frequently requested. [It is
possible](https://github.com/leanprover-community/mathlib4/pull/7910) to
implement this pretty printer option as a user, but it comes with some
artifacts -- for instance, expressions in hovers are not beta reduced.
Adding this as a core pp option is cleanest.

(We should consider having hooks into the tactic evaluator to allow
users to transform the tactic state between tactics. This would enable
beta reducing the entire local context for real, which would be useful
for teaching.)

Closes #715
2023-11-24 12:26:31 +00:00
Scott Morrison
5f5d579986 chore: remove unused MonadBacktrack instance for SimpM (#2943)
We noticed at
https://github.com/leanprover/lean4/pull/2923#discussion_r1400468371
that this instance is not used. It's arguably also incorrect (as it
doesn't backtrack the `usedTheorems` field).

Seems better to just remove to avoid confusion.

Evidence that this is dead code:
* After deleting the instance, calling `saveState` in the `SimpM` monad
raises an error `failed to synthesize instance MonadBacktrack PUnit
SimpM`.
* Understanding the `MonadBacktrack` monad leads one to believe that
would have happened, via the fact that the only instances for
`MonadBacktrack` are either concrete instances (e.g. for `MetaM`,
`TacticM`, etc), or a single lifting instance `instance [MonadBacktrack
s m] [Monad m] : MonadBacktrack s (ExceptT ε m)`. (This is good and
correct behaviour: lifting instances for `MonadBacktrack` would be hard
to model.)
* Mathlib builds after the instance is removed.

Potential evidence that I have not sought, because we don't have
sufficient tooling:
* Compiling Lean/Std/Mathlib with a debugger, breaking on entering this
code.
2023-11-24 08:44:38 +00:00
Marc Huisinga
681fca1f8f feat: import auto-completion (#2904)
This PR adds basic auto-completion support for imports. Since it still
lacks Lake support for accurate completion suggestions (cc @tydeu - we
already know what needs to be done), it falls back to traversing the
`LEAN_SRC_PATH` for available imports.

Three kinds of import completion requests are supported:

- Completion of the full `import` command. Triggered when requesting
completions in an empty space within the header.
- Known issue: It is possible to trigger this completion within a
comment in the header. Fixing this would require architecture for
parsing some kind of sub-syntax between individual commands.
- Completion of the full module name after an incomplete `import`
command.
- Completion of a partial module name with a trailing dot.

Since the set of imports is potentially expensive to compute, they are
cached for 10 seconds after the last import auto-completion request.

Closes #2655.

### Changes

This PR also makes the following changes:
- To support completions on the trailing dot, the `import` syntax was
adjusted to provide partial syntax when a trailing dot is used.
- `FileWorker.lean` was refactored lightly with some larger definitions
being broken apart.
- The `WorkerState` gained two new fields:
- `currHeaderStx` tracks the current header syntax, as opposed to
tracking only the initial header syntax in `initHeaderStx`. When the
header syntax changes, a task is launched that restarts the file worker
after a certain delay to avoid constant restarts while editing the
header. During this time period, we may still want to serve import
auto-completion requests, so we need to know the up-to-date header
syntax.
- `importCachingTask?` contains a task that computes the set of
available imports.
- `determineLakePath` has moved to a new file `Lean/Util/LakePath.lean`
as it is now needed both in `ImportCompletion.lean` and
`FileWorker.lean`.
- `forEachModuleIn` from `Lake/Config/Blob.lean` has moved to
`Lean/Util/Path.lean` as it is a generally useful utility function that
was useful for traversing the `LEAN_SRC_PATH` as well.

### Tests

Unfortunately, this PR lacks tests since the set of imports available in
`tests/lean/interactive` will not be stable. In the future, I will add
support for testing LSP requests in full project setups, which is when
tests for import auto-completion will be added as well.
2023-11-24 07:46:19 +00:00
Joachim Breitner
e34656ce75 doc: Markdown fixes in Lean.Expr (#2956)
there were wrong italics, missing backticks, missing indentation and I
took the liberty to replace `[here]` links with link targets that better
tell the reader what to expect when clicking there.
2023-11-24 06:54:43 +00:00
Joachim Breitner
5a68ad9ef4 chore: Run CI on all PRs, even base ≠ master (#2955) 2023-11-23 21:50:30 +00:00
Scott Morrison
a422f3f2c9 chore: script/most-recent-nightly-tag uses https rather than ssh repo URL (#2951)
The https URL suffices, and does not require that the caller has an
appropriate ssh key.
2023-11-23 10:27:46 +00:00
Joachim Breitner
260eaebf4e fix: PackMutual: Eta-Expand as needed (#2902)
The `packMutual` code ought to reliably replace all recursive calls to
the functions in `preDefs`, even when they are under- or over-applied.
Therefore eta-expand if need rsp. keep extra arguments around.

Needs a tweak to `Meta.transform` to avoid mistaking the `f` in
`f x1 x2` as a zero-arity application.

Includes a test case.

This fixes #2628 and #2883.
2023-11-22 14:25:56 +00:00
Joachim Breitner
dede354e77 fix: Float RecApp out of applications (#2818)
This didn't work before
```
def f (n : Nat) : Nat :=
  match n with
  | 0 => 0
  | n + 1 => (f) n
```
because the `RecApp` metadata marker gets in the way. More practically
relevant, such code is to be produced when using `rw` or `simp` in
recursive theorems (see included test case).

We can fix this by preprocessing the definitions and floating the
`.mdata` marker out of applications.

For structural recursion, there already exists a `preprocess` function;
this now also floats out `.mdata` markers.

For well-founded recursion, this introduces an analogous `preprocess`
function.

Fixes #2810.

One test case output changes: With the `.mdata` out of the way, we get a
different error message. Seems fine.

Alternative approaches are:

* Leaving the `.mdata` marker where it is, and looking around it.
  Tried in #2813, but not nice (many many places where `withApp` etc.
  need to be adjusted).
* Moving the `.mdata` _inside_ the application, so that `withApp` still
  works. Tried in #2814. Also not nice, the invariant that the `.mdata`
  is around the `.const` is tedious to maintain.
2023-11-22 14:25:09 +00:00
Sebastian Ullrich
5eb4a007a6 chore: CI: pin macos-11 to work around 12.7.1 breakage (#2946)
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2023-11-22 13:17:27 +00:00
Joachim Breitner
54dd588fc2 fix: Use whnf for mutual recursion with types hiding (#2926)
the code stumbled over recursive functions whose type doesn’t have
enough manifest foralls, like:

```
def FunType := Nat → Nat

mutual
def foo : FunType
  | .zero => 0
  | .succ n => bar n
def bar : FunType
  | .zero => 0
  | .succ n => foo n
end
termination_by foo n => n; bar n => n
```

This can be fixed by using `whnf` in appropriate places, to expose the
`.forall` constructor.

Fixes #2925, comes with test case.
2023-11-22 11:31:36 +00:00
Scott Morrison
9efdde23e0 fix: most-recently-nightly-tag does not assume a 'nightly' remote (#2947)
`script/most-recent-nightly-tag.sh` determines the most recent nightly
release in your current git history.

Previously it was assuming that you had a `nightly` remote, to pull tags
from. Now it just pulls directly from the repository by URL.
2023-11-22 10:56:39 +00:00
Scott Morrison
91917516f1 chore: run CI on merge_group (#2948)
Per
https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue#triggering-merge-group-checks-with-github-actions
2023-11-22 11:24:13 +00:00
Joachim Breitner
fb30932ca7 refactor: WF.Fix: Pass all remaining goals to Term.reportUnsolvedGoals (#2922)
This only really shows up when the `decreasing_tactic` fails with
multiple goals, as in
```
macro_rules
  | `(tactic|decreasing_tactic) => `(tactic| by_cases (2 > 1))
def foo (n : Nat) : Nat := foo (n - 1)
termination_by foo n => n
```
where we now get
```
unsolved goals
case inl
n: Nat
h✝: 2 > 1
⊢ (invImage (fun a => a) instWellFoundedRelation).1 (n - 1) n


case inr
n: Nat
h✝: ¬2 > 1
⊢ (invImage (fun a => a) instWellFoundedRelation).1 (n - 1) n
```
rather than
```
LeanProject.lean:3:27

unsolved goals
case inl
n: Nat
h✝: 2 > 1
⊢ (invImage (fun a => a) instWellFoundedRelation).1 (n - 1) n

LeanProject.lean:3:27

unsolved goals
case inr
n: Nat
h✝: ¬2 > 1
⊢ (invImage (fun a => a) instWellFoundedRelation).1 (n - 1) n
```

The effect is neglectible, but the code is a bit nicer, so why not,
before someone looks at it again and wonders whether the goals are
reported separately for a reason.
2023-11-21 19:26:52 +01:00
Joachim Breitner
0adca630cc chore: update stage0 2023-11-21 18:59:22 +01:00
Joachim Breitner
37362658ab fix: eq_refl tactic’s name is eqRefl
Previously, it has `name := refl`, which looked confusing in
[the
docs](https://leanprover-community.github.io/mathlib4_docs/Init/Tactics.html#Lean.Parser.Tactic.refl),
as there is no `refl` tactic,
2023-11-21 18:59:22 +01:00
Adrien Champion
66aa2c46a8 doc: mention dite in ite docstring (#2924)
Some beginners have trouble finding the `if h : c then t else e`
(`dite`) version of `ite`. This augments `ite`'s docstring to mention
the dependent version.
2023-11-21 15:59:35 +01:00
Mario Carneiro
b97b0ad2aa feat: rename request handler (#2462)
This implements a request handler for the `textDocument/rename` LSP
request, enabling renames via F2. It handles both local renames (e.g.
`let x := 1; x` to `let y := 1; y`) as well as global renames
(definitions).

Unfortunately it does not work for "orphan" files outside a project, as
it uses ilean data for the current file and this does not seem to be
saved for orphan files. As a result, the test file does not work,
although one can manually test the implementation against a project such
as mathlib. (This issue already exists for the "references" request,
e.g. ctrl click on the first `x` in `let x := 1; x` takes you to the
second one only if you are not in an orphan file.)

* Fixes leanprover-community/mathlib4#7124
2023-11-21 13:10:52 +01:00
Joachim Breitner
fbefbce8c7 doc: Adjust contributor's docs to squash merging (#2927)
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2023-11-21 10:13:43 +00:00
Scott Morrison
f1b274279b feat: helpful error message about supportInterpreter (#2912)
Following [@Kha's
suggestion](https://github.com/leanprover/lean4/issues/2897#issuecomment-1816043031)
from #2897.

---------

Co-authored-by: Mario Carneiro <di.gama@gmail.com>
2023-11-21 10:31:26 +01:00
Kyle Miller
6a33afb745 feat: Lean.MVarId.cleanup configuration (#2919)
Modifies `cleanup` so that it takes (1) an array of additional fvarids
to preserve and (2) a flag to control whether to include indirect
propositions.

(This is wanted in mathlib for the `extract_goal` tactic.)
2023-11-21 10:09:48 +01:00
Joachim Breitner
9800e066bc fix: PackMutual: Deal with extra arguments (#2892)
previously, it would ignore a recursive call that has extra arguments,
which can happen when the recursive functions return something of
function type. Therefore just leave them extra arguments in place.

Fixes #2883.
2023-11-20 17:07:50 +01:00
Mac Malone
5858549037 doc: release notes for recent lake fixes
Co-authored-by: Scott Morrison <scott@tqft.net>
2023-11-20 15:51:31 +00:00
Kyle Miller
4d39a0b0e3 fix: DecidableEq deriving handler could not handle fields whose types start with an implicit argument (#2918)
Fixes #2914
2023-11-20 20:51:47 +11:00
Sebastian Ullrich
9bf0f5116b chore: more code owners 2023-11-20 09:30:18 +01:00
Scott Morrison
8b86beeb07 doc: clarify doc-string for Lean.Elab.Tactic.withLocation (#2909)
In the previous doc-string, the sentence

> "If any of the selected tactic applications fail, it will call
`failed` with the main goal mvar."

was false both for `Location.wildcard` (where it should have said "If
all", not "If any") or for `Location.targets` (where `failed` is never
called).
2023-11-20 09:15:27 +01:00
Mario Carneiro
8881517018 fix: report goals in induction with parse error 2023-11-20 09:15:27 +01:00
Eric Wieser
0668544a35 feat: add an OfNat instance for Level (#2880)
This allows writing `1 : Level`, which is pretty handy for using `Sort 1` aka `Type`.

Co-authored-by: Scott Morrison <scott@tqft.net>
2023-11-20 09:14:16 +01:00
Marcus Rossel
1362268472 doc: fix typos (#2915) 2023-11-19 20:00:47 +00:00
tydeu
65d08fdcdd chore: ignore forgotten Lake test artifacts 2023-11-17 21:25:41 -05:00
tydeu
e29c3239e3 fix: lake: whitelist loaded config olean env exts 2023-11-17 13:50:14 -05:00
Adrien Champion
ed1a98d5ae doc: add documentation for universe, open, export, variable
Add documentation comments with examples to `universe`, `open`,
`export`, and `variable`.

The documentation shows up when hovering over keywords, hopefully
improving the experience for beginners.
2023-11-17 13:19:10 +00:00
github-actions[bot]
c6e4b98793 doc: update changelog 2023-11-17 12:32:02 +00:00
Leni Aniva
ab36ed477e feat: allow trailing comma in tuples, lists, and tactics (#2643) 2023-11-17 13:31:41 +01:00
tydeu
5d1d493635 feat: bare lake init & validated pkg names 2023-11-16 12:54:52 -05:00
Mac Malone
893d480c77 refactor: lake: use plain lib name for root and native name 2023-11-16 12:49:46 -05:00
tydeu
857ba0a3e5 fix: support non-identifier library names 2023-11-16 12:48:55 -05:00
Joachim Breitner
ad77e7e762 chore: Issue template: Suggest #eval Lean.versionString (#2884)
as this works also on https://live.lean-lang.org/ or for people
not familiar with the command line.
2023-11-16 18:40:55 +01:00
Scott Morrison
b3e9bb4997 chore: update release notes after v4.3.0-rc2 2023-11-16 21:55:25 +11:00
Sebastian Ullrich
139973217c chore: more code owners 2023-11-16 10:09:54 +01:00
tydeu
b770060b9e doc: lakefile.olean compatibility check release note 2023-11-15 19:31:08 -05:00
tydeu
8a2054ca09 fix: stricter lakefile.olean compatibility check 2023-11-15 19:31:08 -05:00
tydeu
171837216a feat: IO.FS.Handle.lock/tryLock/unlock 2023-11-15 19:31:08 -05:00
tydeu
19c81a19ea feat: IO.FS.Handle.rewind/truncate 2023-11-15 19:31:08 -05:00
Sebastian Ullrich
7cc2c9f1c9 doc: code owners (#2875) 2023-11-15 18:21:23 +01:00
Alexander Bentkamp
7fb7b5c5cb chore: releases for web assembly and x86 Linux (#2855) 2023-11-15 18:18:47 +01:00
tydeu
bbc759522a doc: lake: flexible manifest release notes 2023-11-15 00:39:06 -05:00
tydeu
712d3c2292 chore: deprecate Lake.PackageConfig.manifestFile 2023-11-15 00:39:06 -05:00
tydeu
73540ecd48 feat: lake: use / in Windows manifest file paths 2023-11-15 00:39:06 -05:00
tydeu
d07e8fd6a4 test: lake: add manifest version upgrade test 2023-11-15 00:39:06 -05:00
tydeu
446d547817 refactor: lake: more flexible manifest 2023-11-15 00:39:06 -05:00
Scott Morrison
37c2ec10e9 chore: fix conditional syntax in pre-release.yml 2023-11-15 12:24:20 +11:00
Eric Wieser
6f2eb3f6b4 doc: fix typo 2023-11-15 12:19:42 +11:00
Kyle Miller
76a7754d08 fix: have parenthesizer copy source info to parenthesized term
This causes the info view to have the entire parenthesized expression be hoverable.
2023-11-14 20:24:30 +01:00
Sebastian Ullrich
77ee031172 fix: re-read HTTP header when skipping notification in Ipc.readResponseAs 2023-11-14 17:34:04 +01:00
Sebastian Ullrich
2f35651308 perf: leak environments not freed before process exit 2023-11-14 17:33:04 +01:00
Sebastian Ullrich
62dc8d7308 feat: Runtime.markMultiThreaded/Persistent 2023-11-14 17:33:04 +01:00
tydeu
6d349201b4 doc: add note on .lake to RELEASES.md 2023-11-13 20:31:24 -05:00
tydeu
dcb92296f6 test: use built lake for examples/reverse-ffi 2023-11-13 20:31:24 -05:00
tydeu
4ec3d78afa chore: update tests to account for .lake 2023-11-13 20:31:24 -05:00
tydeu
2ff4821026 refactor: .lake directory for Lake outputs 2023-11-13 20:31:24 -05:00
tydeu
ffd79a0824 fix: lake: ensure untar output directory exists 2023-11-13 20:31:24 -05:00
Sebastian Ullrich
8cfcf7ce61 fix: look through binop% variants in elabCDotFunctionAlias? 2023-11-12 16:57:51 +11:00
Sebastian Ullrich
dbe1c7f459 fix: make ^ a right action, add NatPow and HomogeneousPow 2023-11-12 16:57:51 +11:00
Kyle Miller
4bd0525a99 chore: update stage0 2023-11-12 16:57:51 +11:00
Sebastian Ullrich
31f234ba3c feat: leftact%/rightact% binop variants 2023-11-12 16:57:51 +11:00
Kyle Miller
262f213391 chore: update stage0 2023-11-12 16:57:51 +11:00
Sebastian Ullrich
8b145b05e2 feat: add leftact%/rightact% syntax 2023-11-12 16:57:51 +11:00
Scott Morrison
f3c3a1b62d feat: find Decidable instances via unification (#2816)
Because `Decidable` carries data,
when writing `@[simp]` lemmas which include a `Decidable` instance on the LHS,
it is best to use `{_ : Decidable p}` rather than `[Decidable p]`
so that non-canonical instances can be found via unification rather than
typeclass search.

(Previously this behaviour was often being hidden by the default `decide :=
true` in `simp`.)
2023-11-12 00:47:13 +00:00
Scott Morrison
1f68dec119 chore: fix commit used for PR release (#2861)
We were checking out the synthetic merge commit between the PR `HEAD`
and `master`, and this was then breaking the logic to determine which
nightly-testing branches to use in Mathlib and Std.
2023-11-11 00:35:27 +00:00
Joachim Breitner
fd0a209f74 refactor: TerminationHint: Remove duplicted code line (#2859)
(I sincerely hope that erasing from a map is idempotent :-))
2023-11-10 16:17:27 +00:00
Marcus Rossel
5189578a48 doc: fix typo in Array.Mem docstring (#2856) 2023-11-10 11:16:32 +00:00
Leonardo de Moura
dcb40f67c1 chore: update RELEASES.md 2023-11-09 04:06:30 -08:00
Leonardo de Moura
e53952f167 chore: fix tests 2023-11-09 04:06:30 -08:00
Leonardo de Moura
d7c05a5ac4 fix: fixes #2042 2023-11-09 04:06:30 -08:00
Scott Morrison
ac73c8d342 feat: Lean.Linter.logLintIf (#2852)
A utility function moving from Mathlib.
2023-11-09 23:00:34 +11:00
Scott Morrison
007b1b5979 feat: extend API of KVMap (#2851) 2023-11-09 22:59:56 +11:00
Sebastian Ullrich
b278172b7c chore: add import Lean benchmark 2023-11-07 18:46:28 +01:00
Leonardo de Moura
d9eddc9652 feat: ensure nested proofs having been abstracted in equation and unfold auxiliary theorems 2023-11-07 06:23:45 -08:00
Leonardo de Moura
2099190ad4 chore: do not abstract nested proofs in a proof 2023-11-07 06:23:45 -08:00
Eric Wieser
72f7144403 doc: mention the proof-binding syntax in match
This comes up over and over again in the zulip; let's document it!
2023-11-06 11:28:03 -08:00
Joachim Breitner
995725b256 test: C trigraph
add a test file that checks that C trigraphs in string literals are not
miscompiled.
2023-11-06 16:31:05 +01:00
Joachim Breitner
b1f2fcf758 fix: Escape ? in C literal strings to avoid trigraphs
This fixes #3829
2023-11-06 16:25:00 +01:00
Scott Morrison
37c154b6de chore: use flow control rather than exit codes in CI scripts (#2828) 2023-11-06 06:08:12 +00:00
Scott Morrison
f201f63e49 chore: fix identification of most recent nightly tag (#2827) 2023-11-06 03:53:06 +00:00
Scott Morrison
691113ca7c chore: add Mathlib CI comments using the mathlib bot (#2824) 2023-11-06 00:25:41 +00:00
Joachim Breitner
ea20911a85 feat: Better error location in structural recursion (#2819)
previously, only the WellFounded code was making use of the error
location in the RecApp-metadata. We can do the same for structural
recursion. This way,
```
def f (n : Nat) : Nat :=
  match n with
  | 0 => 0
  | n + 1 => f (n + 1)
```
will show the error with squiggly lines under `f (n + 1)`, and not at
`def f`.
2023-11-05 22:24:17 +01:00
Sebastian Ullrich
b0d1c3b99c perf: avoid quadratic number of info tree nodes in DecEq deriving handler 2023-11-04 13:59:23 -07:00
Scott Morrison
8cf9d13ca4 chore: still fixing CI (#2817) 2023-11-04 06:32:05 +00:00
Leonardo de Moura
47c09ac36c chore: the previous commit exposed an issue with simp
`simp` was previously swallowing runtime exceptions and masking an
issue with this example.

`runT` is defined by well-founded recursion, but reducing the ground
term `runT x` takes a long time when `decide := true`.

Remark PR #2722 changes the `decide` default value to `false`.

When `decide := true`, we should probably have better diagnostics /
error messages for this kind of situation.
2023-11-03 05:56:59 -07:00
Leonardo de Moura
4afcdeb771 fix: fixes #2775
fixes #2744
2023-11-03 05:56:59 -07:00
Scott Morrison
e217ad3929 chore: more adjustments to new CI scripts (#2811) 2023-11-03 02:34:23 +00:00
Henrik Böving
1d061da98f fix: --no-build lake test for new naming scheme 2023-11-02 23:21:47 +01:00
Henrik Böving
59d3b3d85a chore: update stage0 2023-11-02 23:21:47 +01:00
Henrik Böving
433c094e95 feat: LLVM bc separation for CMake 2023-11-02 23:21:47 +01:00
Siddharth Bhat
5980e665a8 test: add test of LLVM integration into lake 2023-11-02 23:21:47 +01:00
Siddharth Bhat
3b175fdb0e feat: lake LLVM backend support
Co-authored-by: Henrik Böving <hargonix@gmail.com>
Co-authored-by: Mac Malone <tydeu@hatpress.net>
2023-11-02 23:21:47 +01:00
Siddharth Bhat
145a4952e5 feat: add internal flag lean_has_llvm_backend 2023-11-02 23:21:47 +01:00
Siddharth Bhat
f165414e13 fix: use -O3 for LLVM tests in common.sh
This is the same flag that the C test uses. Previously this was hidden
in the Lean compiler itself but now that the optimization pass is phased
out of the compiler we need to put it here.

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2023-11-02 23:21:47 +01:00
Henrik Böving
1b3799ecde fix: set LEANC_CC to the CMake CC by default
In LLVM builds the Cmake CC is necessarily clang -> leanc will be able
to act on LLVM bitcode files if configured this way.

Co-authored-by: Siddharth <siddu.druid@gmail.com>
2023-11-02 23:21:47 +01:00
Siddharth Bhat
3369356788 fix: remove target triple parameter from FFI that no longer exists in the Lean API 2023-11-02 23:21:47 +01:00
Siddharth Bhat
b8d81e1081 fix: option parsing for bitcode, needs to be -b 2023-11-02 23:21:47 +01:00
Siddharth Bhat
0b37bad2cb feat: split bitcode optimization and object file building to be outside lean 2023-11-02 23:21:47 +01:00
Mario Carneiro
82196efe94 feat: hovers on open and export decls 2023-11-02 17:01:51 +01:00
Scott Morrison
4934f5c56d chore: force push to nightly branch when making nightly-YYYY-MM-DD tags (#2808) 2023-11-02 12:05:15 +00:00
Scott Morrison
e360544001 chore: don't run irrelevant CI steps (#2807) 2023-11-02 11:45:19 +00:00
Joachim Breitner
03b681c056 doc: Add docstrings to dbg_trace and assert! in do blocks (#2787)
they had doc strings in their term forms, but the doElem variant did
not, as noted [on zulip](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Infoview.20hangs.20after.20using.20.60IO.2Eprintln.60.20in.20.60Delab.60/near/399317734)
2023-11-02 11:10:42 +01:00
Scott Morrison
1bc07a4e06 chore: fix to Mathlib combined CI (#2806) 2023-11-02 09:54:08 +00:00
Scott Morrison
1e915b1248 chore: fix to Mathlib combined CI (#2804) 2023-11-02 06:21:19 +00:00
Scott Morrison
da32b5f837 feat: use nightly-testing-YYYY-MM-DD branches on Mathlib for testing PRs, and be more conservative about launching Mathlib CI (#2798) 2023-11-02 12:07:06 +11:00
Mauricio Collares
cfe5a5f188 chore: change simp default to decide := false (#2722) 2023-11-02 10:06:38 +11:00
TAKANO Mitsuhiro
29b09b0900 chore: CI: use setup-emsdk@v12 (#2796)
Fix for below warning in GitHub Actions.

```
Web Assembly
The following actions uses node12 which is deprecated and will be forced to run on node16: mymindstorm/setup-emsdk@v11. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/
```
2023-11-01 18:53:08 +01:00
Scott Morrison
9d2ea99753 chore: fix duplication in release notes (#2794) 2023-11-01 00:52:49 +00:00
tydeu
72fdddfed3 test: adjustment for lake update behavior change 2023-10-31 13:25:26 -04:00
tydeu
793329fd56 fix: lake: consistent order for manifest packages 2023-10-31 13:25:26 -04:00
Scott Morrison
55bd2eb2e1 feat: reorder Lake help 2023-10-31 13:24:44 -04:00
Scott Morrison
c359d03b60 chore: begin development cycle for v4.4.0 (#2792) 2023-10-31 08:57:26 +00:00
Scott Morrison
49bdeb3c46 doc: complete release notes for v4.3.0-rc1 (#2791) 2023-10-31 02:50:06 +00:00
Leonardo de Moura
db281f60fe fix: fixes #2178 (#2784) 2023-10-30 15:06:56 +11:00
Scott Morrison
7286dfa38a feat: withAssignableSyntheticOpaque in assumption (#2596)
* feat: withAssignableSyntheticOpaque in assumption
* add test
2023-10-30 04:00:52 +00:00
thorimur
50f2154cbb fix: make rw [foo] look in the local context for foo before it looks in the environment (#2738) 2023-10-30 14:08:02 +11:00
Parth Shastri
642bc5d8f3 fix: replace DecidableEq with BEq/LawfulBEq in List mem theorems (#2041) 2023-10-30 14:03:16 +11:00
Joachim Breitner
f74ae5f9c0 feat: Array.mem: Avoid DecidableEq, set up decreasing_trivial (#2774)
The notation `a ∈ as` for Arrays was previously only defined with
`DecidableEq` on the elements, for (apparently) no good reason. This
drops this requirements (by using `a ∈ as.data`), and simplifies a bunch
of proofs by simply lifting the corresponding proof from lists.

Also, `sizeOf_lt_of_mem` was defined, but not set up to be picked up by
`decreasing_trivial` in the same way that the corresponding List lemma
was set up, so this adds the tactic setup.

The definition for `a ∈ as` is intentionally not defeq to `a ∈ as.data`
so that the termination tactics for Arrays don’t spuriously apply when
recursing through lists.
2023-10-30 13:47:30 +11:00
Eric Rodriguez
df6626f06b doc: fix a link in development documentation #2780 2023-10-30 10:58:25 +11:00
Kyle Miller
5fc079d9ce fix: dsimp missing consumeMData when closing goals by rfl (#2776)
Fixes #2514
2023-10-30 09:32:32 +11:00
Leonardo de Moura
af301fac55 chore: update stage0 2023-10-29 09:41:48 -07:00
Leonardo de Moura
175a6ab606 refactor: add Init/MetaTypes to workaround bootstrapping issues
Motivation: we could not set `simp` configuration options at `WFTactics.lean`
2023-10-29 09:38:23 -07:00
Leonardo de Moura
a53ec40df1 chore: update stage0 2023-10-29 09:18:23 -07:00
Leonardo de Moura
1abd5cc665 chore: add simp option unfoldPartialApp
It is not being used yet, but we need to add it before solving issue #2042.
Reason: bootstrapping.
2023-10-29 09:12:21 -07:00
Leonardo de Moura
08c47b2d61 chore: update stage0 2023-10-29 09:07:33 -07:00
Patrick Massot
c916238e5c feat: lake: sensible default arguments for math template (#2770) 2023-10-29 10:39:43 -04:00
Scott Morrison
f76a17b33f chore: run CI against the head of the branch, not a virtual merge with master (#2769) 2023-10-27 21:46:49 +11:00
Scott Morrison
dba299ac6a chore: update 'nightly' branch to track nightly releases (#2767) 2023-10-27 21:46:06 +11:00
Sebastian Ullrich
6c5f79c0df chore: update stage0 2023-10-26 10:47:14 +02:00
Sebastian Ullrich
23c68cfc5b fix: remove unguarded check_interrupted call 2023-10-26 08:33:09 +02:00
Sebastian Ullrich
9874848f83 perf: inline checkInterrupted
Amazingly, the extra result allocation seems to have triggered a mathlib
heartbeat timeout
2023-10-26 08:33:09 +02:00
Sebastian Ullrich
d3bc2ac1a9 fix: switch to C++ interruption whitelist 2023-10-26 08:33:09 +02:00
Sebastian Ullrich
462a583d98 fix: do not throw interrupt exceptions inside pure functions 2023-10-26 08:33:09 +02:00
Sebastian Ullrich
c5691f816a feat: cancel tasks on document edit 2023-10-26 08:33:09 +02:00
Sebastian Ullrich
fa3cf4d613 feat: translate interrupted kernel exception 2023-10-26 08:33:09 +02:00
Sebastian Ullrich
74b8dda181 feat: check task cancellation in elaborator 2023-10-26 08:33:09 +02:00
Sebastian Ullrich
5f37f7d86f feat: move check_interrupted from unused thread class to Task cancellation 2023-10-26 08:33:09 +02:00
Leonardo de Moura
dbcc7966cf test: for simp [x] where x is a let-variable 2023-10-25 03:12:35 -07:00
Leonardo de Moura
a3642bd8d9 feat: add support for expanding let-declarations to simp
Given a local context containing `x : t := e`,
`simp (config := { zeta := false }) [x]` will expand `x` even
if `zeta := false`.
2023-10-25 03:12:35 -07:00
Leonardo de Moura
771ec8324c chore: fix configuration for UnificationHints 2023-10-25 03:12:35 -07:00
Leonardo de Moura
691defdc5d chore: typos and PR feedback
Co-authored-by: Scott Morrison <scott.morrison@gmail.com>

Co-authored-by: Scott Morrison <scott.morrison@gmail.com>

Co-authored-by: Scott Morrison <scott.morrison@gmail.com>
2023-10-25 03:12:35 -07:00
Leonardo de Moura
3b831271ee fix: fixes #2669 #2281 2023-10-25 03:12:35 -07:00
Leonardo de Moura
3a13200772 refactor: add configuration options to control WHNF
This commit also removes parameter `simpleReduce` from discrimination
trees, and take WHNF configuration options.
Reason: it is more dynamic now. For example, the simplifier
will be able to use different configurations for discrimination tree insertion
and retrieval. We need this feature to address issues #2669 and #2281

This commit also removes the dead Meta.Config field `zetaNonDep`.
2023-10-25 03:12:35 -07:00
Leonardo de Moura
aecc83e2fc chore: add some doc strings and cleanup 2023-10-25 03:12:35 -07:00
tydeu
170fd845f2 feat: LAKE_PKG_URL_MAP 2023-10-25 04:17:53 -04:00
Denis Gorbachev
d126c099f4 doc: Update contribution guides (#2624)
* doc: update contribution guides

This moves the contribution guide from `doc/contributions.md` to `CONTRIBUTING.md`.
2023-10-25 13:05:55 +11:00
thorimur
6063deb6bd fix: rw ... at h unknown fvar bug (#2728) 2023-10-25 01:52:19 +00:00
thorimur
291e95e3c5 fix: add instantiateMVars to replaceLocalDecl (#2712)
* fix: `instantiateMVars` in `replaceLocalDecl`
* docs: update `replaceLocalDecl`
* test: `replaceLocalDecl` instantiates mvars
2023-10-25 10:26:09 +11:00
Buster Copley
bccbefdc1c fix: version numbers in code actions (#2721)
Co-authored-by: Richard Copley <buster@buster.me.uk>
2023-10-24 22:55:47 +11:00
Scott Morrison
d07ec56c33 chore: correcting typos (#2746) 2023-10-24 10:55:30 +00:00
Leonardo de Moura
b00c13a00e chore: remove "paper cut" when using Fin USize.size (#2724) 2023-10-24 21:06:35 +11:00
Mario Carneiro
eaf85607f4 fix: don't pack ._ files on MacOS (#2743) 2023-10-24 21:03:51 +11:00
Leonardo de Moura
a7323c9805 feat: use forall_prop_domain_congr in simp tactic
closes #1926
2023-10-23 06:19:19 -07:00
Leonardo de Moura
50d0aced7f feat: add auxiliary lemma for simp 2023-10-23 06:19:19 -07:00
Leonardo de Moura
0bd15be1a1 chore: fix tests output 2023-10-22 06:48:22 -07:00
Leonardo de Moura
370476cc14 fix: bug at substCore 2023-10-22 06:48:22 -07:00
Leonardo de Moura
9a7565d66c perf: closes #2552 2023-10-22 06:48:22 -07:00
Leonardo de Moura
52f1000955 chore: update doc, add support for modn 2023-10-20 19:07:48 -07:00
Leonardo de Moura
9d02e0ee6f chore: remove unnecessary % operations at Fin.mod and Fin.div
We now have the missing proofs `Nat.mod_le` and `Nat.div_le_self` in
core.
See:
https://github.com/leanprover/std4/pull/286#discussion_r1359807875
2023-10-20 19:07:48 -07:00
thorimur
1facbde113 test: ensure dsimp can use rfl thm constants 2023-10-20 19:06:40 -07:00
thorimur
b5e95bf632 fix: allow constants to be marked for dsimp 2023-10-20 19:06:40 -07:00
tydeu
6c20673737 refactor: change postUpdate? config to a decl 2023-10-20 21:38:31 -04:00
github-actions[bot]
b52317537c doc: update changelog 2023-10-20 18:51:37 +00:00
tydeu
6e98453189 chore: lake: test auto-manifest & update cleaning 2023-10-20 14:51:10 -04:00
tydeu
e435a45af7 feat: lake: create manifest on load if missing 2023-10-20 14:51:10 -04:00
Leonardo de Moura
419100d42b feat: add Simp.Config.ground for simplifying nested ground terms
This is an experimental new feature. We need more bells and whistles,
and `cbv` tactic for improving its performance.
2023-10-19 13:59:17 -07:00
tydeu
67b5cd9c0e feat: lake: run_io to execute IO at term elab time 2023-10-19 13:27:35 -04:00
tydeu
ca6d1fd47a chore: lake: simplify config decl syntax
* deprecate `:=` syntax in config decls
* standardize field syntax across `where` and `{...}`
2023-10-19 13:25:07 -04:00
Scott Morrison
fb0d0245db Revert "Cancel outstanding tasks on document edit in the language server" (#2703)
* Revert "perf: inline `checkInterrupted`"

This reverts commit 6494af4513.

* Revert "fix: switch to C++ interruption whitelist"

This reverts commit 5aae74199b.

* Revert "fix: do not throw interrupt exceptions inside pure functions"

This reverts commit c0e3b9568e.

* Revert "feat: cancel tasks on document edit"

This reverts commit a2e2481c51.

* Revert "feat: translate `interrupted` kernel exception"

This reverts commit 14c640c15e.

* Revert "feat: check task cancellation in elaborator"

This reverts commit 2070df2328.

* Revert "feat: move `check_interrupted` from unused thread class to `Task` cancellation"

This reverts commit bf48a18cf9.
2023-10-17 00:59:11 +00:00
SADIK KUZU
e0802d2dea fix: typos in specialize.cpp (#2702) 2023-10-17 00:58:10 +00:00
tydeu
4441662490 test: lake: tests/manifest -> tests/depTree
also clarify its difference  distinction with `tests/clone`
2023-10-16 13:35:24 -04:00
tydeu
8db978bb10 chore: lake: fail if no error in tests/serve 2023-10-16 13:35:24 -04:00
tydeu
894c3abb37 chore: lake: ignore manifest in examples/scripts 2023-10-16 13:35:24 -04:00
tydeu
2a91d3cf68 fix: lake: casing in tests/order/clean.sh 2023-10-16 13:35:24 -04:00
David Thrane Christiansen
d15a0a4acb chore: lake: explicit branch name in tests/clone 2023-10-16 09:29:31 +00:00
Mario Carneiro
e0cba05167 refactor: env extensions can only modify .extensions (#2661) 2023-10-16 14:22:09 +11:00
Scott Morrison
2f9c964753 chore: update RELEASES.md to reflect v4.2.0-rc2 (#2692) 2023-10-16 01:19:12 +00:00
Mario Carneiro
c0b021e196 fix: pp projection indices starting at 1 2023-10-15 14:25:00 -07:00
Scott Morrison
3e79ddda27 chore: add items to RELEASES.md (#2687) 2023-10-15 02:51:58 +00:00
Scott Morrison
1e74c6a348 feat: use nat_gcd in the kernel (#2533)
* feat: use nat_gcd in the kernel

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2023-10-15 13:49:41 +11:00
Scott Morrison
66ab016723 chore: simp tracing reports ← (#2621)
* chore: simp tracing reports ←

---------

Co-authored-by: Mario Carneiro <di.gama@gmail.com>
2023-10-15 12:12:10 +11:00
github-actions[bot]
6df09d16e5 doc: update changelog 2023-10-14 17:20:54 +00:00
Leonardo de Moura
b8af36fba0 chore: update comments at src/Lean/Meta/Basic.lean
Co-authored-by: Timo <timorcb@gmail.com>
2023-10-14 10:20:29 -07:00
Leonardo de Moura
29198371d9 chore: update comments at src/Lean/Meta/ExprDefEq.lean
Co-authored-by: Timo <timorcb@gmail.com>
2023-10-14 10:20:29 -07:00
Leonardo de Moura
3bc18797b0 fix: ensure transient cache results for different transparency modes don't mix up 2023-10-14 10:20:29 -07:00
Sebastian Ullrich
6d0a3287e0 fix: cache typos 2023-10-14 10:20:29 -07:00
Leonardo de Moura
e3b08060d0 fix: chore add workaround for corrupted cache 2023-10-14 10:20:29 -07:00
Leonardo de Moura
2253b788b4 perf: fine grain isDefEq cache for terms not containing metavariables 2023-10-14 10:20:29 -07:00
Arthur Adjedj
ff20a14c69 fix : make mk_no_confusion_type handle delta-reduction when generating telescope (#2501)
* fix : make `mk_no_confusion_type` handle delta-reduction when checking the inductive type.

* tests: extend `2500.lean`
2023-10-14 17:18:37 +11:00
mhuisi
d0ae87d13f chore: improve error 2023-10-13 16:42:19 +02:00
mhuisi
b5348786a6 fix: pre-dependency-build-mode compatibility 2023-10-13 16:42:19 +02:00
mhuisi
253a5b931d fix: correct handling of FileWorker restarts 2023-10-13 16:42:19 +02:00
mhuisi
9945fa04d6 feat: FileWorker handling of --no-build 2023-10-13 16:42:19 +02:00
Sebastian Ullrich
4e1d95ce58 feat: pass along extra print-paths flags and handle no-build Lake error in server 2023-10-13 16:42:19 +02:00
tydeu
f3de5eb1e8 feat: lake: --no-build to exit before a build 2023-10-13 14:42:58 +02:00
github-actions[bot]
9f63a9f288 doc: update changelog 2023-10-13 07:57:12 +00:00
Sebastian Ullrich
6494af4513 perf: inline checkInterrupted
Amazingly, the extra result allocation seems to have triggered a mathlib
heartbeat timeout
2023-10-13 09:52:26 +02:00
Sebastian Ullrich
5aae74199b fix: switch to C++ interruption whitelist 2023-10-13 09:52:26 +02:00
Sebastian Ullrich
c0e3b9568e fix: do not throw interrupt exceptions inside pure functions 2023-10-13 09:52:26 +02:00
Sebastian Ullrich
a2e2481c51 feat: cancel tasks on document edit 2023-10-13 09:52:26 +02:00
Sebastian Ullrich
14c640c15e feat: translate interrupted kernel exception 2023-10-13 09:52:26 +02:00
Sebastian Ullrich
2070df2328 feat: check task cancellation in elaborator 2023-10-13 09:52:26 +02:00
Sebastian Ullrich
bf48a18cf9 feat: move check_interrupted from unused thread class to Task cancellation 2023-10-13 09:52:26 +02:00
tydeu
42802f9788 feat: lake: postUpdate? + test 2023-10-13 02:31:06 -04:00
tydeu
275af93904 test: lake: show module with failed import 2023-10-12 22:16:04 -04:00
tydeu
99b78bcc23 fix: stdin := .null in IO.Process.output 2023-10-12 08:55:26 +02:00
Mario Carneiro
6bdfde7939 fix: quot reduction bug 2023-10-11 21:25:34 -07:00
Scott Morrison
5d096c3fd8 chore: make Environment.add private (#2642)
* feat: replay constants into an Environment

* suggestions from code review

* chore: make Environment.add private

* patch Lake to use Environment.add via extern
2023-10-12 15:00:27 +11:00
Mario Carneiro
b558b5b912 perf: use quick_is_def_eq first 2023-10-11 19:35:16 -07:00
Scott Morrison
57e23917b6 fix: implementation of Array.anyMUnsafe
move test
2023-10-11 11:20:45 +02:00
Mario Carneiro
14e626a925 feat: ToMessageData (α × β) instance 2023-10-11 10:12:06 +02:00
Wojciech Nawrocki
856a9b5153 fix: treat pretty-printed names as strings
I initially expected `Name`s to always faithfully represent internal data, in particular that a name with macro scopes would have a form such as  ```foo._@.Module._hyg.1``, and that tombstones would only appear in types that represent pretty-printed output such as as `String` or `Format`. However, that is not what happens. We have `sanitizeNames` which rewrites the `userName` field of local hypotheses to be `Name.str .anonymous "blah✝"`.

Then in the server code, we put these into `names : Array Name`e. This works fine for displaying in the infoview, but if we try to deserialize an `InteractiveHypothesisBundle` inside an RPC method for widget purposes, the `FromJson Name` instance blows up in `String.toName`.

I think my preferred solution is to, rather than 'fix' `String.toName` to accept these names with tombstones, stop pretending that they are actual `Name`s and re-type `InteractiveHypothesisBundle.names : Array String`. This should be a backwards-compatible change w.r.t. infoview code as the JSON representation is a string in either case. It is not backwards compatible w.r.t. meta code that uses this field.
2023-10-11 09:51:14 +02:00
David Christiansen
0700925bbe doc: add a brief description of ccache 2023-10-11 09:30:46 +02:00
David Christiansen
7450a8cfa3 doc: describe commit conventions for update-stage0
Updates to stage0 should be their own commits.
2023-10-11 09:30:46 +02:00
Scott Morrison
97f5ad7804 chore: change trustCompiler axiom to True (#2662) 2023-10-11 06:59:03 +00:00
1094 changed files with 12299 additions and 4931 deletions

View File

@@ -33,7 +33,7 @@ assignees: ''
### Versions
[Output of `lean --version` in the folder that the issue occured in]
[Output of `#eval Lean.versionString` or of `lean --version` in the folder that the issue occured in]
[OS version]
### Additional Information

View File

@@ -1,14 +1,14 @@
* [ ] Put an X in this bracket to confirm you have read the
[External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/doc/contributions.md).
# Read this section before submitting
* Please put the link to your `RFC` or `bug` issue here.
PRs missing this link will be marked as `missing RFC`.
* Ensure your PR follows the [External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/CONTRIBUTING.md).
* Please make sure the PR has excellent documentation and tests. If we label it `missing documentation` or `missing tests` then it needs fixing!
* Include the link to your `RFC` or `bug` issue in the description.
* If the issue does not already have approval from a developer, submit the PR as draft.
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR.
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
* Remove this section, up to and including the `---` before submitting.
* If that issue does not already have approval from a developer,
please be sure to open this PR in "Draft" mode.
---
* Please make sure the PR has excellent documentation and tests.
If we label it `missing documentation` or `missing tests` then it needs fixing!
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels
yourself, by writing a comment containing one of these labels on its own line.
Closes #0000 (`RFC` or `bug` issue number fixed by this PR, if any)

View File

@@ -1,33 +0,0 @@
name: add PR to changelog
on:
# needs read/write GH token, do *not* execute arbitrary code from PR
pull_request_target:
types: [closed]
jobs:
update-changelog:
if: |
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'changelog') &&
github.base_ref == 'master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# needs sufficiently elevated token to override branch protection rules
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- name: Update changelog
run: |
set -euxo pipefail
escaped_link=$(sed -e 's/[\/&]/\\&/g' <<'EOF'
[${{ github.event.pull_request.title}}](${{ github.event.pull_request.html_url }})
EOF
)
# insert link below first dashes line (https://stackoverflow.com/a/9453461/161659)
sed -i "0,/^---*/s/^---*/\0\n\n* $escaped_link./" RELEASES.md
# commit as github-actions bot (https://github.com/orgs/community/discussions/26560#discussioncomment-3252339)
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git commit -i RELEASES.md -m "doc: update changelog"
git push

View File

@@ -6,8 +6,8 @@ on:
tags:
- '*'
pull_request:
branches:
- master
types: [opened, synchronize, reopened, labeled]
merge_group:
schedule:
- cron: '0 7 * * *' # 8AM CET/11PM PT
@@ -16,18 +16,185 @@ concurrency:
cancel-in-progress: true
jobs:
set-nightly:
# This job determines various settings for the following CI runs; see the `outputs` for details
configure:
runs-on: ubuntu-latest
outputs:
nightly: ${{ steps.set.outputs.nightly }}
# Should we run only a quick CI? Yes on a pull request without the full-ci label
quick: ${{ steps.set-quick.outputs.quick }}
# The build matrix, dynamically generated here
matrix: ${{ steps.set-matrix.outputs.result }}
# Should we make a nightly release? If so, this output contains the lean version string, else it is empty
nightly: ${{ steps.set-nightly.outputs.nightly }}
# Should this be the CI for a tagged release?
# Yes only if a tag is pushed to the `leanprover` repository, and the tag is "v" followed by a valid semver.
# It sets `set-release.outputs.RELEASE_TAG` to the tag
# and sets `set-release.outputs.{LEAN_VERSION_MAJOR,LEAN_VERSION_MINOR,LEAN_VERSION_PATCH,LEAN_SPECIAL_VERSION_DESC}`
# to the semver components parsed via regex.
LEAN_VERSION_MAJOR: ${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}
LEAN_VERSION_MINOR: ${{ steps.set-release.outputs.LEAN_VERSION_MINOR }}
LEAN_VERSION_PATCH: ${{ steps.set-release.outputs.LEAN_VERSION_PATCH }}
LEAN_SPECIAL_VERSION_DESC: ${{ steps.set-release.outputs.LEAN_SPECIAL_VERSION_DESC }}
RELEASE_TAG: ${{ steps.set-release.outputs.RELEASE_TAG }}
steps:
- name: Run quick CI?
id: set-quick
env:
quick: ${{
github.event_name == 'pull_request' && !contains( github.event.pull_request.labels.*.name, 'full-ci')
}}
run: |
echo "quick=${{env.quick}}" >> $GITHUB_OUTPUT
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
with:
script: |
const quick = ${{ steps.set-quick.outputs.quick }};
console.log(`quick: ${quick}`)
let matrix = [
{
// portable release build: use channel with older glibc (2.27)
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
"quick": false,
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{}}\" --run \"bash -euxo pipefail {0}\"",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
// reverse-ffi needs to be updated to link to LLVM libraries
"CTEST_OPTIONS": "-E 'foreign|leanlaketest_reverse-ffi'",
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
},
{
"name": "Linux release",
"os": "ubuntu-latest",
"release": true,
"quick": true,
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{}}\" --run \"bash -euxo pipefail {0}\"",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
{
"name": "Linux",
"os": "ubuntu-latest",
"check-stage3": true,
"test-speedcenter": true,
"quick": false,
},
{
"name": "Linux Debug",
"os": "ubuntu-latest",
"quick": false,
"CMAKE_OPTIONS": "-DCMAKE_BUILD_TYPE=Debug",
// exclude seriously slow tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},
{
"name": "Linux fsanitize",
"os": "ubuntu-latest",
"quick": false,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined -DLEANC_EXTRA_FLAGS='-fsanitize=address,undefined -fsanitize-link-c++-runtime' -DSMALL_ALLOCATOR=OFF -DBSYMBOLIC=OFF",
// exclude seriously slow/problematic tests (laketests crash)
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},
{
"name": "macOS",
"os": "macos-latest",
"release": true,
"quick": false,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
},
{
"name": "macOS aarch64",
"os": "macos-latest",
"release": true,
"quick": false,
"cross": true,
"shell": "bash -euxo pipefail {0}",
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "EXTRA_FLAGS=--target=aarch64-apple-darwin ../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
},
{
"name": "Windows",
"os": "windows-2022",
"release": true,
"quick": false,
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\" -DUSE_GMP=OFF",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd"
},
{
"name": "Linux aarch64",
"os": "ubuntu-latest",
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"quick": false,
"cross": true,
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{ localSystem.config = \\\"aarch64-unknown-linux-gnu\\\"; }}\" --run \"bash -euxo pipefail {0}\"",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "EXTRA_FLAGS=--target=aarch64-unknown-linux-gnu ../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
},
{
"name": "Linux 32bit",
"os": "ubuntu-latest",
// Use 32bit on stage0 and stage1 to keep oleans compatible
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86",
"cmultilib": true,
"release": true,
"quick": false,
"cross": true,
"shell": "bash -euxo pipefail {0}"
},
{
"name": "Web Assembly",
"os": "ubuntu-latest",
// Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32",
"wasm": true,
"cmultilib": true,
"release": true,
"quick": false,
"cross": true,
"shell": "bash -euxo pipefail {0}",
// Just a few selected tests because wasm is slow
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean\""
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
if (quick) {
return matrix.filter((job) => job.quick)
} else {
return matrix
}
- name: Checkout
uses: actions/checkout@v3
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
- name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
id: set
id: set-nightly
run: |
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
@@ -39,26 +206,9 @@ jobs:
fi
fi
# This job determines if this CI build is for a tagged release.
# It only runs when a tag is pushed to the `leanprover` repository.
# It sets `set-release.outputs.RELEASE_TAG` to the tag, if the tag is "v" followed by a valid semver,
# and sets `set-release.outputs.{LEAN_VERSION_MAJOR,LEAN_VERSION_MINOR,LEAN_VERSION_PATCH,LEAN_SPECIAL_VERSION_DESC}`
# to the semver components parsed via regex.
set-release:
runs-on: ubuntu-latest
outputs:
LEAN_VERSION_MAJOR: ${{ steps.set.outputs.LEAN_VERSION_MAJOR }}
LEAN_VERSION_MINOR: ${{ steps.set.outputs.LEAN_VERSION_MINOR }}
LEAN_VERSION_PATCH: ${{ steps.set.outputs.LEAN_VERSION_PATCH }}
LEAN_SPECIAL_VERSION_DESC: ${{ steps.set.outputs.LEAN_SPECIAL_VERSION_DESC }}
RELEASE_TAG: ${{ steps.set.outputs.RELEASE_TAG }}
steps:
- name: Checkout
uses: actions/checkout@v3
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4'
- name: Check for official release
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4'
id: set
id: set-release
run: |
TAG_NAME=${GITHUB_REF##*/}
@@ -87,98 +237,17 @@ jobs:
fi
build:
needs: [set-nightly, set-release]
needs: [configure]
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
strategy:
matrix:
include: ${{fromJson(needs.configure.outputs.matrix)}}
# complete all jobs
fail-fast: false
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.shell || 'nix-shell --run "bash -euxo pipefail {0}"' }}
strategy:
matrix:
include:
# portable release build: use channel with older glibc (2.27)
- name: Linux LLVM
os: ubuntu-latest
release: false
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{}}" --run "bash -euxo pipefail {0}"
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst
prepare-llvm: ../script/prepare-llvm-linux.sh lean-llvm*
binary-check: ldd -v
# foreign code may be linked against more recent glibc
# reverse-ffi needs to be updated to link to LLVM libraries
CTEST_OPTIONS: -E 'foreign|leanlaketest_reverse-ffi'
CMAKE_OPTIONS: -DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config
- name: Linux release
os: ubuntu-latest
release: true
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{}}" --run "bash -euxo pipefail {0}"
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst
prepare-llvm: ../script/prepare-llvm-linux.sh lean-llvm*
binary-check: ldd -v
# foreign code may be linked against more recent glibc
CTEST_OPTIONS: -E 'foreign'
- name: Linux
os: ubuntu-latest
check-stage3: true
test-speedcenter: true
- name: Linux Debug
os: ubuntu-latest
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Debug
# exclude seriously slow tests
CTEST_OPTIONS: -E 'interactivetest|leanpkgtest|laketest|benchtest'
- name: Linux fsanitize
os: ubuntu-latest
# turn off custom allocator & symbolic functions to make LSAN do its magic
CMAKE_OPTIONS: -DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined -DLEANC_EXTRA_FLAGS='-fsanitize=address,undefined -fsanitize-link-c++-runtime' -DSMALL_ALLOCATOR=OFF -DBSYMBOLIC=OFF
# exclude seriously slow/problematic tests (laketests crash)
CTEST_OPTIONS: -E 'interactivetest|leanpkgtest|laketest|benchtest'
- name: macOS
os: macos-latest
release: true
shell: bash -euxo pipefail {0}
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst
prepare-llvm: ../script/prepare-llvm-macos.sh lean-llvm*
binary-check: otool -L
tar: gtar # https://github.com/actions/runner-images/issues/2619
- name: macOS aarch64
os: macos-latest
release: true
cross: true
shell: bash -euxo pipefail {0}
CMAKE_OPTIONS: -DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-darwin_aarch64
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst
prepare-llvm: EXTRA_FLAGS=--target=aarch64-apple-darwin ../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*
binary-check: otool -L
tar: gtar # https://github.com/actions/runner-images/issues/2619
- name: Windows
os: windows-2022
release: true
shell: msys2 {0}
CMAKE_OPTIONS: -G "Unix Makefiles" -DUSE_GMP=OFF
# for reasons unknown, interactivetests are flaky on Windows
CTEST_OPTIONS: --repeat until-pass:2
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst
prepare-llvm: ../script/prepare-llvm-mingw.sh lean-llvm*
binary-check: ldd
- name: Linux aarch64
os: ubuntu-latest
CMAKE_OPTIONS: -DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64
release: true
cross: true
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{ localSystem.config = \"aarch64-unknown-linux-gnu\"; }}" --run "bash -euxo pipefail {0}"
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst
prepare-llvm: EXTRA_FLAGS=--target=aarch64-unknown-linux-gnu ../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*
- name: Web Assembly
os: ubuntu-latest
# Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
CMAKE_OPTIONS: -DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX="" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
wasm: true
cross: true
shell: bash -euxo pipefail {0}
# Just a few selected test because wasm is slow
CTEST_OPTIONS: -R "leantest_1007\.lean|leantest_Format\.lean|leanruntest\_1037.lean|leanruntest_ac_rfl\.lean"
# complete all jobs
fail-fast: false
name: ${{ matrix.name }}
env:
# must be inside workspace
@@ -197,11 +266,13 @@ jobs:
uses: actions/checkout@v3
with:
submodules: true
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Install Nix
uses: cachix/install-nix-action@v18
with:
install_url: https://releases.nixos.org/nix/nix-2.12.0/install
if: matrix.os == 'ubuntu-latest' && !matrix.wasm
if: matrix.os == 'ubuntu-latest' && !matrix.cmultilib
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
@@ -214,7 +285,7 @@ jobs:
brew install ccache tree zstd coreutils gmp
if: matrix.os == 'macos-latest'
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v11
uses: mymindstorm/setup-emsdk@v12
with:
version: 3.1.44
actions-cache-folder: emsdk
@@ -223,7 +294,7 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib ccache
if: matrix.wasm
if: matrix.cmultilib
- name: Cache
uses: actions/cache@v3
with:
@@ -248,21 +319,23 @@ jobs:
mkdir build
cd build
ulimit -c unlimited # coredumps
OPTIONS=()
# this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
wget -q ${{ matrix.llvm-url }}
PREPARE="$(${{ matrix.prepare-llvm }})"
eval "OPTIONS+=($PREPARE)"
fi
if [[ -n '${{ matrix.release }}' && -n '${{ needs.set-nightly.outputs.nightly }}' ]]; then
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.set-nightly.outputs.nightly }})
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.nightly }}' ]]; then
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.nightly }})
fi
if [[ -n '${{ matrix.release }}' && -n '${{ needs.set-release.outputs.RELEASE_TAG }}' ]]; then
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ needs.set-release.outputs.LEAN_VERSION_MAJOR }})
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ needs.set-release.outputs.LEAN_VERSION_MINOR }})
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ needs.set-release.outputs.LEAN_VERSION_PATCH }})
if [[ -n '${{ matrix.release }}' && -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
OPTIONS+=(-DLEAN_VERSION_MAJOR=${{ needs.configure.outputs.LEAN_VERSION_MAJOR }})
OPTIONS+=(-DLEAN_VERSION_MINOR=${{ needs.configure.outputs.LEAN_VERSION_MINOR }})
OPTIONS+=(-DLEAN_VERSION_PATCH=${{ needs.configure.outputs.LEAN_VERSION_PATCH }})
OPTIONS+=(-DLEAN_VERSION_IS_RELEASE=1)
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.set-release.outputs.LEAN_SPECIAL_VERSION_DESC }})
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }})
fi
# contortion to support empty OPTIONS with old macOS bash
cmake .. ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
@@ -273,13 +346,13 @@ jobs:
- name: List Install Tree
run: |
# omit contents of Init/, ...
tree --du -h lean-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
tree --du -h lean-*-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
- name: Pack
run: |
dir=$(echo lean-*)
dir=$(echo lean-*-*)
mkdir pack
# high-compression tar.zst + zip for release, fast tar.zst otherwise
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.set-nightly.outputs.nightly }}' || -n '${{ needs.set-release.outputs.RELEASE_TAG }}' ]]; then
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.configure.outputs.nightly }}' || -n '${{ needs.configure.outputs.RELEASE_TAG }}' ]]; then
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
zip -rq pack/$dir.zip $dir
else
@@ -300,22 +373,22 @@ jobs:
ulimit -c unlimited # coredumps
# exclude nonreproducible test
ctest -j4 --output-on-failure ${{ matrix.CTEST_OPTIONS }} < /dev/null
if: matrix.wasm || !matrix.cross
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.quick == 'false'
- name: Check Test Binary
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
if: ${{ !matrix.cross }}
if: ${{ !matrix.cross && needs.configure.outputs.quick == 'false' }}
- name: Build Stage 2
run: |
cd build
ulimit -c unlimited # coredumps
make -j4 stage2
if: matrix.build-stage2 || matrix.check-stage3
if: matrix.test-speedcenter
- name: Check Stage 3
run: |
cd build
ulimit -c unlimited # coredumps
make -j4 check-stage3
if: matrix.check-stage3
if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks
run: |
echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
@@ -328,7 +401,7 @@ jobs:
cd build
ulimit -c unlimited # coredumps
make update-stage0 && make -j4
if: matrix.name == 'Linux'
if: matrix.name == 'Linux' && needs.configure.outputs.quick == 'false'
- name: CCache stats
run: ccache -s
- name: Show stacktrace for coredumps
@@ -352,6 +425,21 @@ jobs:
./build/stage2/bin/lean
./build/stage2/lib/lean/libleanshared.so
# This job collects results from all the matrix jobs
# This can be made the “required” job, instead of listing each
# matrix job separately
all-done:
name: Build matrix complete
runs-on: ubuntu-latest
needs: build
if: ${{ always() }}
steps:
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
uses: actions/github-script@v7
with:
script: |
core.setFailed('Some jobs failed')
# This job creates releases from tags
# (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.)
# We do not attempt to automatically construct a changelog here:
@@ -375,8 +463,8 @@ jobs:
# This job creates nightly releases during the cron job.
# It is responsible for creating the tag, and automatically generating a changelog.
release-nightly:
needs: [set-nightly, build]
if: needs.set-nightly.outputs.nightly
needs: [configure, build]
if: needs.configure.outputs.nightly
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -392,8 +480,9 @@ jobs:
run: |
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
git fetch nightly --tags
git tag ${{ needs.set-nightly.outputs.nightly }}
git push nightly ${{ needs.set-nightly.outputs.nightly }}
git tag ${{ needs.configure.outputs.nightly }}
git push nightly ${{ needs.configure.outputs.nightly }}
git push -f origin refs/tags/${{ needs.configure.outputs.nightly }}:refs/heads/nightly
last_tag=$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[-0-9]*" | head -n 1)
echo -e "*Changes since ${last_tag}:*\n\n" > diff.md
git show $last_tag:RELEASES.md > old.md
@@ -408,7 +497,7 @@ jobs:
prerelease: true
files: artifacts/*/*
fail_on_unmatched_files: true
tag_name: ${{ needs.set-nightly.outputs.nightly }}
tag_name: ${{ needs.configure.outputs.nightly }}
repository: ${{ github.repository_owner }}/lean4-nightly
env:
GITHUB_TOKEN: ${{ secrets.PUSH_NIGHTLY_TOKEN }}

View File

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

View File

@@ -6,8 +6,7 @@ on:
tags:
- '*'
pull_request:
branches:
- master
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -18,7 +17,7 @@ jobs:
runs-on: ${{ matrix.os }}
defaults:
run:
shell: nix -v --experimental-features "nix-command flakes" run .#ciShell -- bash -euxo pipefail {0}
shell: nix run .#ciShell -- bash -euxo pipefail {0}
strategy:
matrix:
include:
@@ -30,18 +29,13 @@ jobs:
fail-fast: false
name: ${{ matrix.name }}
env:
NIX_BUILD_ARGS: -v --print-build-logs --fallback
NIX_BUILD_ARGS: --print-build-logs --fallback
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Nix
uses: cachix/install-nix-action@v18
with:
# https://github.com/NixOS/nix/issues/6572
install_url: https://releases.nixos.org/nix/nix-2.7.0/install
extra_nix_config: |
extra-sandbox-paths = /nix/var/cache/ccache
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Set Up Nix Cache
uses: actions/cache@v3
with:
@@ -55,8 +49,13 @@ jobs:
run: |
# Nix seems to mutate the cache, so make a copy
cp -r nix-store-cache nix-store-cache-copy || true
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
extra-conf: |
extra-sandbox-paths = /nix/var/cache/ccache?
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
- name: Prepare CCache Cache
shell: bash -euxo pipefail {0}
run: |
sudo mkdir -m0770 -p /nix/var/cache/ccache
sudo chown -R $USER /nix/var/cache/ccache
@@ -69,7 +68,6 @@ jobs:
restore-keys: |
${{ matrix.name }}-nix-ccache
- name: Further Set Up CCache Cache
shell: bash -euxo pipefail {0}
run: |
sudo chown -R root:nixbld /nix/var/cache
sudo chmod -R 770 /nix/var/cache

View File

@@ -16,25 +16,16 @@ on:
jobs:
on-success:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success' && github.repository == 'leanprover/lean4'
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
steps:
- name: Retrieve information about the original workflow
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
# This action is deprecated and archived, but it seems hard to find a better solution for getting the PR number
# see https://github.com/orgs/community/discussions/25220 for some discussion
id: workflow-info
with:
token: ${{ secrets.GITHUB_TOKEN }}
sourceRunId: ${{ github.event.workflow_run.id }}
- name: Checkout
# Only proceed if the previous workflow had a pull request number.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/checkout@v3
with:
token: ${{ secrets.PR_RELEASES_TOKEN }}
# Since `workflow_run` runs on master, we need to specify which commit to check out,
# so that we tag the PR.
ref: ${{ steps.workflow-info.outputs.targetCommitSha }}
# We need a full checkout, so that we can push the PR commits to the `lean4-pr-releases` repo.
fetch-depth: 0
- name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
@@ -45,14 +36,22 @@ jobs:
path: artifacts
name: build-.*
name_is_regexp: true
- name: Prepare release
- name: Push branch and tag
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
git init --bare lean4.git
git -C lean4.git remote add origin https://github.com/${{ github.repository_owner }}/lean4.git
git -C lean4.git fetch -n origin master
git -C lean4.git fetch -n origin "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} "${{ steps.workflow-info.outputs.sourceHeadSha }}"
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
- name: Delete existing release if present
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
# Try to delete any existing release for the current PR.
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
env:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release
@@ -72,35 +71,143 @@ jobs:
- name: Add label
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions-ecosystem/action-add-labels@v1
uses: actions/github-script@v7
with:
number: ${{ steps.workflow-info.outputs.pullRequestNumber }}
labels: toolchain-available
script: |
await github.rest.issues.addLabels({
issue_number: ${{ steps.workflow-info.outputs.pullRequestNumber }},
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['toolchain-available']
})
# Next, determine the most recent nightly release in this PR's history.
- name: Find most recent nightly in feature branch
id: most-recent-nightly-tag
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
run: |
git -C lean4.git remote add nightly https://github.com/leanprover/lean4-nightly.git
git -C lean4.git fetch nightly '+refs/tags/nightly-*:refs/tags/nightly-*'
git -C lean4.git tag --merged "${{ steps.workflow-info.outputs.sourceHeadSha }}" --list "nightly-*" \
| sort -rV | head -n 1 | sed "s/^nightly-*/MOST_RECENT_NIGHTLY=/" | tee -a $GITHUB_ENV
- name: 'Setup jq'
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: dcarbone/install-jq-action@v1.0.1
# Check that the most recently nightly coincides with 'git merge-base HEAD master'
- name: Check merge-base and nightly-testing-YYYY-MM-DD
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: ready
run: |
echo "Most recent nightly in your branch: $MOST_RECENT_NIGHTLY"
NIGHTLY_SHA=$(git -C lean4.git rev-parse "nightly-$MOST_RECENT_NIGHTLY^{commit}")
echo "SHA of most recent nightly: $NIGHTLY_SHA"
MERGE_BASE_SHA=$(git -C lean4.git merge-base origin/master "${{ steps.workflow-info.outputs.sourceHeadSha }}")
echo "SHA of merge-base: $MERGE_BASE_SHA"
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
echo "Most recent nightly tag agrees with the merge base."
REMOTE_BRANCHES=$(git ls-remote -h https://github.com/leanprover-community/mathlib4.git nightly-testing-$MOST_RECENT_NIGHTLY)
if [[ -n "$REMOTE_BRANCHES" ]]; then
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' branch."
MESSAGE=""
else
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' branch."
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the 'nightly-testing-$MOST_RECENT_NIGHTLY' branch does not exist there yet. We will retry when you push more commits. It may be necessary to rebase onto 'nightly' tomorrow."
fi
else
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
git -C lean4.git log -10 origin/master
MESSAGE="- ❗ Mathlib CI will not be attempted unless you rebase your PR onto the 'nightly' branch."
fi
if [[ -n "$MESSAGE" ]]; then
echo "Checking existing messages"
# Use GitHub API to check if a comment already exists
existing_comment=$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
| jq '.[] | select(.body | startswith("- ❗ Mathlib") or startswith("- ✅ Mathlib") or startswith("- ❌ Mathlib") or startswith("- 💥 Mathlib") or startswith("- 🟡 Mathlib"))')
existing_comment_id=$(echo "$existing_comment" | jq -r .id)
existing_comment_body=$(echo "$existing_comment" | jq -r .body)
if [[ "$existing_comment_body" != *"$MESSAGE"* ]]; then
MESSAGE="$MESSAGE ($(date "+%Y-%m-%d %H:%M:%S"))"
echo "Posting message to the comments: $MESSAGE"
# Append new result to the existing comment or post a new comment
# It's essential we use the MATHLIB4_BOT token here, so that Mathlib CI can subsequently edit the comment.
if [ -z "$existing_comment_id" ]; then
# Post new comment with a bullet point
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
curl -L -s \
-X POST \
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
-d "$(jq --null-input --arg val "$MESSAGE" '{"body": $val}')" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
else
# Append new result to the existing comment
echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
curl -L -s \
-X PATCH \
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \
-d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \
"https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id"
fi
else
echo "The message already exists in the comment body."
fi
echo "mathlib_ready=false" >> $GITHUB_OUTPUT
else
echo "mathlib_ready=true" >> $GITHUB_OUTPUT
fi
# We next automatically create a Mathlib branch using this toolchain.
# Mathlib CI will be responsible for reporting back success or failure
# to the PR comments asynchronously.
- name: Cleanup workspace
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
run: |
sudo rm -rf *
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
uses: actions/checkout@v2
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v3
with:
repository: leanprover-community/mathlib4
token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing # This is more likely than `master` to work with the base of this PR.
fetch-depth: 0
ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches.
- name: Check if branch exists
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
id: check_branch
run: |
git config user.name "leanprover-community-mathlib4-bot"
git config user.email "leanprover-community-mathlib4-bot@users.noreply.github.com"
if git branch -r | grep -q "nightly-testing-${MOST_RECENT_NIGHTLY}"; then
BASE=nightly-testing-${MOST_RECENT_NIGHTLY}
else
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
BASE=nightly-testing
fi
echo "Using base branch: $BASE"
git checkout $BASE
EXISTS=$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)
echo "Branch exists: $EXISTS"
if [ "$EXISTS" = "0" ]; then
@@ -112,15 +219,12 @@ jobs:
else
echo "Branch already exists, pushing an empty commit."
git checkout lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
# The Mathlib `nightly-testing` branch may have moved since this branch was created, so merge their changes.
# If the base of this Lean4 PR becomes significantly older than the nightly being used by `nightly-testing`
# this will cause breakages rather than fixing them!
# Without cumbersome requirements that Lean4 PRs are based off nightlies, I'm not sure there is a perfect solution here.
git merge nightly-testing --strategy-option ours --no-commit --allow-unrelated-histories
# The Mathlib `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
git merge $BASE --strategy-option ours --no-commit --allow-unrelated-histories
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi
- name: Push changes
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
run: |
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}

20
.github/workflows/pr-title.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Check PR title for commit convention
on:
merge_group:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
check-pr-title:
runs-on: ubuntu-latest
steps:
- name: Check PR title
uses: actions/github-script@v7
with:
script: |
const msg = context.payload.pull_request? context.payload.pull_request.title : context.payload.merge_group.head_commit.message;
console.log(`Message: ${msg}`)
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(msg)) {
core.setFailed('PR title does not follow the Commit Convention (https://leanprover.github.io/lean4/doc/dev/commit_convention.html).');
}

View File

@@ -1,31 +0,0 @@
name: sanity-check opened PRs
on:
# needs read/write GH token, do *not* execute arbitrary code from PR
pull_request_target:
types: [opened]
jobs:
check-pr:
runs-on: ubuntu-latest
steps:
- name: Check Commit Message
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: commits } = await github.rest.pulls.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
console.log(commits[0].commit.message);
// check first commit only (and only once) since later commits might be intended to be squashed away
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(commits[0].commit.message)) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: 'Thanks for your contribution! Please make sure to follow our [Commit Convention](https://leanprover.github.io/lean4/doc/dev/commit_convention.html).',
});
}

64
.github/workflows/update-stage0.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Update stage0
# This action will update stage0 on master as soon as
# src/stdlib_flags.h and stage0/src/stdlib_flags.h
# are out of sync there, or when manually triggered.
# The update bypasses the merge queue to be quick.
# Also see <doc/dev/bootstrap.md>.
on:
push:
branches:
- 'master'
workflow_dispatch:
concurrency:
group: stage0
cancel-in-progress: true
jobs:
update-stage0:
runs-on: ubuntu-latest
steps:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v3
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
- name: Check if automatic update is needed
if: github.event_name == 'push'
run: |
if diff -u src/stdlib_flags.h stage0/src/stdlib_flags.h
then
echo "src/stdlib_flags.h and stage0/src/stdlib_flags.h agree, nothing to do"
echo "should_update_stage0=no" >> "$GITHUB_ENV"
fi
- name: Setup git user
if: env.should_update_stage0 == 'yes'
run: |
git config --global user.name "Lean stage0 autoupdater"
git config --global user.email "<>"
- if: env.should_update_stage0 == 'yes'
uses: DeterminateSystems/nix-installer-action@main
# Would be nice, but does not work yet:
# https://github.com/DeterminateSystems/magic-nix-cache/issues/39
# This action does not run that often and building runs in a few minutes, so ok for now
#- if: env.should_update_stage0 == 'yes'
# uses: DeterminateSystems/magic-nix-cache-action@v2
- if: env.should_update_stage0 == 'yes'
name: Install Cachix
uses: cachix/cachix-action@v12
with:
name: lean4
- if: env.should_update_stage0 == 'yes'
run: nix run .#update-stage0-commit
- if: env.should_update_stage0 == 'yes'
run: git show --stat
- if: env.should_update_stage0 == 'yes' && github.event_name == 'push'
name: Sanity check # to avoid loops
run: |
diff -u src/stdlib_flags.h stage0/src/stdlib_flags.h || exit 1
- if: env.should_update_stage0 == 'yes'
run: git push origin

2
.gitignore vendored
View File

@@ -2,6 +2,8 @@
\#*
.#*
*.lock
.lake
lake-manifest.json
build
!/src/lake/Lake/Build
GPATH

View File

@@ -1,7 +0,0 @@
{
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"[markdown]": {
"rewrap.wrappingColumn": 70
}
}

View File

@@ -11,7 +11,7 @@ foreach(var ${vars})
list(APPEND STAGE0_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
list(APPEND CL_ARGS "-D${var}=${${var}}")
if("${var}" STREQUAL "USE_GMP")
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION")
# must forward options that generate incompatible .olean format
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
endif()
@@ -35,6 +35,8 @@ ExternalProject_add(stage0
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, `CHECK_OLEAN_VERSION=ON` in CI will override this as we need to
# embed the githash into the stage 1 library built by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install

22
CODEOWNERS Normal file
View File

@@ -0,0 +1,22 @@
# Code Owners
#
# Documents responsible people per component.
# Listed persons will automatically be asked by GitHub to review a PR touching these paths.
# If multiple names are listed, a review by any of them is considered sufficient by default.
/.github/ @Kha @semorrison
/RELEASES.md @semorrison
/src/ @leodemoura @Kha
/src/Init/IO.lean @joehendrix
/src/kernel/ @leodemoura
/src/lake/ @tydeu
/src/Lean/Compiler/ @leodemoura
/src/Lean/Data/Lsp/ @mhuisi
/src/Lean/Elab/Deriving/ @semorrison
/src/Lean/Elab/Tactic/ @semorrison
/src/Lean/Meta/Tactic/ @leodemoura
/src/Lean/Parser/ @Kha
/src/Lean/PrettyPrinter/ @Kha
/src/Lean/Server/ @mhuisi
/src/Lean/Widget/ @Vtec234
/src/runtime/io.cpp @joehendrix

View File

@@ -1,61 +1,79 @@
# Contribution Guidelines
External Contribution Guidelines
============
Thank you for your interest in contributing to Lean! There are many ways to contribute and we appreciate all of them.
In the past, we accepted most pull requests. This practice produced hard to maintain code, performance problems, and bugs. In order to improve the quality and maintainability of our codebase, we've established the following guidelines for external contributions.
## Bug reports
Helpful links
-------
Bug reports as new issues are always welcome. Please check the existing [issues](https://github.com/leanprover/lean4/issues) first.
Reduce the issue to a self-contained, reproducible test case.
If you have the chance, before reporting a bug, please search existing issues, as it's possible that
someone else has already reported your error.
If you're not sure if something is a bug or not, feel free to file a bug anyway. You may also want to discuss it with the Lean
community using the [lean4 Zulip channel](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4).
* [Development Setup](./doc/dev/index.md)
* [Testing](./doc/dev/testing.md)
* [Commit convention](./doc/dev/commit_convention.md)
## Simple fixes
Before You Submit a Pull Request (PR):
-------
Simple fixes for **typos and clear bugs** are welcome.
**Start with an Issue**: Before submitting a PR, always open an issue discussing the problem you wish to solve or the feature you'd like to add. Use the prefix `RFC:` (request for comments) if you are proposing a new feature. Ask for feedback from other users. Take the time to summarize all the feedback. This allows the maintainers to evaluate your proposal more efficiently. When creating a RFC, consider the following questions:
# **IMPORTANT**
- **User Experience**: How does this feature improve the user experience?
We are currently overwhelmed. We respectfully request that you hold off on submitting Pull Requests and creating Request for Comments (RFCs) at this time. Our team is actively seeking funding to expand the Lean development team and improve our capacity to review and integrate contributions. We appreciate your understanding and look forward to being able to accept contributions in the near future. In the meantime, the process described in the following sections is temporarily suspended.
- **Beneficiaries**: Which Lean users and projects do benefit most from this feature/change?
## Documentation
- **Community Feedback**: Have you sought feedback or insights from other Lean users?
Tutorial-like examples are very welcome.
They are useful for finding rough edges and bugs in Lean 4, for highlighting new features, and for showing how to use Lean.
If you want to store your tutorial in the Lean 4 repository to make sure future changes will not break it, we suggest the following workflow:
* Contact one of the Lean developers on Zulip, and check whether your tutorial is a good match for the Lean 4 repository.
* Send bug reports and report rough edges. We will work with you until the tutorial looks great.
* Add plenty of comments and make sure others will be able to follow it.
* Create a pull request in the Lean 4 repository. After merging, we will link it to the official documentation and make sure it becomes part of our test suite.
- **Maintainability**: Will this change streamline code maintenance or simplify its structure?
You can use `.lean` or `.md` files to create your tutorial. The `.md` files are ideal when you want to format your prose using markdown. For an example, see [this `.md` file](https://github.com/leanprover/lean4/blob/master/doc/lean3changes.md).
**Understand the Project**: Familiarize yourself with the project, existing issues, and latest commits. Ensure your contribution aligns with the project's direction and priorities.
Contributions to the reference manual are also welcome, but since Lean 4 is changing rapidly, please contact us first using Zulip
to find out which parts are stable enough to document. We will work with you to get this kind of
pull request merged. We are also happy to meet using Zoom, Skype or Google hangout to coordinate this kind of effort.
**Stay Updated**: Regularly fetch and merge changes from the main branch to ensure your branch is up-to-date and can be smoothly integrated.
As Lean 4 matures, other forms of documentation (e.g., doc-strings) will be welcome too.
**Help wanted**: We have issues tagged with ["help wanted"](https://github.com/leanprover/lean4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22), if you want to contribute to the project, please take a look at them. If you are interested in one of them, post comments, ask questions, and engage with the core developers there.
## "Help wanted"
Quality Over Quantity:
-----
For issues marked as [`help wanted`](https://github.com/leanprover/lean4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22), pull requests (PR) are welcome and we will work with you to get a PR merged. Some of these issues are nontrivial. If you are interested, please consider adding comments to the issue and/or messaging the Lean developers in [Zulip](https://leanprover.zulipchat.com/#).
**Focused Changes**: Each PR should address a single, clearly-defined issue or feature. Avoid making multiple unrelated changes in a single PR.
## Unexpected Pull Requests
**Write Tests**: Every new feature or bug fix should come with relevant tests. This ensures the robustness and reliability of the contribution.
We have very few core developers, and we cannot review arbitrary pull requests (PRs). Moreover, many features involve subtle tradeoffs, and it may require significant time and energy to even assess a proposed design. We suggest the following workflow:
**Documentation**: Update relevant documentation, including comments in the code, to explain the logic and reasoning behind your changes.
* First, discuss your idea with the Lean community on Zulip. Ask the community to help collect examples, document the requirements, and detect complications.
* If there is broad support, create a detailed issue for it on the Lean 4 repository at GitHub, and tag the issue with `RFC`.
* Ask the community for help documenting the requirements, and for collecting examples and concerns.
* Wait for one of the core developers to give you a "go ahead". At this point, the core developers will work with you to make sure your PR gets merged.
Coding Standards:
----
We don't want to waste your time by you implementing a feature and then us not being able to merge it.
**Follow the Code Style**: Ensure that your code follows the established coding style of the project.
## How to Contribute
**Lean on Lean**: Use Lean's built-in features and libraries effectively, avoiding reinventions.
* Always follow the [commit convention](https://lean-lang.org/lean4/doc/dev/commit_convention.html).
* Follow the style of the surrounding code. When in doubt, look at other files using the particular syntax as well.
* Make sure your code is documented.
* New features or bug fixes should come with appropriate tests.
* Ensure all tests work before submitting a PR; see [Development Setup](https://lean-lang.org/lean4/doc/make/index.html#development-setup) and [Fixing Tests](https://lean-lang.org/lean4/doc/dev/fixing_tests.html).
**Performance**: Make sure that your changes do not introduce performance regressions. If possible, optimize the solution for speed and resource usage.
PR Submission:
---
**Descriptive Title and Summary**: The PR title should briefly explain the purpose of the PR. The summary should give more detailed information on what changes are made and why. Links to Zulip threads are not acceptable as a summary. You are responsible for summarizing the discussion, and getting support for it.
**Follow the commit convention**: Pull requests are squash merged, and the
commit message is taken from the pull request title and body, so make sure they adhere to the [commit convention](https://github.com/leanprover/lean4/blob/master/doc/dev/commit_convention.md). Put questions and extra information, which should not be part of the final commit message, into a first comment rather than the Pull Request description.
Because the change will be squashed, there is no need to polish the commit messages and history on the branch.
**Link to Relevant Issues**: Reference any issues that your PR addresses to provide context.
**Stay Responsive**: Once the PR is submitted, stay responsive to feedback and be prepared to make necessary revisions. We will close any PR that has been inactive (no response or updates from the submitter) for more than a month.
Reviews and Feedback:
----
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.
**Continuous Integration**: Ensure that all CI checks pass on your PR. Failed checks will delay the review process. The maintainers will not check PRs containing failures.
What to Expect:
----
**Not All PRs Get Merged**: While we appreciate every contribution, not all PRs will be merged. Ensure your changes align with the project's goals and quality standards.
**Feedback is a Gift**: It helps improve the project and can also help you grow as a developer or contributor.
**Community Involvement**: Engage with the Lean community on our communication channels. This can lead to better collaboration and understanding of the project's direction.

View File

@@ -14,7 +14,7 @@ and have just begun regular [stable point releases](https://github.com/leanprove
- [Manual](https://lean-lang.org/lean4/doc/)
- [Release notes](RELEASES.md) starting at v4.0.0-m3
- [Examples](https://lean-lang.org/lean4/doc/examples.html)
- [External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/doc/contributions.md)
- [External Contribution Guidelines](CONTRIBUTING.md)
- [FAQ](https://lean-lang.org/lean4/doc/faq.html)
# Installation

View File

@@ -5,18 +5,186 @@ There is not yet a strong guarantee of backwards compatibility between versions,
only an expectation that breaking changes will be documented in this file.
This file contains work-in-progress notes for the upcoming release, as well as previous stable releases.
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status of each version.
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
of each version.
v4.3.0 (development in progress)
v4.6.0 (development in progress)
---------
* The derive handler for `DecidableEq` [now handles](https://github.com/leanprover/lean4/pull/2591) mutual inductive types.
* [Show path of failed import in Lake](https://github.com/leanprover/lean4/pull/2616).
* [Fix linker warnings on macOS](https://github.com/leanprover/lean4/pull/2598).
v4.5.0
---------
* Modify the lexical syntax of string literals to have string gaps, which are escape sequences of the form `"\" newline whitespace*`.
These have the interpetation of an empty string and allow a string to flow across multiple lines without introducing additional whitespace.
The following is equivalent to `"this is a string"`.
```lean
"this is \
a string"
```
[PR #2821](https://github.com/leanprover/lean4/pull/2821) and [RFC #2838](https://github.com/leanprover/lean4/issues/2838).
* Add raw string literal syntax. For example, `r"\n"` is equivalent to `"\\n"`, with no escape processing.
To include double quote characters in a raw string one can add sufficiently many `#` characters before and after
the bounding `"`s, as in `r#"the "the" is in quotes"#` for `"the \"the\" is in quotes"`.
[PR #2929](https://github.com/leanprover/lean4/pull/2929) and [issue #1422](https://github.com/leanprover/lean4/issues/1422).
* The low-level `termination_by'` clause is no longer supported.
Migration guide: Use `termination_by` instead, e.g.:
```diff
-termination_by' measure (fun ⟨i, _⟩ => as.size - i)
+termination_by go i _ => as.size - i
```
If the well-founded relation you want to use is not the one that the
`WellFoundedRelation` type class would infer for your termination argument,
you can use `WellFounded.wrap` from the std libarary to explicitly give one:
```diff
-termination_by' ⟨r, hwf⟩
+termination_by _ x => hwf.wrap x
```
* Support snippet edits in LSP `TextEdit`s. See `Lean.Lsp.SnippetString` for more details.
* Deprecations and changes in the widget API.
- `Widget.UserWidgetDefinition` is deprecated in favour of `Widget.Module`. The annotation `@[widget]` is deprecated in favour of `@[widget_module]`. To migrate a definition of type `UserWidgetDefinition`, remove the `name` field and replace the type with `Widget.Module`. Removing the `name` results in a title bar no longer being drawn above your panel widget. To add it back, draw it as part of the component using `<details open=true><summary class='mv2 pointer'>{name}</summary>{rest_of_widget}</details>`. See an example migration [here](https://github.com/leanprover/std4/pull/475/files#diff-857376079661a0c28a53b7ff84701afabbdf529836a6944d106c5294f0e68109R43-R83).
- The new command `show_panel_widgets` allows displaying always-on and locally-on panel widgets.
- `RpcEncodable` widget props can now be stored in the infotree.
- See [RFC 2963](https://github.com/leanprover/lean4/issues/2963) for more details and motivation.
* If no usable lexicographic order can be found automatically for a termination proof, explain why.
See [feat: GuessLex: if no measure is found, explain why](https://github.com/leanprover/lean4/pull/2960).
* Option to print [inferred termination argument](https://github.com/leanprover/lean4/pull/3012).
With `set_option showInferredTerminationBy true` you will get messages like
```
Inferred termination argument:
termination_by
ackermann n m => (sizeOf n, sizeOf m)
```
for automatically generated `termination_by` clauses.
* More detailed error messages for [invalid mutual blocks](https://github.com/leanprover/lean4/pull/2949).
* [Multiple](https://github.com/leanprover/lean4/pull/2923) [improvements](https://github.com/leanprover/lean4/pull/2969) to the output of `simp?` and `simp_all?`.
* Tactics with `withLocation *` [no longer fail](https://github.com/leanprover/lean4/pull/2917) if they close the main goal.
* Implementation of a `test_extern` command for writing tests for `@[extern]` and `@[implemented_by]` functions.
Usage is
```
import Lean.Util.TestExtern
test_extern Nat.add 17 37
```
The head symbol must be the constant with the `@[extern]` or `@[implemented_by]` attribute. The return type must have a `DecidableEq` instance.
Bug fixes for
[#2853](https://github.com/leanprover/lean4/issues/2853), [#2953](https://github.com/leanprover/lean4/issues/2953), [#2966](https://github.com/leanprover/lean4/issues/2966),
[#2971](https://github.com/leanprover/lean4/issues/2971), [#2990](https://github.com/leanprover/lean4/issues/2990), [#3094](https://github.com/leanprover/lean4/issues/3094).
Bug fix for [eager evaluation of default value](https://github.com/leanprover/lean4/pull/3043) in `Option.getD`.
Avoid [panic in `leanPosToLspPos`](https://github.com/leanprover/lean4/pull/3071) when file source is unavailable.
Improve [short-circuiting behavior](https://github.com/leanprover/lean4/pull/2972) for `List.all` and `List.any`.
Several Lake bug fixes: [#3036](https://github.com/leanprover/lean4/issues/3036), [#3064](https://github.com/leanprover/lean4/issues/3064), [#3069](https://github.com/leanprover/lean4/issues/3069).
v4.4.0
---------
* Lake and the language server now support per-package server options using the `moreServerOptions` config field, as well as options that apply to both the language server and `lean` using the `leanOptions` config field. Setting either of these fields instead of `moreServerArgs` ensures that viewing files from a dependency uses the options for that dependency. Additionally, `moreServerArgs` is being deprecated in favor of the `moreGlobalServerArgs` field. See PR [#2858](https://github.com/leanprover/lean4/pull/2858).
A Lakefile with the following deprecated package declaration:
```lean
def moreServerArgs := #[
"-Dpp.unicode.fun=true"
]
def moreLeanArgs := moreServerArgs
package SomePackage where
moreServerArgs := moreServerArgs
moreLeanArgs := moreLeanArgs
```
... can be updated to the following package declaration to use per-package options:
```lean
package SomePackage where
leanOptions := #[⟨`pp.unicode.fun, true⟩]
```
* [Rename request handler](https://github.com/leanprover/lean4/pull/2462).
* [Import auto-completion](https://github.com/leanprover/lean4/pull/2904).
* [`pp.beta`` to apply beta reduction when pretty printing](https://github.com/leanprover/lean4/pull/2864).
* [Embed and check githash in .olean](https://github.com/leanprover/lean4/pull/2766).
* [Guess lexicographic order for well-founded recursion](https://github.com/leanprover/lean4/pull/2874).
* [Allow trailing comma in tuples, lists, and tactics](https://github.com/leanprover/lean4/pull/2643).
Bug fixes for [#2628](https://github.com/leanprover/lean4/issues/2628), [#2883](https://github.com/leanprover/lean4/issues/2883),
[#2810](https://github.com/leanprover/lean4/issues/2810), [#2925](https://github.com/leanprover/lean4/issues/2925), and [#2914](https://github.com/leanprover/lean4/issues/2914).
**Lake:**
* `lake init .` and a bare `lake init` and will now use the current directory as the package name. [#2890](https://github.com/leanprover/lean4/pull/2890)
* `lake new` and `lake init` will now produce errors on invalid package names such as `..`, `foo/bar`, `Init`, `Lean`, `Lake`, and `Main`. See issue [#2637](https://github.com/leanprover/lean4/issues/2637) and PR [#2890](https://github.com/leanprover/lean4/pull/2890).
* `lean_lib` no longer converts its name to upper camel case (e.g., `lean_lib bar` will include modules named `bar.*` rather than `Bar.*`). See issue [#2567](https://github.com/leanprover/lean4/issues/2567) and PR [#2889](https://github.com/leanprover/lean4/pull/2889).
* Lean and Lake now properly support non-identifier library names (e.g., `lake new 123-hello` and `import «123Hello»` now work correctly). See issue [#2865](https://github.com/leanprover/lean4/issues/2865) and PR [#2889](https://github.com/leanprover/lean4/pull/2888).
* Lake now filters the environment extensions loaded from a compiled configuration (`lakefile.olean`) to include only those relevant to Lake's workspace loading process. This resolves segmentation faults caused by environment extension type mismatches (e.g., when defining custom elaborators via `elab` in configurations). See issue [#2632](https://github.com/leanprover/lean4/issues/2632) and PR [#2896](https://github.com/leanprover/lean4/pull/2896).
* Cloud releases will now properly be re-unpacked if the build directory is removed. See PR [#2928](https://github.com/leanprover/lean4/pull/2928).
* Lake's `math` template has been simplified. See PR [#2930](https://github.com/leanprover/lean4/pull/2930).
* `lake exe <target>` now parses `target` like a build target (as the help text states it should) rather than as a basic name. For example, `lake exe @mathlib/runLinter` should now work. See PR [#2932](https://github.com/leanprover/lean4/pull/2932).
* `lake new foo.bar [std]` now generates executables named `foo-bar` and `lake new foo.bar exe` properly creates `foo/bar.lean`. See PR [#2932](https://github.com/leanprover/lean4/pull/2932).
* Later packages and libraries in the dependency tree are now preferred over earlier ones. That is, the later ones "shadow" the earlier ones. Such an ordering is more consistent with how declarations generally work in programming languages. This will break any package that relied on the previous ordering. See issue [#2548](https://github.com/leanprover/lean4/issues/2548) and PR [#2937](https://github.com/leanprover/lean4/pull/2937).
* Executable roots are no longer mistakenly treated as importable. They will no longer be picked up by `findModule?`. See PR [#2937](https://github.com/leanprover/lean4/pull/2937).
v4.3.0
---------
* `simp [f]` does not unfold partial applications of `f` anymore. See issue [#2042](https://github.com/leanprover/lean4/issues/2042).
To fix proofs affected by this change, use `unfold f` or `simp (config := { unfoldPartialApp := true }) [f]`.
* By default, `simp` will no longer try to use Decidable instances to rewrite terms. In particular, not all decidable goals will be closed by `simp`, and the `decide` tactic may be useful in such cases. The `decide` simp configuration option can be used to locally restore the old `simp` behavior, as in `simp (config := {decide := true})`; this includes using Decidable instances to verify side goals such as numeric inequalities.
* Many bug fixes:
* [Add left/right actions to term tree coercion elaborator and make `^`` a right action](https://github.com/leanprover/lean4/pull/2778)
* [Fix for #2775, don't catch max recursion depth errors](https://github.com/leanprover/lean4/pull/2790)
* [Reduction of `Decidable` instances very slow when using `cases` tactic](https://github.com/leanprover/lean4/issues/2552)
* [`simp` not rewriting in binder](https://github.com/leanprover/lean4/issues/1926)
* [`simp` unfolding `let` even with `zeta := false` option](https://github.com/leanprover/lean4/issues/2669)
* [`simp` (with beta/zeta disabled) and discrimination trees](https://github.com/leanprover/lean4/issues/2281)
* [unknown free variable introduced by `rw ... at h`](https://github.com/leanprover/lean4/issues/2711)
* [`dsimp` doesn't use `rfl` theorems which consist of an unapplied constant](https://github.com/leanprover/lean4/issues/2685)
* [`dsimp` does not close reflexive equality goals if they are wrapped in metadata](https://github.com/leanprover/lean4/issues/2514)
* [`rw [h]` uses `h` from the environment in preference to `h` from the local context](https://github.com/leanprover/lean4/issues/2729)
* [missing `withAssignableSyntheticOpaque` for `assumption` tactic](https://github.com/leanprover/lean4/issues/2361)
* [ignoring default value for field warning](https://github.com/leanprover/lean4/issues/2178)
* [Cancel outstanding tasks on document edit in the language server](https://github.com/leanprover/lean4/pull/2648).
* [Remove unnecessary `%` operations in `Fin.mod` and `Fin.div`](https://github.com/leanprover/lean4/pull/2688)
* [Avoid `DecidableEq` in `Array.mem`](https://github.com/leanprover/lean4/pull/2774)
* [Ensure `USize.size` unifies with `?m + 1`](https://github.com/leanprover/lean4/issues/1926)
* [Improve compatibility with emacs eglot client](https://github.com/leanprover/lean4/pull/2721)
**Lake:**
* [Sensible defaults for `lake new MyProject math`](https://github.com/leanprover/lean4/pull/2770)
* Changed `postUpdate?` configuration option to a `post_update` declaration. See the `post_update` syntax docstring for more information on the new syntax.
* [A manifest is automatically created on workspace load if one does not exists.](https://github.com/leanprover/lean4/pull/2680).
* The `:=` syntax for configuration declarations (i.e., `package`, `lean_lib`, and `lean_exe`) has been deprecated. For example, `package foo := {...}` is deprecated.
* [support for overriding package URLs via `LAKE_PKG_URL_MAP`](https://github.com/leanprover/lean4/pull/2709)
* Moved the default build directory (e.g., `build`), default packages directory (e.g., `lake-packages`), and the compiled configuration (e.g., `lakefile.olean`) into a new dedicated directory for Lake outputs, `.lake`. The cloud release build archives are also stored here, fixing [#2713](https://github.com/leanprover/lean4/issues/2713).
* Update manifest format to version 7 (see [lean4#2801](https://github.com/leanprover/lean4/pull/2801) for details on the changes).
* Deprecate the `manifestFile` field of a package configuration.
* There is now a more rigorous check on `lakefile.olean` compatibility (see [#2842](https://github.com/leanprover/lean4/pull/2842) for more details).
v4.2.0
---------
* [isDefEq cache for terms not containing metavariables.](https://github.com/leanprover/lean4/pull/2644).
* Make [`Environment.mk`](https://github.com/leanprover/lean4/pull/2604) and [`Environment.add`](https://github.com/leanprover/lean4/pull/2642) private, and add [`replay`](https://github.com/leanprover/lean4/pull/2617) as a safer alternative.
* `IO.Process.output` no longer inherits the standard input of the caller.
* [Do not inhibit caching](https://github.com/leanprover/lean4/pull/2612) of default-level `match` reduction.
* [List the valid case tags](https://github.com/leanprover/lean4/pull/2629) when the user writes an invalid one.
* The derive handler for `DecidableEq` [now handles](https://github.com/leanprover/lean4/pull/2591) mutual inductive types.
* [Show path of failed import in Lake](https://github.com/leanprover/lean4/pull/2616).
* [Fix linker warnings on macOS](https://github.com/leanprover/lean4/pull/2598).
* **Lake:** Add `postUpdate?` package configuration option. Used by a package to specify some code which should be run after a successful `lake update` of the package or one of its downstream dependencies. ([lake#185](https://github.com/leanprover/lake/issues/185))
* Improvements to Lake startup time ([#2572](https://github.com/leanprover/lean4/pull/2572), [#2573](https://github.com/leanprover/lean4/pull/2573))
* `refine e` now replaces the main goal with metavariables which were created during elaboration of `e` and no longer captures pre-existing metavariables that occur in `e` ([#2502](https://github.com/leanprover/lean4/pull/2502)).
* This is accomplished via changes to `withCollectingNewGoalsFrom`, which also affects `elabTermWithHoles`, `refine'`, `calc` (tactic), and `specialize`. Likewise, all of these now only include newly-created metavariables in their output.

View File

@@ -4,7 +4,6 @@
- [Tour of Lean](./tour.md)
- [Setting Up Lean](./quickstart.md)
- [Extended Setup Notes](./setup.md)
- [Nix Setup](./setup/nix.md)
- [Theorem Proving in Lean](./tpil.md)
- [Functional Programming in Lean](fplean.md)
- [Examples](./examples.md)
@@ -86,7 +85,6 @@
- [macOS Setup](./make/osx-10.9.md)
- [Windows MSYS2 Setup](./make/msys2.md)
- [Windows with WSL](./make/wsl.md)
- [Nix Setup (*Experimental*)](./make/nix.md)
- [Bootstrapping](./dev/bootstrap.md)
- [Testing](./dev/testing.md)
- [Debugging](./dev/debugging.md)

View File

@@ -1,68 +0,0 @@
External Contribution Guidelines
============
**In the past, we accepted most pull requests. This practice produced hard to maintain code, performance problems, and bugs.** In order to improve the quality and maintainability of our codebase, we've established the following guidelines for external contributions.
Before You Submit a Pull Request (PR):
-------
**Start with an Issue**: Before submitting a PR, always open an issue discussing the problem you wish to solve or the feature you'd like to add. Use the prefix `RFC:` (request for comments) if you are proposing a new feature. Ask for feedback from other users. Take the time to summarize all the feedback. This allows the maintainers to evaluate your proposal more efficiently. When creating a RFC, consider the following questions:
- **User Experience**: How does this feature improve the user experience?
- **Beneficiaries**: Which Lean users and projects do benefit most from this feature/change?
- **Community Feedback**: Have you sought feedback or insights from other Lean users?
- **Maintainability**: Will this change streamline code maintenance or simplify its structure?
**Understand the Project**: Familiarize yourself with the project, existing issues, and latest commits. Ensure your contribution aligns with the project's direction and priorities.
**Stay Updated**: Regularly fetch and merge changes from the main branch to ensure your branch is up-to-date and can be smoothly integrated.
**Help wanted**: We have issues tagged with ["help wanted"](https://github.com/leanprover/lean4/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22), if you want to contribute to the project, please take a look at them. If you are interested in one of them, post comments, ask questions, and engage with the core developers there.
Quality Over Quantity:
-----
**Focused Changes**: Each PR should address a single, clearly-defined issue or feature. Avoid making multiple unrelated changes in a single PR.
**Write Tests**: Every new feature or bug fix should come with relevant tests. This ensures the robustness and reliability of the contribution.
**Documentation**: Update relevant documentation, including comments in the code, to explain the logic and reasoning behind your changes.
Coding Standards:
----
**Follow the Code Style**: Ensure that your code follows the established coding style of the project.
**Lean on Lean**: Use Lean's built-in features and libraries effectively, avoiding reinventions.
**Performance**: Make sure that your changes do not introduce performance regressions. If possible, optimize the solution for speed and resource usage.
PR Submission:
---
**Descriptive Title and Summary**: The PR title should briefly explain the purpose of the PR. The summary should give more detailed information on what changes are made and why. Links to Zulip threads are not acceptable as a summary. You are responsible for summarizing the discussion, and getting support for it.
**Link to Relevant Issues**: Reference any issues that your PR addresses to provide context.
**Stay Responsive**: Once the PR is submitted, stay responsive to feedback and be prepared to make necessary revisions. We will close any PR that has been inactive (no response or updates from the submitter) for more than a month.
Reviews and Feedback:
----
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.
**Continuous Integration**: Ensure that all CI checks pass on your PR. Failed checks will delay the review process. The maintainers will not check PRs containing failures.
What to Expect:
----
**Not All PRs Get Merged**: While we appreciate every contribution, not all PRs will be merged. Ensure your changes align with the project's goals and quality standards.
**Feedback is a Gift**: It helps improve the project and can also help you grow as a developer or contributor.
**Community Involvement**: Engage with the Lean community on our communication channels. This can lead to better collaboration and understanding of the project's direction.

View File

@@ -65,9 +65,36 @@ You now have a Lean binary and library that include your changes, though their
own compilation was not influenced by them, that you can use to test your
changes on test programs whose compilation *will* be influenced by the changes.
## Updating stage0
Finally, when we want to use new language features in the library, we need to
update the stage 0 compiler, which can be done via `make -C stageN update-stage0`.
`make update-stage0` without `-C` defaults to stage1.
update the archived C source code of the stage 0 compiler in `stage0/src`.
The github repository will automatically update stage0 on `master` once
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
If you have write access to the lean4 repository, you can also also manually
trigger that process, for example to be able to use new features in the compiler itself.
You can do that on <https://github.com/nomeata/lean4/actions/workflows/update-stage0.yml>
or using Github CLI with
```
gh workflow run update-stage0.yml
```
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

View File

@@ -1,10 +1,15 @@
Git Commit Convention
=====================
We are using the following convention for writing git-commit messages.
It is based on the one from AngularJS project([doc][angularjs-doc],
We are using the following convention for writing git commit messages. For pull
requests, make sure the pull request title and description follow this
convention, as the squash-merge commit will inherit title and body from the
pull request.
This convention is based on the one from the AngularJS project ([doc][angularjs-doc],
[commits][angularjs-git]).
[angularjs-git]: https://github.com/angular/angular.js/commits/master
[angularjs-doc]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#

View File

@@ -1,6 +1,6 @@
# Development Workflow
If you want to make changes to Lean itself, start by [building Lean](../make/index.html) from a clean checkout to make sure that everything is set up correctly.
If you want to make changes to Lean itself, start by [building Lean](../make/index.md) from a clean checkout to make sure that everything is set up correctly.
After that, read on below to find out how to set up your editor for changing the Lean source code, followed by further sections of the development manual where applicable such as on the [test suite](testing.md) and [commit convention](commit_convention.md).
If you are planning to make any changes that may affect the compilation of Lean itself, e.g. changes to the parser, elaborator, or compiler, you should first read about the [bootstrapping pipeline](bootstrap.md).
@@ -30,20 +30,14 @@ powershell -f elan-init.ps1 --default-toolchain none
del elan-init.ps1
```
You can use `elan toolchain link` to give a specific stage build
directory a reference name, then use `elan override set` to associate
such a name to the current directory. We usually want to use `stage0`
for editing files in `src` and `stage1` for everything else (e.g.
tests).
The `lean-toolchain` files in the Lean 4 repository are set up to use the `lean4-stage0`
toolchain for editing files in `src` and the `lean4` toolchain for editing files in `tests`.
Run the following commands to make `lean4` point at `stage1` and `lean4-stage0` point at `stage0`:
```bash
# in the Lean rootdir
elan toolchain link lean4 build/release/stage1
elan toolchain link lean4-stage0 build/release/stage0
# make `lean` etc. point to stage1 in the rootdir and subdirs
elan override set lean4
cd src
# make `lean` etc. point to stage0 anywhere inside `src`
elan override set lean4-stage0
```
You can also use the `+toolchain` shorthand (e.g. `lean +lean4-debug`) to switch
@@ -64,3 +58,19 @@ simply by pushing a tag to your fork of the Lean 4 github repository
If you push `my-tag` to a fork in your github account `my_name`,
you can then put `my_name/lean4:my-tag` in your `lean-toolchain` file in a project using `lake`.
(You must use a tag name that does not start with a numeral, or contain `_`).
### VS Code
There is a `lean.code-workspace` file that correctly sets up VS Code with workspace roots for the stage0/stage1 setup described above as well as with other settings.
You should always load it when working on Lean, such as by invoking
```
code lean.code-workspace
```
on the command line.
### `ccache`
Lean's build process uses [`ccache`](https://ccache.dev/) if it is
installed to speed up recompilation of the generated C code. Without
`ccache`, you'll likely spend more time than necessary waiting on
rebuilds - it's a good idea to make sure it's installed.

View File

@@ -5,7 +5,6 @@ After [building Lean](../make/index.md) you can run all the tests using
cd build/release
make test ARGS=-j4
```
Change the 4 to the maximum number of parallel tests you want to
allow. The best choice is the number of CPU cores on your machine as
the tests are mostly CPU bound. You can find the number of processors
@@ -17,6 +16,12 @@ adding the `-C stageN` argument. The default when run as above is stage 1. The
Lean tests will automatically use that stage's corresponding Lean
executables
Running `make test` will not pick up new test files; run
```bash
cmake build/release/stage1
```
to update the list of tests.
You can also use `ctest` directly if you are in the right folder. So
to run stage1 tests with a 300 second timeout run this:
@@ -24,6 +29,9 @@ to run stage1 tests with a 300 second timeout run this:
cd build/release/stage1
ctest -j 4 --output-on-failure --timeout 300
```
Useful `ctest` flags are `-R <name of test>` to run a single test, and
`--rerun-failed` to run all tests that failed during the last run.
You can also pass `ctest` flags via `make test ARGS="--rerun-failed"`.
To get verbose output from ctest pass the `--verbose` command line
option. Test output is normally suppressed and only summary
@@ -124,8 +132,3 @@ outputs. `meld` can also be used to repair the problems.
In Emacs, we can also execute `M-x lean4-diff-test-file` to check/diff the file of the current buffer.
To mass-copy all `.produced.out` files to the respective `.expected.out` file, use `tests/lean/copy-produced`.
When using the Nix setup, add `--keep-failed` to the `nix build` call and then call
```sh
tests/lean/copy-produced <build-dir>/source/tests/lean
```
instead where `<build-dir>` is the path printed out by `nix build`.

View File

@@ -15,9 +15,8 @@ sections of a Lean document. User widgets are rendered in the Lean infoview.
To try it out, simply type in the following code and place your cursor over the `#widget` command.
-/
@[widget]
def helloWidget : UserWidgetDefinition where
name := "Hello"
@[widget_module]
def helloWidget : Widget.Module where
javascript := "
import * as React from 'react';
export default function(props) {
@@ -25,7 +24,7 @@ def helloWidget : UserWidgetDefinition where
return React.createElement('p', {}, name + '!')
}"
#widget helloWidget .null
#widget helloWidget
/-!
If you want to dive into a full sample right away, check out
@@ -56,7 +55,11 @@ to the React component. In our first invocation of `#widget`, we set it to `.nul
happens when you type in:
-/
#widget helloWidget (Json.mkObj [("name", "<your name here>")])
structure HelloWidgetProps where
name? : Option String := none
deriving Server.RpcEncodable
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
/-!
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
@@ -132,9 +135,8 @@ on this we either display an `InteractiveCode` with the type, `mapRpcError` the
to turn it into a readable message, or show a `Loading..` message, respectively.
-/
@[widget]
def checkWidget : UserWidgetDefinition where
name := "#check as a service"
@[widget_module]
def checkWidget : Widget.Module where
javascript := "
import * as React from 'react';
const e = React.createElement;
@@ -160,7 +162,7 @@ export default function(props) {
Finally we can try out the widget.
-/
#widget checkWidget .null
#widget checkWidget
/-!
![`#check` as a service](../images/widgets_caas.png)
@@ -193,9 +195,8 @@ interact with the text editor.
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
-/
@[widget]
def insertTextWidget : UserWidgetDefinition where
name := "textInserter"
@[widget_module]
def insertTextWidget : Widget.Module where
javascript := "
import * as React from 'react';
const e = React.createElement;
@@ -213,4 +214,4 @@ export default function(props) {
/-! Finally, we can try this out: -/
#widget insertTextWidget .null
#widget insertTextWidget

BIN
doc/images/setup_guide.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -8,7 +8,7 @@ A Lean program consists of a stream of UTF-8 tokens where each token
is one of the following:
```
token: symbol | command | ident | string | char | numeral |
token: symbol | command | ident | string | raw_string | char | numeral |
: decimal | doc_comment | mod_doc_comment | field_notation
```
@@ -79,15 +79,35 @@ special characters:
[Unicode table](https://unicode-table.com/en/) so "\xA9 Copyright 2021" is "© Copyright 2021".
- `\uHHHH` puts the character represented by the 4 digit hexadecimal into the string, so the following
string "\u65e5\u672c" will become "日本" which means "Japan".
- `\` followed by a newline and then any amount of whitespace is a "gap" that is equivalent to the empty string,
useful for letting a string literal span across multiple lines. Gaps spanning multiple lines can be confusing,
so the parser raises an error if the trailing whitespace contains any newlines.
So the complete syntax is:
```
string : '"' string_item '"'
string_item : string_char | string_escape
string_char : [^\\]
string_escape: "\" ("\" | '"' | "'" | "n" | "t" | "x" hex_char{2} | "u" hex_char{4} )
string_item : string_char | char_escape | string_gap
string_char : [^"\\]
char_escape : "\" ("\" | '"' | "'" | "n" | "t" | "x" hex_char{2} | "u" hex_char{4})
hex_char : [0-9a-fA-F]
string_gap : "\" newline whitespace*
```
Raw String Literals
===================
Raw string literals are string literals without any escape character processing.
They begin with `r##...#"` (with zero or more `#` characters) and end with `"#...##` (with the same number of `#` characters).
The contents of a raw string literal may contain `"##..#` so long as the number of `#` characters
is less than the number of `#` characters used to begin the raw string literal.
```
raw_string : raw_string_aux(0) | raw_string_aux(1) | raw_string_aux(2) | ...
raw_string_aux(n) : 'r' '#'{n} '"' raw_string_item '"' '#'{n}
raw_string_item(n) : raw_string_char | raw_string_quote(n)
raw_string_char : [^"]
raw_string_quote(n) : '"' '#'{0..n-1}
```
Char Literals
@@ -96,7 +116,9 @@ Char Literals
Char literals are enclosed by single quotes (``'``).
```
char: "'" string_item "'"
char : "'" char_item "'"
char_item : char_char | char_escape
char_char : [^'\\]
```
Numeric Literals

View File

@@ -14,8 +14,6 @@ Platform-Specific Setup
- [Windows (WSL)](wsl.md)
- [macOS (homebrew)](osx-10.9.md)
- Linux/macOS/WSL via [Nix](https://nixos.org/nix/): Call `nix-shell` in the project root. That's it.
- There is also an [**experimental** setup based purely on Nix](nix.md) that works fundamentally differently from the
make/CMake setup described on this page.
Generic Build Instructions
--------------------------

View File

@@ -1,110 +0,0 @@
# Building with Nix
While [Nix](https://nixos.org/nix/) can be used to quickly open a shell with all dependencies for the [standard setup](index.md) installed, the user-facing [Nix Setup](../setup.md#nix-setup) can also be used to work *on* Lean.
## Setup
Follow the setup in the link above; to open the Lean shell inside a Lean checkout, you can also use
```bash
# in the Lean root directory
$ nix-shell -A nix
```
On top of the local and remote Nix cache, we do still rely on CCache as well to make C/C++ build steps incremental, which are atomic steps from Nix's point of view.
To enable CCache, add the following line to the config file mentioned in the setup:
```bash
extra-sandbox-paths = /nix/var/cache/ccache
```
Then set up that directory as follows:
```bash
sudo mkdir -m0770 -p /nix/var/cache/ccache
# macOS standard chown doesn't support --reference
nix shell .#nixpkgs.coreutils -c sudo chown --reference=/nix/store /nix/var/cache/ccache
```
## Basic Build Commands
From the Lean root directory inside the Lean shell:
```bash
nix build .#stage1 # build this stage's stdlib & executable
nix build .#stage1.test # run all tests
nix run .#stage1.update-stage0 # update ./stage0 from this stage
nix run .#stage1.update-stage0-commit # ...and commit the results
```
The `stage1.` part in each command is optional:
```bash
nix build .#test # run tests for stage 1
nix build . # build stage 1
nix build # ditto
```
## Build Process Description
The Nix build process conceptually works the same as described in [Lean Build Pipeline](index.md#lean-build-pipeline).
However, there are two important differences in practice apart from the standard Nix properties (hermeneutic, reproducible builds stored in a global hash-indexed store etc.):
* Only files tracked by git (using `git add` or at least `git add --intent-to-add`) are compiled.
This is actually a general property of Nix flakes, and has the benefit of making it basically impossible to forget to commit a file (at least in `src/`).
* Only files reachable from `src/Lean.lean` are compiled.
This is because modules are discovered not from a directory listing anymore but by recursively compiling all dependencies of that top module.
## Editor Integration
As in the standard Nix setup.
After adding `src/` as an LSP workspace, it should automatically fall back to using stage 0 in there.
Note that the UX of `{emacs,vscode}-dev` is quite different from the Make-based setup regarding the compilation of dependencies:
there is no mutable directory incrementally filled by the build that we could point the editor at for .olean files.
Instead, `emacs-dev` will gather the individual dependency outputs from the Nix store when checking a file -- and build them on the fly when necessary.
However, it will only ever load changes saved to disk, not ones opened in other buffers.
The absence of a mutable output directory also means that the Lean server will not automatically pick up `.ilean` metadata from newly compiled files.
Instead, you can run `nix run .#link-ilean` to symlink the `.ilean` tree of the stdlib state at that point in time to `src/build/lib`, where the server should automatically find them.
## Other Fun Stuff to Do with Nix
Open Emacs with Lean set up from an arbitrary commit (without even cloning Lean beforehand... if your Nix is new enough):
```bash
nix run github:leanprover/lean4/7e4edeb#emacs-package
```
Open a shell with `lean` and `LEAN_PATH` set up for compiling a specific module (this is exactly what `emacs-dev` is doing internally):
```bash
nix develop .#mods.\"Lean.Parser.Basic\"
# alternatively, directly pass a command to execute:
nix develop .#stage2.mods.\"Init.Control.Basic\" -c bash -c 'lean $src -Dtrace.Elab.command=true'
```
Not sure what you just broke? Run Lean from (e.g.) the previous commit on a file:
```bash
nix run .\?rev=$(git rev-parse @^) scratch.lean
```
Work on two adjacent stages at the same time without the need for repeatedly updating and reverting `stage0/`:
```bash
# open an editor that will use only committed changes (so first commit them when changing files)
nix run .#HEAD-as-stage1.emacs-dev&
# open a second editor that will use those committed changes as stage 0
# (so don't commit changes done here until you are done and ran a final `update-stage0-commit`)
nix run .#HEAD-as-stage0.emacs-dev&
```
To run `nix build` on the second stage outside of the second editor, use
```bash
nix build .#stage0-from-input --override-input lean-stage0 .\?rev=$(git rev-parse HEAD)
```
This setup will inadvertently change your `flake.lock` file, which you can revert when you are done.
...more surely to come...
## Debugging
Since Nix copies all source files before compilation, you will need to map debug symbols back to the original path using `set substitute-path` in GDB.
For example, for a build on Linux with the Nix sandbox activated:
```bash
(gdb) f
#1 0x0000000000d23a4f in lean_inc (o=0x1) at /build/source/build/include/lean/lean.h:562
562 /build/source/build/include/lean/lean.h: No such file or directory.
(gdb) set substitute-path /build/source/build src
(gdb) f
#1 0x0000000000d23a4f in lean_inc (o=0x1) at /build/source/build/include/lean/lean.h:562
562 static inline void lean_inc(lean_object * o) { if (!lean_is_scalar(o)) lean_inc_ref(o); }
```

View File

@@ -1,55 +1,18 @@
# Quickstart
These instructions will walk you through setting up Lean using the "basic" setup and VS Code as the editor.
See [Setup](./setup.md) for other ways, supported platforms, and more details on setting up Lean.
See quick [walkthrough demo video](https://www.youtube.com/watch?v=yZo6k48L0VY).
These instructions will walk you through setting up Lean 4 together with VS Code as an editor for Lean 4.
See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
1. Install [VS Code](https://code.visualstudio.com/).
1. Launch VS Code and install the `lean4` extension.
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
![installing the vscode-lean4 extension](images/code-ext.png)
1. Create a new file using "File > New Text File" (`Ctrl+N`). Click the `Select a language` prompt, type in `lean4`, and hit ENTER. You should see the following popup:
![elan](images/install_elan.png)
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Setup: Show Setup Guide".
Click the "Install Lean using Elan" button. You should see some progress output like this:
![show setup guide](images/show-setup-guide.png)
```
info: syncing channel updates for 'stable'
info: latest update on stable, lean version v4.0.0
info: downloading component 'lean'
```
If there is no popup, you probably have Elan installed already.
You may want to make sure that your default toolchain is Lean 4 in this case by running `elan default leanprover/lean4:stable` and reopen the file, as the next step will fail otherwise.
1. Follow the Lean 4 setup guide. It will walk you through learning resources for Lean 4, teach you how to set up Lean's dependencies on your platform, install Lean 4 for you at the click of a button and help you set up your first project.
1. While it is installing, you can paste the following Lean program into the new file:
```lean
#eval Lean.versionString
```
When the installation has finished, the Lean Language Server should start automatically and you should get syntax-highlighting and a "Lean Infoview" popping up on the right. You will see the output of the `#eval` statement when
you place your cursor at the end of the statement.
![successful setup](images/code-success.png)
You are set up!
## Create a Lean Project
*If your goal is to contribute to [mathlib4](https://github.com/leanprover-community/mathlib4) or use it as a dependency, please see its readme for specific instructions on how to do that.*
You can now create a Lean project in a new folder. Run `lake init foo` from "View > Terminal" to create a package, followed by `lake build` to get an executable version of your Lean program.
On Linux/macOS, you first have to follow the instructions printed by the Lean installation or log out and in again for the Lean executables to be available in you terminal.
Note: Packages **have** to be opened using "File > Open Folder..." for imports to work.
Saved changes are visible in other files after running "Lean 4: Refresh File Dependencies" (`Ctrl+Shift+X`).
## Troubleshooting
**The InfoView says "Waiting for Lean server to start..." forever.**
Check that the VS Code Terminal is not showing some installation errors from `elan`.
If that doesn't work, try also running the VS Code command `Developer: Reload Window`.
![setup guide](images/setup_guide.png)

View File

@@ -17,6 +17,8 @@ Issue reports and fixes are welcome.
* aarch64 Linux with glibc 2.27+
* aarch64 (Apple Silicon) macOS
* x86 (32-bit) Linux
* Emscripten Web Assembly
<!--
### Tier 3
@@ -48,10 +50,10 @@ Foo.lean # main file, import via `import Foo`
Foo/
A.lean # further files, import via e.g. `import Foo.A`
A/... # further nesting
build/ # `lake` build output directory
.lake/ # `lake` build output directory
```
After running `lake build` you will see a binary named `./build/bin/foo` and when you run it you should see the output:
After running `lake build` you will see a binary named `./.lake/build/bin/foo` and when you run it you should see the output:
```
Hello, world!
```

View File

@@ -1,71 +0,0 @@
# Nix Setup
An alternative setup based on Nix provides a perfectly reproducible development environment for your project from the Lean version down to the editor and Lean extension.
However, it is still experimental and subject to change; in particular, it is heavily based on an unreleased version of Nix enabling [Nix Flakes](https://www.tweag.io/blog/2020-05-25-flakes/). The setup has been tested on NixOS, other Linux distributions, and macOS.
After installing (any version of) Nix (<https://nixos.org/download.html>), you can easily open a shell with the particular pre-release version of Nix needed by and tested with our setup (called the "Lean shell" from here on):
```bash
$ nix-shell https://github.com/leanprover/lean4/archive/master.tar.gz -A nix
```
While this shell is sufficient for executing the steps below, it is recommended to also set the following options in `/etc/nix/nix.conf` (`nix.extraOptions` in NixOS):
```
max-jobs = auto # Allow building multiple derivations in parallel
keep-outputs = true # Do not garbage-collect build time-only dependencies (e.g. clang)
# Allow fetching build results from the Lean Cachix cache
trusted-substituters = https://lean4.cachix.org/
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= lean4.cachix.org-1:mawtxSxcaiWE24xCXXgh3qnvlTkyU7evRRnGeAhD4Wk=
```
On a multi-user installation of Nix (the default), you need to restart the Nix daemon afterwards:
```bash
sudo pkill nix-daemon
```
The [Cachix](https://cachix.org/) integration will magically beam any build steps already executed by the CI right onto your machine when calling Nix commands in the shell opened above.
It can be set up analogously as a cache for your own project.
Note: Your system Nix might print warnings about not knowing some of the settings used by the Lean shell Nix, which can be ignored.
## Basic Commands
From a Lean shell, run
```bash
$ nix flake new mypkg -t github:leanprover/lean4
```
to create a new Lean package in directory `mypkg` using the latest commit of Lean 4.
Such packages follow the same directory layout as described in the standard setup, except for a `lakefile.lean` replaced by a `flake.nix` file set up so you can run Nix commands on it, for example:
```bash
$ nix build # build package and all dependencies
$ nix build .#executable # compile `main` definition into executable (after you've added one)
$ nix run .#emacs-dev # open a pinned version of Emacs with lean4-mode fully set up
$ nix run .#emacs-dev MyPackage.lean # arguments can be passed as well, e.g. the file to open
$ nix run .#vscode-dev MyPackage.lean # ditto, using VS Code
```
Note that if you rename `MyPackage.lean`, you also have to adjust the `name` attribute in `flake.nix` accordingly.
Also note that if you turn the package into a Git repository, only tracked files will be visible to Nix.
As in the standard setup, changes need to be saved to be visible in other files, which have then to be invalidated via an editor command.
If you don't want to or cannot start the pinned editor from Nix, e.g. because you're running Lean inside WSL/a container/on a different machine, you can manually point your editor at the `lean` wrapper script the commands above use internally:
```bash
$ nix build .#lean-dev -o result-lean-dev
```
The resulting `./result-lean-dev/bin/lean` script essentially runs `nix run .#lean` in the current project's root directory when you open a Lean file or use the "refresh dependencies" command such that the correct Lean version for that project is executed.
This includes selecting the correct stage of Lean (which it will compile on the fly, though without progress output) if you are [working on Lean itself](./make/nix.md#editor-integration).
Package dependencies can be added as further input flakes and passed to the `deps` list of `buildLeanPackage`. Example: <https://github.com/Kha/testpkg2/blob/master/flake.nix#L5>
For hacking, it can be useful to temporarily override an input with a local checkout/different version of a dependency:
```bash
$ nix build --override-input somedep path/to/somedep
```
On a build error, Nix will show the last 10 lines of the output by default. You can pass `-L` to `nix build` to show all lines, or pass the shown `*.drv` path to `nix log` to show the full log after the fact.
Keeping all outputs ever built on a machine alive can accumulate to quite impressive amounts of disk space, so you might want to trigger the Nix GC when `/nix/store/` has grown too large:
```bash
nix-collect-garbage
```
This will remove everything not reachable from "GC roots" such as the `./result` symlink created by `nix build`.
Note that the package information in `flake.nix` is currently completely independent from `lakefile.lean` used in the standard setup.
Unifying the two formats is TBD.

View File

@@ -67,6 +67,9 @@ theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f
\end{document}
```
If your version of `minted` is v2.7 or newer, but before v3.0,
you will additionally need to follow the workaround described in https://github.com/gpoore/minted/issues/360.
You can then compile `test.tex` by executing the following command:
```bash

View File

@@ -99,11 +99,11 @@ Let us start with the first step of the program above, declaring an appropriate
```lean
# namespace Ex
class Inhabited (a : Type u) where
class Inhabited (a : Sort u) where
default : a
#check @Inhabited.default
-- Inhabited.default : {a : Type u} → [self : Inhabited a] → a
-- Inhabited.default : {a : Sort u} → [self : Inhabited a] → a
# end Ex
```
Note `Inhabited.default` doesn't have any explicit argument.
@@ -114,7 +114,7 @@ Now we populate the class with some instances:
```lean
# namespace Ex
# class Inhabited (a : Type _) where
# class Inhabited (a : Sort _) where
# default : a
instance : Inhabited Bool where
default := true
@@ -138,7 +138,7 @@ instance : Inhabited Prop where
You can use the command `export` to create the alias `default` for `Inhabited.default`
```lean
# namespace Ex
# class Inhabited (a : Type _) where
# class Inhabited (a : Sort _) where
# default : a
# instance : Inhabited Bool where
# default := true
@@ -174,7 +174,7 @@ instance [Inhabited a] [Inhabited b] : Inhabited (a × b) where
With this added to the earlier instance declarations, type class instance can infer, for example, a default element of ``Nat × Bool``:
```lean
# namespace Ex
# class Inhabited (a : Type u) where
# class Inhabited (a : Sort u) where
# default : a
# instance : Inhabited Bool where
# default := true
@@ -191,8 +191,14 @@ instance [Inhabited a] [Inhabited b] : Inhabited (a × b) where
```
Similarly, we can inhabit type function with suitable constant functions:
```lean
# namespace Ex
# class Inhabited (a : Sort u) where
# default : a
# opaque default [Inhabited a] : a :=
# Inhabited.default
instance [Inhabited b] : Inhabited (a -> b) where
default := fun _ => default
# end Ex
```
As an exercise, try defining default instances for other types, such as `List` and `Sum` types.

1
lean-toolchain Normal file
View File

@@ -0,0 +1 @@
lean4

57
lean.code-workspace Normal file
View File

@@ -0,0 +1,57 @@
{
"folders": [
{
"path": "."
},
{
"path": "src"
},
{
"path": "tests"
}
],
"settings": {
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"cmake.buildDirectory": "${workspaceFolder}/build/release",
"cmake.generator": "Unix Makefiles",
"[markdown]": {
"rewrap.wrappingColumn": 70
},
"[lean4]": {
"editor.rulers": [
100
]
}
},
"tasks": {
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make -C build/release -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "test",
"type": "shell",
"command": "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4); CTEST_OUTPUT_ON_FAILURE=1 make -C build/release test -j$NPROC ARGS=\"-j$NPROC\"",
"problemMatcher": [],
"group": {
"kind": "test",
"isDefault": true
}
}
]
},
"extensions": {
"recommendations": [
"leanprover.lean4"
]
}
}

View File

@@ -83,13 +83,13 @@ rec {
# use same stage for retrieving dependencies
lean-leanDeps = stage0;
lean-final = self;
leanFlags = [ "-DwarningAsError=true" ];
} ({
src = src + "/src";
roots = [ { mod = args.name; glob = "andSubmodules"; } ];
fullSrc = src;
srcPath = "$PWD/src:$PWD/src/lake";
inherit debug;
leanFlags = [ "-DwarningAsError=true" ];
} // args);
Init' = build { name = "Init"; deps = []; };
Lean' = build { name = "Lean"; deps = [ Init' ]; };

View File

@@ -10,7 +10,7 @@ function pebkac() {
[[ $# -gt 0 ]] || pebkac
case $1 in
--version)
# minimum version for `lake server` with fallback
# minimum version for `lake serve` with fallback
echo 3.1.0
;;
print-paths)

View File

@@ -9,7 +9,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 3)
set(LEAN_VERSION_MINOR 6)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -64,10 +64,10 @@ option(BSYMBOLIC "Link with -Bsymbolic to reduce call overhead in shared librari
option(USE_GMP "USE_GMP" ON)
# development-specific options
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" ON)
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
set(LEANC_CC "cc" CACHE STRING "C compiler to use in `leanc`")
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
if ("${LAZY_RC}" MATCHES "ON")
set(LEAN_LAZY_RC "#define LEAN_LAZY_RC")
@@ -93,8 +93,9 @@ if ("${RUNTIME_STATS}" MATCHES "ON")
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_RUNTIME_STATS")
endif()
if (NOT("${CHECK_OLEAN_VERSION}" MATCHES "ON"))
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_IGNORE_OLEAN_VERSION")
if ("${CHECK_OLEAN_VERSION}" MATCHES "ON")
set(USE_GITHASH ON)
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_CHECK_OLEAN_VERSION")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
@@ -401,26 +402,17 @@ if(MULTI_THREAD AND NOT MSVC AND (NOT ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin"))
endif()
# Git HASH
set(LEAN_PACKAGE_VERSION "NOT-FOUND")
if(USE_GITHASH)
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
if(${GIT_SHA1} MATCHES "GITDIR-NOTFOUND")
message(STATUS "Failed to read git_sha1")
set(GIT_SHA1 "")
if(EXISTS "${LEAN_SOURCE_DIR}/bin/package_version")
file(STRINGS "${LEAN_SOURCE_DIR}/bin/package_version" LEAN_PACKAGE_VERSION)
message(STATUS "Package version detected: ${LEAN_PACKAGE_VERSION}")
endif()
else()
message(STATUS "git commit sha1: ${GIT_SHA1}")
endif()
else()
set(GIT_SHA1 "")
if(EXISTS "${LEAN_SOURCE_DIR}/bin/package_version")
file(STRINGS "${LEAN_SOURCE_DIR}/bin/package_version" LEAN_PACKAGE_VERSION)
message(STATUS "Package version detected: ${LEAN_PACKAGE_VERSION}")
endif()
endif()
configure_file("${LEAN_SOURCE_DIR}/githash.h.in" "${LEAN_BINARY_DIR}/githash.h")
@@ -447,12 +439,13 @@ include_directories(${LEAN_SOURCE_DIR})
include_directories(${CMAKE_BINARY_DIR}) # version.h etc., "private" headers
include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" headers
# Use CMake profile C++ flags for building Lean libraries, but do not embed in `leanc`
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
# These are used in lean.mk (and libleanrt) and passed through by stdlib.make
# They are not embedded into `leanc` since they are build profile/machine specific
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
# Do embed flag for finding system libraries in dev builds
if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
string(APPEND LEANC_OPTS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
endif()
if(${STAGE} GREATER 1)

View File

@@ -17,6 +17,7 @@ import Init.System
import Init.Util
import Init.Dynamic
import Init.ShareCommon
import Init.MetaTypes
import Init.Meta
import Init.NotationExtra
import Init.SimpLemmas

View File

@@ -22,7 +22,7 @@ noncomputable def choose {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : α :
theorem choose_spec {α : Sort u} {p : α Prop} (h : x, p x) : p (choose h) :=
(indefiniteDescription p h).property
/-- Diaconescu's theorem: excluded middle from choice, Function extensionality and propositional extensionality. -/
/-- **Diaconescu's theorem**: excluded middle from choice, Function extensionality and propositional extensionality. -/
theorem em (p : Prop) : p ¬p :=
let U (x : Prop) : Prop := x = True p
let V (x : Prop) : Prop := x = False p

View File

@@ -328,8 +328,9 @@ unsafe def anyMUnsafe {α : Type u} {m : Type → Type w} [Monad m] (p : α
else
any (i+1) stop
if start < stop then
if stop as.size then
any (USize.ofNat start) (USize.ofNat stop)
let stop' := min stop as.size
if start < stop' then
any (USize.ofNat start) (USize.ofNat stop')
else
pure false
else
@@ -467,6 +468,9 @@ def elem [BEq α] (a : α) (as : Array α) : Bool :=
else
(true, r)
/-- Convert a `Array α` into an `List α`. This is O(n) in the size of the array. -/
-- This function is exported to C, where it is called by `Array.data`
-- (the projection) to implement this functionality.
@[export lean_array_to_list]
def toList (as : Array α) : List α :=
as.foldr List.cons []

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Init.Data.Array.Basic
@@ -20,32 +20,26 @@ theorem List.sizeOf_get_lt [SizeOf α] (as : List α) (i : Fin as.length) : size
namespace Array
instance [DecidableEq α] : Membership α (Array α) where
mem a as := as.contains a
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
-- NB: This is defined as a structure rather than a plain def so that a lemma
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
structure Mem (a : α) (as : Array α) : Prop where
val : a as.data
instance : Membership α (Array α) where
mem a as := Mem a as
theorem sizeOf_get_lt [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as; rename_i as
simp [get]
have ih := List.sizeOf_get_lt as i
exact Nat.lt_trans ih (by simp_arith)
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_get_lt as i) (by simp_arith)
theorem sizeOf_lt_of_mem [DecidableEq α] [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
simp [Membership.mem, contains, any, Id.run, BEq.beq, anyM] at h
let rec aux (j : Nat) (h : anyM.loop (m := Id) (fun b => decide (a = b)) as as.size (Nat.le_refl ..) j = true) : sizeOf a < sizeOf as := by
unfold anyM.loop at h
split at h
· simp [Bind.bind, pure] at h; split at h
next he => subst a; apply sizeOf_get_lt
next => have ih := aux (j+1) h; assumption
· contradiction
apply aux 0 h
termination_by aux j _ => as.size - j
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
@[simp] theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as
simp [get]
apply Nat.lt_trans (List.sizeOf_get ..)
simp_arith
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
`sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions
@@ -57,4 +51,17 @@ macro "array_get_dec" : tactic =>
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that `sizeOf a < sizeOf arr`
provided that `a ∈ arr` which is useful for well founded recursions over a nested inductive like
`inductive T | mk : Array T → T`. -/
-- NB: This is analogue to tactic `sizeOf_list_dec`
macro "array_mem_dec" : tactic =>
`(tactic| first
| apply Array.sizeOf_lt_of_mem; assumption; done
| apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
case' h => assumption
simp_arith)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_mem_dec)
end Array

View File

@@ -81,7 +81,7 @@ def isEmpty (s : ByteArray) : Bool :=
If `exact` is `false`, the capacity will be doubled when grown. -/
@[extern "lean_byte_array_copy_slice"]
def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray :=
dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + len) dest.data.size
dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + min len (src.data.size - srcOff)) dest.data.size
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
a.copySlice b empty 0 (e - b)

View File

@@ -45,19 +45,19 @@ protected def sub : Fin n → Fin n → Fin n
| a, h, b, _ => (a + (n - b)) % n, mlt h
/-!
Remark: mod/div/modn/land/lor can be defined without using (% n), but
Remark: land/lor can be defined without using (% n), but
we are trying to minimize the number of Nat theorems
needed to bootstrap Lean.
-/
protected def mod : Fin n Fin n Fin n
| a, h, b, _ => (a % b) % n, mlt h
| a, h, b, _ => a % b, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h
protected def div : Fin n Fin n Fin n
| a, h, b, _ => (a / b) % n, mlt h
| a, h, b, _ => a / b, Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h
def modn : Fin n Nat Fin n
| a, h, m => (a % m) % n, mlt h
| a, h, m => a % m, Nat.lt_of_le_of_lt (Nat.mod_le _ _) h
def land : Fin n Fin n Fin n
| a, h, b, _ => (Nat.land a b) % n, mlt h
@@ -110,7 +110,7 @@ theorem val_ne_of_ne {i j : Fin n} (h : i ≠ j) : val i ≠ val j :=
fun h' => absurd (eq_of_val_eq h') h
theorem modn_lt : {m : Nat} (i : Fin n), m > 0 (modn i m).val < m
| _, _, _, hp => Nat.lt_of_le_of_lt (mod_le _ _) (mod_lt _ hp)
| _, _, _, hp => by simp [modn]; apply Nat.mod_lt; assumption
theorem val_lt_of_le (i : Fin b) (h : b n) : i.val < n :=
Nat.lt_of_lt_of_le i.isLt h

View File

@@ -132,7 +132,7 @@ instance : ReprAtom Float := ⟨⟩
@[extern "round"] opaque Float.round : Float Float
@[extern "fabs"] opaque Float.abs : Float Float
instance : Pow Float Float := Float.pow
instance : HomogeneousPow Float := Float.pow
instance : Min Float := minOfLe

View File

@@ -6,9 +6,48 @@ Author: Leonardo de Moura
prelude
import Init.SimpLemmas
import Init.Data.Nat.Basic
import Init.Data.Nat.Div
set_option linter.missingDocs true -- keep it documented
open Decidable List
/--
The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
list literals.
For lists of length at least 64, an alternative desugaring strategy is used
which uses let bindings as intermediates as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
Note that this changes the order of evaluation, although it should not be observable
unless you use side effecting operations like `dbg_trace`.
-/
syntax "[" withoutPosition(term,*,?) "]" : term
/--
Auxiliary syntax for implementing `[$elem,*]` list literal syntax.
The syntax `%[a,b,c|tail]` constructs a value equivalent to `a::b::c::tail`.
It uses binary partitioning to construct a tree of intermediate let bindings as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
-/
syntax "%[" withoutPosition(term,*,? " | " term) "]" : term
namespace Lean
macro_rules
| `([ $elems,* ]) => do
-- NOTE: we do not have `TSepArray.getElems` yet at this point
let rec expandListLit (i : Nat) (skip : Bool) (result : TSyntax `term) : MacroM Syntax := do
match i, skip with
| 0, _ => pure result
| i+1, true => expandListLit i false result
| i+1, false => expandListLit i true ( ``(List.cons $(elems.elemsAndSeps.get! i) $result))
let size := elems.elemsAndSeps.size
if size < 64 then
expandListLit size (size % 2 == 0) ( ``(List.nil))
else
`(%[ $elems,* | List.nil ])
end Lean
universe u v w
variable {α : Type u} {β : Type v} {γ : Type w}
@@ -85,7 +124,8 @@ def appendTR (as bs : List α) : List α :=
induction as with
| nil => rfl
| cons a as ih =>
simp [reverseAux, List.append, ih, reverseAux_reverseAux]
rw [reverseAux, reverseAux_reverseAux]
simp [List.append, ih, reverseAux]
instance : Append (List α) := List.append
@@ -356,7 +396,7 @@ inductive Mem (a : α) : List α → Prop
instance : Membership α (List α) where
mem := Mem
theorem mem_of_elem_eq_true [DecidableEq α] {a : α} {as : List α} : elem a as = true a as := by
theorem mem_of_elem_eq_true [BEq α] [LawfulBEq α] {a : α} {as : List α} : elem a as = true a as := by
match as with
| [] => simp [elem]
| a'::as =>
@@ -365,12 +405,12 @@ theorem mem_of_elem_eq_true [DecidableEq α] {a : α} {as : List α} : elem a as
next h => intros; simp [BEq.beq] at h; subst h; apply Mem.head
next _ => intro h; exact Mem.tail _ (mem_of_elem_eq_true h)
theorem elem_eq_true_of_mem [DecidableEq α] {a : α} {as : List α} (h : a as) : elem a as = true := by
theorem elem_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : List α} (h : a as) : elem a as = true := by
induction h with
| head _ => simp [elem]
| tail _ _ ih => simp [elem]; split; rfl; assumption
instance [DecidableEq α] (a : α) (as : List α) : Decidable (a as) :=
instance [BEq α] [LawfulBEq α] (a : α) (as : List α) : Decidable (a as) :=
decidable_of_decidable_of_iff (Iff.intro mem_of_elem_eq_true elem_eq_true_of_mem)
theorem mem_append_of_mem_left {a : α} {as : List α} (bs : List α) : a as a as ++ bs := by
@@ -518,16 +558,22 @@ def takeWhile (p : α → Bool) : (xs : List α) → List α
/--
`O(|l|)`. Returns true if `p` is true for any element of `l`.
* `any p [a, b, c] = p a || p b || p c`
Short-circuits upon encountering the first `true`.
-/
@[inline] def any (l : List α) (p : α Bool) : Bool :=
foldr (fun a r => p a || r) false l
def any : List α -> (α Bool) -> Bool
| [], _ => false
| h :: t, p => p h || any t p
/--
`O(|l|)`. Returns true if `p` is true for every element of `l`.
* `all p [a, b, c] = p a && p b && p c`
Short-circuits upon encountering the first `false`.
-/
@[inline] def all (l : List α) (p : α Bool) : Bool :=
foldr (fun a r => p a && r) true l
def all : List α -> (α Bool) -> Bool
| [], _ => true
| h :: t, p => p h && all t p
/--
`O(|l|)`. Returns true if `true` is an element of the list of booleans `l`.

View File

@@ -629,7 +629,7 @@ protected theorem sub_lt_sub_left : ∀ {k m n : Nat}, k < m → k < n → m - n
@[simp] protected theorem zero_sub (n : Nat) : 0 - n = 0 := by
induction n with
| zero => rfl
| succ n ih => simp [ih, Nat.sub_succ]
| succ n ih => simp only [ih, Nat.sub_succ]; decide
protected theorem sub_self_add (n m : Nat) : n - (n + m) = 0 := by
show (n + 0) - (n + m) = 0

View File

@@ -285,7 +285,7 @@ instance : Max UInt64 := maxOfLe
instance : Min UInt64 := minOfLe
theorem usize_size_gt_zero : USize.size > 0 :=
Nat.pos_pow_of_pos System.Platform.numBits (Nat.zero_lt_succ _)
Nat.zero_lt_succ ..
@[extern "lean_usize_of_nat"]
def USize.ofNat (n : @& Nat) : USize := Fin.ofNat' n usize_size_gt_zero

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura and Sebastian Ullrich
Additional goodies for writing macros
-/
prelude
import Init.MetaTypes
import Init.Data.Array.Basic
import Init.Data.Option.BasicAux
@@ -64,6 +65,15 @@ def toolchain :=
@[extern "lean_internal_is_stage0"]
opaque Internal.isStage0 (u : Unit) : Bool
/--
This function can be used to detect whether the compiler has support for
generating LLVM instead of C. It is used by lake instead of the --features
flag in order to avoid having to run a compiler for this every time on startup.
See #2572.
-/
@[extern "lean_internal_has_llvm_backend"]
opaque Internal.hasLLVMBackend (u : Unit) : Bool
/-- Valid identifier names -/
def isGreek (c : Char) : Bool :=
0x391 c.val && c.val 0x3dd
@@ -217,11 +227,6 @@ instance : DecidableEq Name :=
end Name
structure NameGenerator where
namePrefix : Name := `_uniq
idx : Nat := 1
deriving Inhabited
namespace NameGenerator
@[inline] def curr (g : NameGenerator) : Name :=
@@ -452,11 +457,6 @@ end Syntax
| none => x
| some ref => withRef ref x
/-- Syntax objects for a Lean module. -/
structure Module where
header : Syntax
commands : Array Syntax
/--
Expand macros in the given syntax.
A node with kind `k` is visited only if `p k` is true.
@@ -773,6 +773,16 @@ def decodeQuotedChar (s : String) (i : String.Pos) : Option (Char × String.Pos)
else
none
/--
Decodes a valid string gap after the `\`.
Note that this function matches `"\" whitespace+` rather than
the more restrictive `"\" newline whitespace*` since this simplifies the implementation.
Justification: this does not overlap with any other sequences beginning with `\`.
-/
def decodeStringGap (s : String) (i : String.Pos) : Option String.Pos := do
guard <| (s.get i).isWhitespace
s.nextWhile Char.isWhitespace (s.next i)
partial def decodeStrLitAux (s : String) (i : String.Pos) (acc : String) : Option String := do
let c := s.get i
let i := s.next i
@@ -781,14 +791,49 @@ partial def decodeStrLitAux (s : String) (i : String.Pos) (acc : String) : Optio
else if s.atEnd i then
none
else if c == '\\' then do
let (c, i) decodeQuotedChar s i
decodeStrLitAux s i (acc.push c)
if let some (c, i) := decodeQuotedChar s i then
decodeStrLitAux s i (acc.push c)
else if let some i := decodeStringGap s i then
decodeStrLitAux s i acc
else
none
else
decodeStrLitAux s i (acc.push c)
def decodeStrLit (s : String) : Option String :=
decodeStrLitAux s ⟨1⟩ ""
/--
Takes a raw string literal, counts the number of `#`'s after the `r`, and interprets it as a string.
The position `i` should start at `1`, which is the character after the leading `r`.
The algorithm is simple: we are given `r##...#"...string..."##...#` with zero or more `#`s.
By counting the number of leading `#`'s, we can extract the `...string...`.
-/
partial def decodeRawStrLitAux (s : String) (i : String.Pos) (num : Nat) : String :=
let c := s.get i
let i := s.next i
if c == '#' then
decodeRawStrLitAux s i (num + 1)
else
s.extract i ⟨s.utf8ByteSize - (num + 1)⟩
/--
Takes the string literal lexical syntax parsed by the parser and interprets it as a string.
This is where escape sequences are processed for example.
The string `s` is is either a plain string literal or a raw string literal.
If it returns `none` then the string literal is ill-formed, which indicates a bug in the parser.
The function is not required to return `none` if the string literal is ill-formed.
-/
def decodeStrLit (s : String) : Option String :=
if s.get 0 == 'r' then
decodeRawStrLitAux s ⟨1⟩ 0
else
decodeStrLitAux s ⟨1⟩ ""
/--
If the provided `Syntax` is a string literal, returns the string it represents.
Even if the `Syntax` is a `str` node, the function may return `none` if its internally ill-formed.
The parser should always create well-formed `str` nodes.
-/
def isStrLit? (stx : Syntax) : Option String :=
match isLit? strLitKind stx with
| some val => decodeStrLit val
@@ -1162,8 +1207,12 @@ private partial def decodeInterpStrLit (s : String) : Option String :=
else if s.atEnd i then
none
else if c == '\\' then do
let (c, i) decodeInterpStrQuotedChar s i
loop i (acc.push c)
if let some (c, i) := decodeInterpStrQuotedChar s i then
loop i (acc.push c)
else if let some i := decodeStringGap s i then
loop i acc
else
none
else
loop i (acc.push c)
loop ⟨1⟩ ""
@@ -1203,88 +1252,7 @@ end TSyntax
namespace Meta
inductive TransparencyMode where
| all | default | reducible | instances
deriving Inhabited, BEq, Repr
inductive EtaStructMode where
/-- Enable eta for structure and classes. -/
| all
/-- Enable eta only for structures that are not classes. -/
| notClasses
/-- Disable eta for structures and classes. -/
| none
deriving Inhabited, BEq, Repr
namespace DSimp
structure Config where
zeta : Bool := true
beta : Bool := true
eta : Bool := true
etaStruct : EtaStructMode := .all
iota : Bool := true
proj : Bool := true
decide : Bool := false
autoUnfold : Bool := false
/-- If `failIfUnchanged := true`, then calls to `simp`, `dsimp`, or `simp_all`
will fail if they do not make progress. -/
failIfUnchanged : Bool := true
deriving Inhabited, BEq, Repr
end DSimp
namespace Simp
def defaultMaxSteps := 100000
structure Config where
maxSteps : Nat := defaultMaxSteps
maxDischargeDepth : Nat := 2
contextual : Bool := false
memoize : Bool := true
singlePass : Bool := false
zeta : Bool := true
beta : Bool := true
eta : Bool := true
etaStruct : EtaStructMode := .all
iota : Bool := true
proj : Bool := true
decide : Bool := true
arith : Bool := false
autoUnfold : Bool := false
/--
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 := true`, then calls to `simp`, `dsimp`, or `simp_all`
will fail if they do not make progress. -/
failIfUnchanged : Bool := true
deriving Inhabited, BEq, Repr
-- Configuration object for `simp_all`
structure ConfigCtx extends Config where
contextual := true
def neutralConfig : Simp.Config := {
zeta := false
beta := false
eta := false
iota := false
proj := false
decide := false
arith := false
autoUnfold := false
}
end Simp
inductive Occurrences where
| all
| pos (idxs : List Nat)
| neg (idxs : List Nat)
deriving Inhabited, BEq
deriving instance Repr for TransparencyMode, EtaStructMode, DSimp.Config, Simp.Config
def Occurrences.contains : Occurrences → Nat → Bool
| all, _ => true
@@ -1342,14 +1310,14 @@ This will rewrite with all equation lemmas, which can be used to
partially evaluate many definitions. -/
declare_simp_like_tactic simpAutoUnfold "simp! " fun (c : Lean.Meta.Simp.Config) => { c with autoUnfold := true }
/-- `simp_arith` is shorthand for `simp` with `arith := true`.
/-- `simp_arith` is shorthand for `simp` with `arith := true` and `decide := true`.
This enables the use of normalization by linear arithmetic. -/
declare_simp_like_tactic simpArith "simp_arith " fun (c : Lean.Meta.Simp.Config) => { c with arith := true }
declare_simp_like_tactic simpArith "simp_arith " fun (c : Lean.Meta.Simp.Config) => { c with arith := true, decide := true }
/-- `simp_arith!` is shorthand for `simp_arith` with `autoUnfold := true`.
This will rewrite with all equation lemmas, which can be used to
partially evaluate many definitions. -/
declare_simp_like_tactic simpArithAutoUnfold "simp_arith! " fun (c : Lean.Meta.Simp.Config) => { c with arith := true, autoUnfold := true }
declare_simp_like_tactic simpArithAutoUnfold "simp_arith! " fun (c : Lean.Meta.Simp.Config) => { c with arith := true, autoUnfold := true, decide := true }
/-- `simp_all!` is shorthand for `simp_all` with `autoUnfold := true`.
This will rewrite with all equation lemmas, which can be used to
@@ -1357,10 +1325,10 @@ partially evaluate many definitions. -/
declare_simp_like_tactic (all := true) simpAllAutoUnfold "simp_all! " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with autoUnfold := true }
/-- `simp_all_arith` combines the effects of `simp_all` and `simp_arith`. -/
declare_simp_like_tactic (all := true) simpAllArith "simp_all_arith " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with arith := true }
declare_simp_like_tactic (all := true) simpAllArith "simp_all_arith " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with arith := true, decide := true }
/-- `simp_all_arith!` combines the effects of `simp_all`, `simp_arith` and `simp!`. -/
declare_simp_like_tactic (all := true) simpAllArithAutoUnfold "simp_all_arith! " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with arith := true, autoUnfold := true }
declare_simp_like_tactic (all := true) simpAllArithAutoUnfold "simp_all_arith! " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with arith := true, autoUnfold := true, decide := true }
/-- `dsimp!` is shorthand for `dsimp` with `autoUnfold := true`.
This will rewrite with all equation lemmas, which can be used to

124
src/Init/MetaTypes.lean Normal file
View File

@@ -0,0 +1,124 @@
/-
Copyright (c) Leonardo de Moura. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Core
namespace Lean
structure NameGenerator where
namePrefix : Name := `_uniq
idx : Nat := 1
deriving Inhabited
/-- Syntax objects for a Lean module. -/
structure Module where
header : Syntax
commands : Array Syntax
namespace Meta
inductive TransparencyMode where
/-- unfold all constants, even those tagged as `@[irreducible]`. -/
| all
/-- unfold all constants except those tagged as `@[irreducible]`. -/
| default
/-- unfold only constants tagged with the `@[reducible]` attribute. -/
| reducible
/-- unfold reducible constants and constants tagged with the `@[instance]` attribute. -/
| instances
deriving Inhabited, BEq
inductive EtaStructMode where
/-- Enable eta for structure and classes. -/
| all
/-- Enable eta only for structures that are not classes. -/
| notClasses
/-- Disable eta for structures and classes. -/
| none
deriving Inhabited, BEq
namespace DSimp
structure Config where
zeta : Bool := true
beta : Bool := true
eta : Bool := true
etaStruct : EtaStructMode := .all
iota : Bool := true
proj : Bool := true
decide : Bool := false
autoUnfold : Bool := false
/-- If `failIfUnchanged := true`, then calls to `simp`, `dsimp`, or `simp_all`
will fail if they do not make progress. -/
failIfUnchanged : Bool := true
/-- 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
deriving Inhabited, BEq
end DSimp
namespace Simp
def defaultMaxSteps := 100000
structure Config where
maxSteps : Nat := defaultMaxSteps
maxDischargeDepth : Nat := 2
contextual : Bool := false
memoize : Bool := true
singlePass : Bool := false
zeta : Bool := true
beta : Bool := true
eta : Bool := true
etaStruct : EtaStructMode := .all
iota : Bool := true
proj : Bool := true
decide : Bool := false
arith : Bool := false
autoUnfold : Bool := false
/--
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 := true`, then calls to `simp`, `dsimp`, or `simp_all`
will fail if they do not make progress. -/
failIfUnchanged : Bool := true
/-- 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 := 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
deriving Inhabited, BEq
-- Configuration object for `simp_all`
structure ConfigCtx extends Config where
contextual := true
def neutralConfig : Simp.Config := {
zeta := false
beta := false
eta := false
iota := false
proj := false
decide := false
arith := false
autoUnfold := false
ground := false
}
end Simp
inductive Occurrences where
| all
| pos (idxs : List Nat)
| neg (idxs : List Nat)
deriving Inhabited, BEq
end Lean.Meta

View File

@@ -295,7 +295,8 @@ macro_rules | `($x - $y) => `(binop% HSub.hSub $x $y)
macro_rules | `($x * $y) => `(binop% HMul.hMul $x $y)
macro_rules | `($x / $y) => `(binop% HDiv.hDiv $x $y)
macro_rules | `($x % $y) => `(binop% HMod.hMod $x $y)
macro_rules | `($x ^ $y) => `(binop% HPow.hPow $x $y)
-- exponentiation should be considered a right action (#2220)
macro_rules | `($x ^ $y) => `(rightact% HPow.hPow $x $y)
macro_rules | `($x ++ $y) => `(binop% HAppend.hAppend $x $y)
macro_rules | `(- $x) => `(unop% Neg.neg $x)
@@ -460,42 +461,8 @@ expected type is known. So, `without_expected_type` is not effective in this cas
-/
macro "without_expected_type " x:term : term => `(let aux := $x; aux)
/--
The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
list literals.
For lists of length at least 64, an alternative desugaring strategy is used
which uses let bindings as intermediates as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
Note that this changes the order of evaluation, although it should not be observable
unless you use side effecting operations like `dbg_trace`.
-/
syntax "[" withoutPosition(term,*) "]" : term
/--
Auxiliary syntax for implementing `[$elem,*]` list literal syntax.
The syntax `%[a,b,c|tail]` constructs a value equivalent to `a::b::c::tail`.
It uses binary partitioning to construct a tree of intermediate let bindings as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
-/
syntax "%[" withoutPosition(term,* " | " term) "]" : term
namespace Lean
macro_rules
| `([ $elems,* ]) => do
-- NOTE: we do not have `TSepArray.getElems` yet at this point
let rec expandListLit (i : Nat) (skip : Bool) (result : TSyntax `term) : MacroM Syntax := do
match i, skip with
| 0, _ => pure result
| i+1, true => expandListLit i false result
| i+1, false => expandListLit i true ( ``(List.cons $(elems.elemsAndSeps.get! i) $result))
if elems.elemsAndSeps.size < 64 then
expandListLit elems.elemsAndSeps.size false ( ``(List.nil))
else
`(%[ $elems,* | List.nil ])
/--
Category for carrying raw syntax trees between macros; any content is printed as is by the pretty printer.
The only accepted parser for this category is an antiquotation.

View File

@@ -66,6 +66,19 @@ example (b : Bool) : Function.const Bool 10 b = 10 :=
@[inline] def Function.const {α : Sort u} (β : Sort v) (a : α) : β α :=
fun _ => a
/--
The encoding of `let_fun x := v; b` is `letFun v (fun x => b)`.
This is equal to `(fun x => b) v`, so the value of `x` is not accessible to `b`.
This is in contrast to `let x := v; b`, where the value of `x` is accessible to `b`.
There is special support for `letFun`.
Both WHNF and `simp` are aware of `letFun` and can reduce it when zeta reduction is enabled,
despite the fact it is marked `irreducible`.
For metaprogramming, the function `Lean.Expr.letFun?` can be used to recognize a `let_fun` expression
to extract its parts as if it were a `let` expression.
-/
@[irreducible] def letFun {α : Sort u} {β : α Sort v} (v : α) (f : (x : α) β x) : β v := f v
set_option checkBinderAnnotations false in
/--
`inferInstance` synthesizes a value of any target type by typeclass
@@ -806,6 +819,12 @@ decidability instance instead of the proposition, which has no code).
If a proposition `p` is `Decidable`, then `(by decide : p)` will prove it by
evaluating the decidability instance to `isTrue h` and returning `h`.
Because `Decidable` carries data,
when writing `@[simp]` lemmas which include a `Decidable` instance on the LHS,
it is best to use `{_ : Decidable p}` rather than `[Decidable p]`
so that non-canonical instances can be found via unification rather than
typeclass search.
-/
class inductive Decidable (p : Prop) where
/-- Prove that `p` is decidable by supplying a proof of `¬p` -/
@@ -920,7 +939,9 @@ or derive `i < arr.size` from some other proposition that we are checking in the
return `t` or `e` depending on whether `c` is true or false. The explicit argument
`c : Prop` does not have any actual computational content, but there is an additional
`[Decidable c]` argument synthesized by typeclass inference which actually
determines how to evaluate `c` to true or false.
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
Because lean uses a strict (call-by-value) evaluation strategy, the signature of this
function is problematic in that it would require `t` and `e` to be evaluated before
@@ -1297,11 +1318,38 @@ class Mod (α : Type u) where
The homogeneous version of `HPow`: `a ^ b : α` where `a : α`, `b : β`.
(The right argument is not the same as the left since we often want this even
in the homogeneous case.)
Types can choose to subscribe to particular defaulting behavior by providing
an instance to either `NatPow` or `HomogeneousPow`:
- `NatPow` is for types whose exponents is preferentially a `Nat`.
- `HomogeneousPow` is for types whose base and exponent are preferentially the same.
-/
class Pow (α : Type u) (β : Type v) where
/-- `a ^ b` computes `a` to the power of `b`. See `HPow`. -/
pow : α β α
/-- The homogenous version of `Pow` where the exponent is a `Nat`.
The purpose of this class is that it provides a default `Pow` instance,
which can be used to specialize the exponent to `Nat` during elaboration.
For example, if `x ^ 2` should preferentially elaborate with `2 : Nat` then `x`'s type should
provide an instance for this class. -/
class NatPow (α : Type u) where
/-- `a ^ n` computes `a` to the power of `n` where `n : Nat`. See `Pow`. -/
protected pow : α Nat α
/-- The completely homogeneous version of `Pow` where the exponent has the same type as the base.
The purpose of this class is that it provides a default `Pow` instance,
which can be used to specialize the exponent to have the same type as the base's type during elaboration.
This is to say, a type should provide an instance for this class in case `x ^ y` should be elaborated
with both `x` and `y` having the same type.
For example, the `Float` type provides an instance of this class, which causes expressions
such as `(2.2 ^ 2.2 : Float)` to elaborate. -/
class HomogeneousPow (α : Type u) where
/-- `a ^ b` computes `a` to the power of `b` where `a` and `b` both have the same type. -/
protected pow : α α α
/-- The homogeneous version of `HAppend`: `a ++ b : α` where `a b : α`. -/
class Append (α : Type u) where
/-- `a ++ b` is the result of concatenation of `a` and `b`. See `HAppend`. -/
@@ -1385,6 +1433,14 @@ instance [Mod α] : HMod α α α where
instance [Pow α β] : HPow α β α where
hPow a b := Pow.pow a b
@[default_instance]
instance [NatPow α] : Pow α Nat where
pow a n := NatPow.pow a n
@[default_instance]
instance [HomogeneousPow α] : Pow α α where
pow a b := HomogeneousPow.pow a b
@[default_instance]
instance [Append α] : HAppend α α α where
hAppend a b := Append.append a b
@@ -1480,8 +1536,7 @@ protected def Nat.pow (m : @& Nat) : (@& Nat) → Nat
| 0 => 1
| succ n => Nat.mul (Nat.pow m n) m
instance : Pow Nat Nat where
pow := Nat.pow
instance : NatPow Nat := Nat.pow
set_option bootstrap.genMatcherCode false in
/--
@@ -1774,7 +1829,7 @@ instance Fin.decLt {n} (a b : Fin n) : Decidable (LT.lt a b) := Nat.decLt ..
instance Fin.decLe {n} (a b : Fin n) : Decidable (LE.le a b) := Nat.decLe ..
/-- The size of type `UInt8`, that is, `2^8 = 256`. -/
def UInt8.size : Nat := 256
abbrev UInt8.size : Nat := 256
/--
The type of unsigned 8-bit integers. This type has special support in the
@@ -1813,7 +1868,7 @@ instance : Inhabited UInt8 where
default := UInt8.ofNatCore 0 (by decide)
/-- The size of type `UInt16`, that is, `2^16 = 65536`. -/
def UInt16.size : Nat := 65536
abbrev UInt16.size : Nat := 65536
/--
The type of unsigned 16-bit integers. This type has special support in the
@@ -1852,7 +1907,7 @@ instance : Inhabited UInt16 where
default := UInt16.ofNatCore 0 (by decide)
/-- The size of type `UInt32`, that is, `2^32 = 4294967296`. -/
def UInt32.size : Nat := 4294967296
abbrev UInt32.size : Nat := 4294967296
/--
The type of unsigned 32-bit integers. This type has special support in the
@@ -1929,7 +1984,7 @@ instance : Max UInt32 := maxOfLe
instance : Min UInt32 := minOfLe
/-- The size of type `UInt64`, that is, `2^64 = 18446744073709551616`. -/
def UInt64.size : Nat := 18446744073709551616
abbrev UInt64.size : Nat := 18446744073709551616
/--
The type of unsigned 64-bit integers. This type has special support in the
compiler to make it actually 64 bits rather than wrapping a `Nat`.
@@ -1969,11 +2024,26 @@ instance : Inhabited UInt64 where
/--
The size of type `UInt16`, that is, `2^System.Platform.numBits`, which may
be either `2^32` or `2^64` depending on the platform's architecture.
Remark: we define `USize.size` using `(2^numBits - 1) + 1` to ensure the
Lean unifier can solve contraints such as `?m + 1 = USize.size`. Recall that
`numBits` does not reduce to a numeral in the Lean kernel since it is platform
specific. Without this trick, the following definition would be rejected by the
Lean type checker.
```
def one: Fin USize.size := 1
```
Because Lean would fail to synthesize instance `OfNat (Fin USize.size) 1`.
Recall that the `OfNat` instance for `Fin` is
```
instance : OfNat (Fin (n+1)) i where
ofNat := Fin.ofNat i
```
-/
def USize.size : Nat := hPow 2 System.Platform.numBits
abbrev USize.size : Nat := Nat.succ (Nat.sub (hPow 2 System.Platform.numBits) 1)
theorem usize_size_eq : Or (Eq USize.size 4294967296) (Eq USize.size 18446744073709551616) :=
show Or (Eq (hPow 2 System.Platform.numBits) 4294967296) (Eq (hPow 2 System.Platform.numBits) 18446744073709551616) from
show Or (Eq (Nat.succ (Nat.sub (hPow 2 System.Platform.numBits) 1)) 4294967296) (Eq (Nat.succ (Nat.sub (hPow 2 System.Platform.numBits) 1)) 18446744073709551616) from
match System.Platform.numBits, System.Platform.numBits_eq with
| _, Or.inl rfl => Or.inl (by decide)
| _, Or.inr rfl => Or.inr (by decide)
@@ -2156,9 +2226,10 @@ returns `a` if `opt = some a` and `dflt` otherwise.
This function is `@[macro_inline]`, so `dflt` will not be evaluated unless
`opt` turns out to be `none`.
-/
@[macro_inline] def Option.getD : Option α α α
| some x, _ => x
| none, e => e
@[macro_inline] def Option.getD (opt : Option α) (dflt : α) : α :=
match opt with
| some x => x
| none => dflt
/--
Map a function over an `Option` by applying the function to the contained
@@ -2491,13 +2562,22 @@ is not observable from lean code. Arrays perform best when unshared; as long
as they are used "linearly" all updates will be performed destructively on the
array, so it has comparable performance to mutable arrays in imperative
programming languages.
From the point of view of proofs `Array α` is just a wrapper around `List α`.
-/
structure Array (α : Type u) where
/-- Convert a `List α` into an `Array α`. This function is overridden
to `List.toArray` and is O(n) in the length of the list. -/
/--
Converts a `List α` into an `Array α`.
At runtime, this constructor is implemented by `List.toArray` and is O(n) in the length of the
list.
-/
mk ::
/-- Convert an `Array α` into a `List α`. This function is overridden
to `Array.toList` and is O(n) in the length of the list. -/
/--
Converts a `Array α` into an `List α`.
At runtime, this projection is implemented by `Array.toList` and is O(n) in the length of the
array. -/
data : List α
attribute [extern "lean_array_data"] Array.data
@@ -2645,12 +2725,9 @@ def List.redLength : List α → Nat
| nil => 0
| cons _ as => as.redLength.succ
/--
Convert a `List α` into an `Array α`. This is O(n) in the length of the list.
This function is exported to C, where it is called by `Array.mk`
(the constructor) to implement this functionality.
-/
/-- Convert a `List α` into an `Array α`. This is O(n) in the length of the list. -/
-- This function is exported to C, where it is called by `Array.mk`
-- (the constructor) to implement this functionality.
@[inline, match_pattern, export lean_list_to_array]
def List.toArray (as : List α) : Array α :=
as.toArrayAux (Array.mkEmpty as.redLength)

View File

@@ -41,6 +41,12 @@ theorem implies_congr_ctx {p₁ p₂ q₁ q₂ : Prop} (h₁ : p₁ = p₂) (h
theorem forall_congr {α : Sort u} {p q : α Prop} (h : a, p a = q a) : ( a, p a) = ( a, q a) :=
(funext h : p = q) rfl
theorem forall_prop_domain_congr {p₁ p₂ : Prop} {q₁ : p₁ Prop} {q₂ : p₂ Prop}
(h₁ : p₁ = p₂)
(h₂ : a : p₂, q₁ (h₁.substr a) = q₂ a)
: ( a : p₁, q₁ a) = ( a : p₂, q₂ a) := by
subst h₁; simp [ h₂]
theorem let_congr {α : Sort u} {β : Sort v} {a a' : α} {b b' : α β}
(h₁ : a = a') (h₂ : x, b x = b' x) : (let x := a; b x) = (let x := a'; b' x) :=
h₁ (funext h₂ : b = b') rfl
@@ -78,6 +84,10 @@ theorem dite_congr {_ : Decidable b} [Decidable c]
@[simp] theorem ite_false (a b : α) : (if False then a else b) = b := rfl
@[simp] theorem dite_true {α : Sort u} {t : True α} {e : ¬ True α} : (dite True t e) = t True.intro := rfl
@[simp] theorem dite_false {α : Sort u} {t : False α} {e : ¬ False α} : (dite False t e) = e not_false := rfl
@[simp ] theorem ite_cond_true {_ : Decidable c} (a b : α) (h : c) : (if c then a else b) = a := by simp [h]
@[simp ] theorem ite_cond_false {_ : Decidable c} (a b : α) (h : ¬ c) : (if c then a else b) = b := by simp [h]
@[simp ] theorem dite_cond_true {α : Sort u} {_ : Decidable c} {t : c α} {e : ¬ c α} (h : c) : (dite c t e) = t h := by simp [h]
@[simp ] theorem dite_cond_false {α : Sort u} {_ : Decidable c} {t : c α} {e : ¬ c α} (h : ¬ c) : (dite c t e) = e h := by simp [h]
@[simp] theorem ite_self {α : Sort u} {c : Prop} {d : Decidable c} (a : α) : ite c a a = a := by cases d <;> rfl
@[simp] theorem and_self (p : Prop) : (p p) = p := propext (·.1), fun h => h, h
@[simp] theorem and_true (p : Prop) : (p True) = p := propext (·.1), (·, trivial)
@@ -98,6 +108,7 @@ theorem dite_congr {_ : Decidable b} [Decidable c]
@[simp] theorem implies_true (α : Sort u) : (α True) = True := eq_true fun _ => trivial
@[simp] theorem true_implies (p : Prop) : (True p) = p := propext (· trivial), (fun _ => ·)
@[simp] theorem not_false_eq_true : (¬ False) = True := eq_true False.elim
@[simp] theorem not_true_eq_false : (¬ True) = False := by decide
@[simp] theorem Bool.or_false (b : Bool) : (b || false) = b := by cases b <;> rfl
@[simp] theorem Bool.or_true (b : Bool) : (b || true) = true := by cases b <;> rfl
@@ -136,9 +147,9 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬(b = true)) = (b = false) := by cases b <;> decide
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬(b = false)) = (b = true) := by cases b <;> decide
@[simp] theorem decide_eq_true_eq [Decidable p] : (decide p = true) = p := propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_not [h : Decidable p] : decide (¬ p) = !decide p := by cases h <;> rfl
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by cases h <;> simp [decide, *]
@[simp] theorem decide_eq_true_eq {_ : Decidable p} : (decide p = true) = p := propext <| Iff.intro of_decide_eq_true decide_eq_true
@[simp] theorem decide_not {h : Decidable p} : decide (¬ p) = !decide p := by cases h <;> rfl
@[simp] theorem not_decide_eq_true {h : Decidable p} : ((!decide p) = true) = ¬ p := by cases h <;> simp [decide, *]
@[simp] theorem heq_eq_eq {α : Sort u} (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
@@ -152,7 +163,7 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a 0) = (a = 0) :=
propext fun h => Nat.le_antisymm h (Nat.zero_le ..), fun h => by simp [h]
propext fun h => Nat.le_antisymm h (Nat.zero_le ..), fun h => by rw [h]; decide
@[simp] theorem decide_False : decide False = false := rfl
@[simp] theorem decide_True : decide True = true := rfl

View File

@@ -328,7 +328,41 @@ namespace FS
namespace Handle
@[extern "lean_io_prim_handle_mk"] opaque mk (fn : @& FilePath) (mode : FS.Mode) : IO Handle
/--
Acquires an exclusive or shared lock on the handle.
Will block to wait for the lock if necessary.
**NOTE:** Acquiring a exclusive lock while already possessing a shared lock
will NOT reliably succeed (i.e., it works on Unix but not on Windows).
-/
@[extern "lean_io_prim_handle_lock"] opaque lock (h : @& Handle) (exclusive := true) : IO Unit
/--
Tries to acquire an exclusive or shared lock on the handle.
Will NOT block for the lock, but instead return `false`.
**NOTE:** Acquiring a exclusive lock while already possessing a shared lock
will NOT reliably succeed (i.e., it works on Unix but not on Windows).
-/
@[extern "lean_io_prim_handle_try_lock"] opaque tryLock (h : @& Handle) (exclusive := true) : IO Bool
/--
Releases any previously acquired lock on the handle.
Will succeed even if no lock has been acquired.
-/
@[extern "lean_io_prim_handle_unlock"] opaque unlock (h : @& Handle) : IO Unit
@[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
/--
Truncates the handle to the read/write cursor.
Does not automatically flush. Usually this is fine because the read/write
cursor includes buffered writes. However, the combination of buffered writes,
then `rewind`, then `truncate`, then close may lead to a file with content.
If unsure, flush before truncating.
-/
@[extern "lean_io_prim_handle_truncate"] opaque truncate (h : @& Handle) : IO Unit
/--
Read up to the given number of bytes from the handle.
If the returned array is empty, an end-of-file marker has been reached.
@@ -638,9 +672,12 @@ structure Output where
stdout : String
stderr : String
/-- Run process to completion and capture output. -/
/--
Run process to completion and capture output.
The process does not inherit the standard input of the caller.
-/
def output (args : SpawnArgs) : IO Output := do
let child spawn { args with stdout := Stdio.piped, stderr := Stdio.piped }
let child spawn { args with stdout := .piped, stderr := .piped, stdin := .null }
let stdout IO.asTask child.stdout.readToEnd Task.Priority.dedicated
let stderr child.stderr.readToEnd
let exitCode child.wait

View File

@@ -250,7 +250,7 @@ macro:1 x:tactic tk:" <;> " y:tactic:2 : tactic => `(tactic|
all_goals $y:tactic)
/-- `eq_refl` is equivalent to `exact rfl`, but has a few optimizations. -/
syntax (name := refl) "eq_refl" : tactic
syntax (name := eqRefl) "eq_refl" : tactic
/--
`rfl` tries to close the current goal using reflexivity.
@@ -435,14 +435,14 @@ non-dependent hypotheses. It has many variants:
other hypotheses.
-/
syntax (name := simp) "simp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*) "]")? (location)? : tactic
(" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*,?) "]")? (location)? : tactic
/--
`simp_all` is a stronger version of `simp [*] at *` where the hypotheses and target
are simplified multiple times until no simplification is applicable.
Only non-dependent propositional hypotheses are considered.
-/
syntax (name := simpAll) "simp_all" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpErase <|> simpLemma),*) "]")? : tactic
(" [" withoutPosition((simpErase <|> simpLemma),*,?) "]")? : tactic
/--
The `dsimp` tactic is the definitional simplifier. It is similar to `simp` but only
@@ -450,7 +450,7 @@ applies theorems that hold by reflexivity. Thus, the result is guaranteed to be
definitionally equal to the input.
-/
syntax (name := dsimp) "dsimp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpErase <|> simpLemma),*) "]")? (location)? : tactic
(" [" withoutPosition((simpErase <|> simpLemma),*,?) "]")? (location)? : tactic
/--
`delta id1 id2 ...` delta-expands the definitions `id1`, `id2`, ....

View File

@@ -85,3 +85,21 @@ private def outOfBounds [Inhabited α] : α :=
macro:max x:term noWs "[" i:term "]" noWs "?" : term => `(getElem? $x $i)
macro:max x:term noWs "[" i:term "]" noWs "!" : term => `(getElem! $x $i)
/--
Marks given value and its object graph closure as multi-threaded if currently
marked single-threaded. This will make reference counter updates atomic and
thus more costly. It can still be useful to do eagerly when the value will be
shared between threads later anyway and there is available time budget to mark
it now. -/
@[extern "lean_runtime_mark_multi_threaded"]
def Runtime.markMultiThreaded (a : α) : α := a
/--
Marks given value and its object graph closure as persistent. This will remove
reference counter updates but prevent the closure from being deallocated until
the end of the process! It can still be useful to do eagerly when the value
will be marked persistent later anyway and there is available time budget to
mark it now or it would be unnecessarily marked multi-threaded in between. -/
@[extern "lean_runtime_mark_persistent"]
def Runtime.markPersistent (a : α) : α := a

View File

@@ -5,12 +5,13 @@ Author: Leonardo de Moura
-/
prelude
import Init.SizeOf
import Init.MetaTypes
import Init.WF
/-- Unfold definitions commonly used in well founded relation definitions.
This is primarily intended for internal use in `decreasing_tactic`. -/
macro "simp_wf" : tactic =>
`(tactic| try simp [invImage, InvImage, Prod.lex, sizeOfWFRel, measure, Nat.lt_wfRel, WellFoundedRelation.rel])
`(tactic| try simp (config := { unfoldPartialApp := true }) [invImage, InvImage, Prod.lex, sizeOfWFRel, measure, Nat.lt_wfRel, WellFoundedRelation.rel])
/-- Extensible helper tactic for `decreasing_tactic`. This handles the "base case"
reasoning after applying lexicographic order lemmas.

View File

@@ -330,7 +330,7 @@ private def AttributeExtension.mkInitial : IO AttributeExtensionState := do
unsafe def mkAttributeImplOfConstantUnsafe (env : Environment) (opts : Options) (declName : Name) : Except String AttributeImpl :=
match env.find? declName with
| none => throw ("unknow constant '" ++ toString declName ++ "'")
| none => throw ("unknown constant '" ++ toString declName ++ "'")
| some info =>
match info.type with
| Expr.const `Lean.AttributeImpl _ => env.evalConst AttributeImpl opts declName

View File

@@ -472,6 +472,7 @@ def quoteString (s : String) : String :=
else if c == '\t' then "\\t"
else if c == '\\' then "\\\\"
else if c == '\"' then "\\\""
else if c == '?' then "\\?" -- avoid trigraphs
else if c.toNat <= 31 then
"\\x" ++ toHexDigit (c.toNat / 16) ++ toHexDigit (c.toNat % 16)
-- TODO(Leo): we should use `\unnnn` for escaping unicode characters.

View File

@@ -1541,15 +1541,6 @@ end EmitLLVM
def getLeanHBcPath : IO System.FilePath := do
return ( getLibDir ( getBuildDir)) / "lean.h.bc"
def optimizeLLVMModule (mod : LLVM.Module ctx) : IO Unit := do
let pm LLVM.createPassManager
let pmb LLVM.createPassManagerBuilder
pmb.setOptLevel 3
pmb.populateModulePassManager pm
LLVM.runPassManager pm mod
LLVM.disposePassManager pm
LLVM.disposePassManagerBuilder pmb
/-- Get the names of all global symbols in the module -/
partial def getModuleGlobals (mod : LLVM.Module llvmctx) : IO (Array (LLVM.Value llvmctx)) := do
let rec go (v : LLVM.Value llvmctx) (acc : Array (LLVM.Value llvmctx)) : IO (Array (LLVM.Value llvmctx)) := do
@@ -1568,7 +1559,7 @@ partial def getModuleFunctions (mod : LLVM.Module llvmctx) : IO (Array (LLVM.Val
`emitLLVM` is the entrypoint for the lean shell to code generate LLVM.
-/
@[export lean_ir_emit_llvm]
def emitLLVM (env : Environment) (modName : Name) (filepath : String) (tripleStr? : Option String) : IO Unit := do
def emitLLVM (env : Environment) (modName : Name) (filepath : String) : IO Unit := do
LLVM.llvmInitializeTargetInfo
let llvmctx LLVM.createContext
let module LLVM.createModule llvmctx modName.toString
@@ -1601,17 +1592,7 @@ def emitLLVM (env : Environment) (modName : Name) (filepath : String) (tripleStr
let some fn LLVM.getNamedFunction emitLLVMCtx.llvmmodule name
| throw <| IO.Error.userError s!"ERROR: linked module must have function from runtime module: '{name}'"
LLVM.setLinkage fn LLVM.Linkage.internal
optimizeLLVMModule emitLLVMCtx.llvmmodule
LLVM.writeBitcodeToFile emitLLVMCtx.llvmmodule filepath
let tripleStr := tripleStr?.getD ( LLVM.getDefaultTargetTriple)
let target LLVM.getTargetFromTriple tripleStr
let cpu := "generic"
let features := ""
let targetMachine LLVM.createTargetMachine target tripleStr cpu features
let codegenType := LLVM.CodegenFileType.ObjectFile
LLVM.targetMachineEmitToFile targetMachine emitLLVMCtx.llvmmodule (filepath ++ ".o") codegenType
LLVM.disposeModule emitLLVMCtx.llvmmodule
LLVM.disposeTargetMachine targetMachine
| .error err => throw (IO.Error.userError err)
end Lean.IR

View File

@@ -46,7 +46,7 @@ structure Pass where
Resulting phase.
-/
phaseOut : Phase := phase
phaseInv : phaseOut phase := by simp
phaseInv : phaseOut phase := by simp_arith
/--
The name of the `Pass`
-/

View File

@@ -658,7 +658,9 @@ where
visit (f.beta e.getAppArgs)
visitApp (e : Expr) : M Arg := do
if let .const declName _ := e.getAppFn then
if let some (args, n, t, v, b) := e.letFunAppArgs? then
visitCore <| mkAppN (.letE n t v b (nonDep := true)) args
else if let .const declName _ := e.getAppFn then
if declName == ``Quot.lift then
visitQuotLift e
else if declName == ``Quot.mk then
@@ -725,11 +727,8 @@ where
pushElement (.fun funDecl)
return .fvar funDecl.fvarId
visitMData (mdata : MData) (e : Expr) : M Arg := do
if let some (.app (.lam n t b ..) v) := letFunAnnotation? (.mdata mdata e) then
visitLet (.letE n t v b (nonDep := true)) #[]
else
visit e
visitMData (_mdata : MData) (e : Expr) : M Arg := do
visit e
visitProj (s : Name) (i : Nat) (e : Expr) : M Arg := do
match ( visit e) with

View File

@@ -65,6 +65,12 @@ structure Context where
initHeartbeats : Nat := 0
maxHeartbeats : Nat := getMaxHeartbeats options
currMacroScope : MacroScope := firstFrontendMacroScope
/--
If `catchRuntimeEx = false`, then given `try x catch ex => h ex`,
an runtime exception occurring in `x` is not handled by `h`.
Recall that runtime exceptions are `maxRecDepth` or `maxHeartbeats`.
-/
catchRuntimeEx : Bool := false
deriving Nonempty
/-- CoreM is a monad for manipulating the Lean environment.
@@ -199,6 +205,11 @@ instance [MetaEval α] : MetaEval (CoreM α) where
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
controlAt CoreM fun runInBase => withIncRecDepth (runInBase x)
@[inline] def checkInterrupted : CoreM Unit := do
if ( IO.checkCanceled) then
-- should never be visible to users!
throw <| Exception.error .missing "elaboration interrupted"
def throwMaxHeartbeat (moduleName : Name) (optionName : Name) (max : Nat) : CoreM Unit := do
let msg := s!"(deterministic) timeout at '{moduleName}', maximum number of heartbeats ({max/1000}) has been reached (use 'set_option {optionName} <num>' to set the limit)"
throw <| Exception.error ( getRef) (MessageData.ofFormat (Std.Format.text msg))
@@ -212,6 +223,11 @@ def checkMaxHeartbeatsCore (moduleName : String) (optionName : Name) (max : Nat)
def checkMaxHeartbeats (moduleName : String) : CoreM Unit := do
checkMaxHeartbeatsCore moduleName `maxHeartbeats ( read).maxHeartbeats
def checkSystem (moduleName : String) : CoreM Unit := do
-- TODO: bring back more checks from the C++ implementation
checkInterrupted
checkMaxHeartbeats moduleName
private def withCurrHeartbeatsImp (x : CoreM α) : CoreM α := do
let heartbeats IO.getNumHeartbeats
withReader (fun ctx => { ctx with initHeartbeats := heartbeats }) x
@@ -240,7 +256,7 @@ instance : MonadLog CoreM where
end Core
export Core (CoreM mkFreshUserName checkMaxHeartbeats withCurrHeartbeats)
export Core (CoreM mkFreshUserName checkSystem withCurrHeartbeats)
@[inline] def withAtLeastMaxRecDepth [MonadFunctorT CoreM m] (max : Nat) : m α m α :=
monadMap (m := CoreM) <| withReader (fun ctx => { ctx with maxRecDepth := Nat.max max ctx.maxRecDepth })
@@ -299,7 +315,7 @@ private def checkUnsupported [Monad m] [MonadEnv m] [MonadError m] (decl : Decla
| _ => pure ()
register_builtin_option compiler.enableNew : Bool := {
defValue := true
defValue := false
group := "compiler"
descr := "(compiler) enable the new code generator, this should have no significant effect on your code but it does help to test the new code generator; unset to only use the old code generator instead"
}
@@ -340,4 +356,42 @@ def ImportM.runCoreM (x : CoreM α) : ImportM α := do
let (a, _) x.toIO { options := ctx.opts, fileName := "<ImportM>", fileMap := default } { env := ctx.env }
return a
/-- Return `true` if the exception was generated by one our resource limits. -/
def Exception.isRuntime (ex : Exception) : Bool :=
ex.isMaxHeartbeat || ex.isMaxRecDepth
/--
Custom `try-catch` for all monads based on `CoreM`. We don't want to catch "runtime exceptions"
in these monads, but on `CommandElabM`. See issues #2775 and #2744
-/
@[inline] protected def Core.tryCatch (x : CoreM α) (h : Exception CoreM α) : CoreM α := do
try
x
catch ex =>
if ex.isRuntime && !( read).catchRuntimeEx then
throw ex
else
h ex
instance : MonadExceptOf Exception CoreM where
throw := throw
tryCatch := Core.tryCatch
@[inline] def Core.withCatchingRuntimeEx (flag : Bool) (x : CoreM α) : CoreM α :=
withReader (fun ctx => { ctx with catchRuntimeEx := flag }) x
@[inline] def mapCoreM [MonadControlT CoreM m] [Monad m] (f : forall {α}, CoreM α CoreM α) {α} (x : m α) : m α :=
controlAt CoreM fun runInBase => f <| runInBase x
/--
Execute `x` with `catchRuntimeEx = flag`. That is, given `try x catch ex => h ex`,
if `x` throws a runtime exception, the handler `h` will be invoked if `flag = true`
Recall that
-/
@[inline] def withCatchingRuntimeEx [MonadControlT CoreM m] [Monad m] (x : m α) : m α :=
mapCoreM (Core.withCatchingRuntimeEx true) x
@[inline] def withoutCatchingRuntimeEx [MonadControlT CoreM m] [Monad m] (x : m α) : m α :=
mapCoreM (Core.withCatchingRuntimeEx false) x
end Lean

46
src/Lean/Data/Array.lean Normal file
View File

@@ -0,0 +1,46 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
import Init.Data.Array
namespace Array
/-!
This module contains utility functions involving Arrays that are useful in a few places
of the lean code base, but too specialized to live in `Init.Data.Array`, which arguably
is part of the public, user-facing standard library.
-/
/--
Given an array `a`, runs `f xᵢ xⱼ` for all `i < j`, removes those entries for which `f` returns
`false` (and will subsequently skip pairs if one element is removed), and returns the array of
remaining elements.
This can be used to remove elements from an array where a “better” element, in some partial
order, exists in the array.
-/
def filterPairsM {m} [Monad m] {α} (a : Array α) (f : α α m (Bool × Bool)) :
m (Array α) := do
let mut removed := Array.mkArray a.size false
let mut numRemoved := 0
for h1 : i in [:a.size] do for h2 : j in [i+1:a.size] do
unless removed[i]! || removed[j]! do
let xi := a[i]'h1.2
let xj := a[j]'h2.2
let (keepi, keepj) f xi xj
unless keepi do
numRemoved := numRemoved + 1
removed := removed.set! i true
unless keepj do
numRemoved := numRemoved + 1
removed := removed.set! j true
let mut a' := Array.mkEmpty numRemoved
for h : i in [:a.size] do
unless removed[i]! do
a' := a'.push (a[i]'h.2)
return a'
end Array

View File

@@ -305,7 +305,7 @@ section
throw $ userError s!"Expected method '{expectedMethod}', got method '{method}'"
| _ => throw $ userError s!"Expected JSON-RPC notification, got: '{(toJson m).compress}'"
partial def readResponseAs (h : FS.Stream) (nBytes : Nat) (expectedID : RequestID) (α) [FromJson α] : IO (Response α) := do
def readResponseAs (h : FS.Stream) (nBytes : Nat) (expectedID : RequestID) (α) [FromJson α] : IO (Response α) := do
let m h.readMessage nBytes
match m with
| Message.response id result =>
@@ -315,7 +315,6 @@ section
| Except.error inner => throw $ userError s!"Unexpected result '{result.compress}'\n{inner}"
else
throw $ userError s!"Expected id {expectedID}, got id {id}"
| Message.notification .. => readResponseAs h nBytes expectedID α
| _ => throw $ userError s!"Expected JSON-RPC response, got: '{(toJson m).compress}'"
end

View File

@@ -97,6 +97,10 @@ def insert : KVMap → Name → DataValue → KVMap
def contains (m : KVMap) (n : Name) : Bool :=
(m.find n).isSome
/-- Erase an entry from the map -/
def erase : KVMap Name KVMap
| m, k => m.filter fun a => a.1 k
def getString (m : KVMap) (k : Name) (defVal := "") : String :=
match m.find k with
| some (DataValue.ofString v) => v
@@ -145,6 +149,30 @@ def setName (m : KVMap) (k : Name) (v : Name) : KVMap :=
def setSyntax (m : KVMap) (k : Name) (v : Syntax) : KVMap :=
m.insert k (DataValue.ofSyntax v)
/-- Update a `String` entry based on its current value. -/
def updateString (m : KVMap) (k : Name) (f : String String) : KVMap :=
m.insert k <| DataValue.ofString <| f <| m.getString k
/-- Update a `Nat` entry based on its current value. -/
def updateNat (m : KVMap) (k : Name) (f : Nat Nat) : KVMap :=
m.insert k <| DataValue.ofNat <| f <| m.getNat k
/-- Update an `Int` entry based on its current value. -/
def updateInt (m : KVMap) (k : Name) (f : Int Int) : KVMap :=
m.insert k <| DataValue.ofInt <| f <| m.getInt k
/-- Update a `Bool` entry based on its current value. -/
def updateBool (m : KVMap) (k : Name) (f : Bool Bool) : KVMap :=
m.insert k <| DataValue.ofBool <| f <| m.getBool k
/-- Update a `Name` entry based on its current value. -/
def updateName (m : KVMap) (k : Name) (f : Name Name) : KVMap :=
m.insert k <| DataValue.ofName <| f <| m.getName k
/-- Update a `Syntax` entry based on its current value. -/
def updateSyntax (m : KVMap) (k : Name) (f : Syntax Syntax) : KVMap :=
m.insert k <| DataValue.ofSyntax <| f <| m.getSyntax k
@[inline] protected def forIn.{w, w'} {δ : Type w} {m : Type w Type w'} [Monad m]
(kv : KVMap) (init : δ) (f : Name × DataValue δ m (ForInStep δ)) : m δ :=
kv.entries.forIn init f
@@ -162,6 +190,16 @@ def subsetAux : List (Name × DataValue) → KVMap → Bool
def subset : KVMap KVMap Bool
| m₁, m₂ => subsetAux m₁ m₂
def mergeBy (mergeFn : Name DataValue DataValue DataValue) (l r : KVMap)
: KVMap := Id.run do
let mut result := l
for k, vᵣ in r do
if let some vₗ := result.find k then
result := result.insert k (mergeFn k vₗ vᵣ)
else
result := result.insert k vᵣ
return result
def eqv (m₁ m₂ : KVMap) : Bool :=
subset m₁ m₂ && subset m₂ m₁
@@ -181,6 +219,11 @@ class Value (α : Type) where
@[inline] def set {α : Type} [Value α] (m : KVMap) (k : Name) (v : α) : KVMap :=
m.insert k (Value.toDataValue v)
@[inline] def update {α : Type} [Value α] (m : KVMap) (k : Name) (f : Option α Option α) : KVMap :=
match f (m.get? k) with
| some a => m.set k a
| none => m.erase k
instance : Value DataValue where
toDataValue := id
ofDataValue? := some

View File

@@ -78,6 +78,38 @@ structure Command where
arguments? : Option (Array Json) := none
deriving ToJson, FromJson
/-- A snippet is a string that gets inserted into a document,
and can afterwards be edited by the user in a structured way.
Snippets contain instructions that
specify how this structured editing should proceed.
They are expressed in a domain-specific language
based on one from TextMate,
including the following constructs:
- Designated positions for subsequent user input,
called "tab stops" after their most frequently-used keybinding.
They are denoted with `$1`, `$2`, and so forth.
`$0` denotes where the cursor should be positioned after all edits are completed,
defaulting to the end of the string.
Snippet tab stops are unrelated to tab stops used for indentation.
- Tab stops with default values, called _placeholders_, written `${1:default}`.
The default may itself contain a tab stop or a further placeholder
and multiple options to select from may be provided
by surrounding them with `|`s and separating them with `,`,
as in `${1|if $2 then $3 else $4,if let $2 := $3 then $4 else $5|}`.
- One of a set of predefined variables that are replaced with their values.
This includes the current line number (`$TM_LINE_NUMBER`)
or the text that was selected when the snippet was invoked (`$TM_SELECTED_TEXT`).
- Formatting instructions to modify variables using regular expressions
or a set of predefined filters.
The full syntax and semantics of snippets,
including the available variables and the rules for escaping control characters,
are described in the [LSP specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax). -/
structure SnippetString where
value : String
deriving ToJson, FromJson
/-- A textual edit applicable to a text document.
[reference](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit) -/
@@ -87,6 +119,21 @@ structure TextEdit where
range : Range
/-- The string to be inserted. For delete operations use an empty string. -/
newText : String
/-- If this field is present and the editor supports it,
`newText` is ignored
and an interactive snippet edit is performed instead.
The use of snippets in `TextEdit`s
is a Lean-specific extension to the LSP standard,
so `newText` should still be set to a correct value
as fallback in case the editor does not support this feature.
Even editors that support snippets may not always use them;
for instance, if the file is not already open,
VS Code will perform a normal text edit in the background instead. -/
/- NOTE: Similar functionality may be added to LSP in the future:
see [issue #592](https://github.com/microsoft/language-server-protocol/issues/592).
If such an addition occurs, this field should be deprecated. -/
leanExtSnippet? : Option SnippetString := none
/-- Identifier for annotated edit.
`WorkspaceEdit` has a `changeAnnotations` field that maps these identifiers to a `ChangeAnnotation`.
@@ -206,7 +253,7 @@ instance : FromJson DocumentChange where
[reference](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit) -/
structure WorkspaceEdit where
/-- Changes to existing resources. -/
changes : RBMap DocumentUri TextEditBatch compare :=
changes? : Option (RBMap DocumentUri TextEditBatch compare) := none
/-- Depending on the client capability
`workspace.workspaceEdit.resourceOperations` document changes are either
an array of `TextDocumentEdit`s to express changes to n different text
@@ -220,14 +267,14 @@ structure WorkspaceEdit where
If a client neither supports `documentChanges` nor
`workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s
using the `changes` property are supported. -/
documentChanges : Array DocumentChange :=
documentChanges? : Option (Array DocumentChange) := none
/-- A map of change annotations that can be referenced in
`AnnotatedTextEdit`s or create, rename and delete file / folder
operations.
Whether clients honor this property depends on the client capability
`workspace.changeAnnotationSupport`. -/
changeAnnotations : RBMap String ChangeAnnotation compare :=
changeAnnotations? : Option (RBMap String ChangeAnnotation compare) := none
deriving ToJson, FromJson
namespace WorkspaceEdit
@@ -236,24 +283,24 @@ instance : EmptyCollection WorkspaceEdit := ⟨{}⟩
instance : Append WorkspaceEdit where
append x y := {
changes := x.changes.mergeBy (fun _ v₁ v₂ => v₁ ++ v₂) y.changes
documentChanges := x.documentChanges ++ y.documentChanges
changeAnnotations := x.changeAnnotations.mergeBy (fun _ _v₁ v => v) y.changeAnnotations
changes? :=
match x.changes?, y.changes? with
| v, none | none, v => v
| some x, some y => x.mergeBy (fun _ v₁ v₂ => v₁ ++ v₂) y
documentChanges? :=
match x.documentChanges?, y.documentChanges? with
| v, none | none, v => v
| some x, some y => x ++ y
changeAnnotations? :=
match x.changeAnnotations?, y.changeAnnotations? with
| v, none | none, v => v
| some x, some y => x.mergeBy (fun _ _v₁ v₂ => v₂) y
}
def ofTextDocumentEdit (e : TextDocumentEdit) : WorkspaceEdit :=
{ documentChanges := #[DocumentChange.edit e]}
{ documentChanges? := #[DocumentChange.edit e]}
def ofTextEdit (uri : DocumentUri) (te : TextEdit) : WorkspaceEdit :=
/- [note], there is a bug in vscode where not including the version will cause an error,
even though the version field is not used to validate the change.
References:
- [a fix in the wild](https://github.com/stylelint/vscode-stylelint/pull/330/files).
Note that the version field needs to be present, even if the value is `undefined`.
- [angry comment](https://github.com/tsqllint/tsqllint-vscode-extension/blob/727026fce9f8c6a33d113373666d0776f8f6c23c/server/src/server.ts#L70)
-/
let doc := {uri, version? := some 0}
def ofTextEdit (doc : VersionedTextDocumentIdentifier) (te : TextEdit) : WorkspaceEdit :=
ofTextDocumentEdit { textDocument := doc, edits := #[te]}
end WorkspaceEdit

View File

@@ -74,6 +74,7 @@ structure ServerCapabilities where
declarationProvider : Bool := false
typeDefinitionProvider : Bool := false
referencesProvider : Bool := false
renameProvider? : Option RenameOptions := none
workspaceSymbolProvider : Bool := false
foldingRangeProvider : Bool := false
semanticTokensProvider? : Option SemanticTokensOptions := none

View File

@@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Marc Huisinga, Wojciech Nawrocki
-/
import Lean.Data.Lsp.Basic
import Lean.Data.Lsp.TextSync
import Lean.Server.Rpc.Basic
/-! This file contains Lean-specific extensions to LSP. See the structures below for which
@@ -13,6 +14,22 @@ additional requests and notifications are supported. -/
namespace Lean.Lsp
open Json
/--
Controls when dependencies are built on `textDocument/didOpen` notifications.
-/
inductive DependencyBuildMode where
/-- Always build dependencies. -/
| always
/-- Build dependencies once, but then do not build them again on import changes / crashes. Used for `didOpen` notifications that are explicitly intended for manually triggering the build. -/
| once
/-- Never build dependencies. -/
| never
deriving FromJson, ToJson, Inhabited
structure LeanDidOpenTextDocumentParams extends DidOpenTextDocumentParams where
dependencyBuildMode? : Option DependencyBuildMode := none -- `none`: Compatibility with clients pre-build-mode
deriving FromJson, ToJson
/-- `textDocument/waitForDiagnostics` client->server request.
Yields a response when all the diagnostics for a version of the document greater or equal to the

View File

@@ -62,8 +62,23 @@ def readMessage : IpcM JsonRpc.Message := do
def readRequestAs (expectedMethod : String) (α) [FromJson α] : IpcM (Request α) := do
(stdout).readLspRequestAs expectedMethod α
def readResponseAs (expectedID : RequestID) (α) [FromJson α] : IpcM (Response α) := do
(stdout).readLspResponseAs expectedID α
/--
Reads response, discarding notifications in between. This function is meant
purely for testing where we use `collectDiagnostics` explicitly if we do care
about such notifications. -/
partial def readResponseAs (expectedID : RequestID) (α) [FromJson α] :
IpcM (Response α) := do
let m (stdout).readLspMessage
match m with
| Message.response id result =>
if id == expectedID then
match fromJson? result with
| Except.ok v => pure expectedID, v
| Except.error inner => throw $ userError s!"Unexpected result '{result.compress}'\n{inner}"
else
throw $ userError s!"Expected id {expectedID}, got id {id}"
| .notification .. => readResponseAs expectedID α
| _ => throw $ userError s!"Expected JSON-RPC response, got: '{(toJson m).compress}'"
def waitForExit : IpcM UInt32 := do
(read).wait

View File

@@ -351,5 +351,16 @@ structure FoldingRange where
kind? : Option FoldingRangeKind := none
deriving ToJson
structure RenameOptions where
prepareProvider : Bool := false
deriving FromJson, ToJson
structure RenameParams extends TextDocumentPositionParams where
newName : String
deriving FromJson, ToJson
structure PrepareRenameParams extends TextDocumentPositionParams
deriving FromJson, ToJson
end Lsp
end Lean

View File

@@ -62,21 +62,24 @@ end String
namespace Lean
namespace FileMap
private def lineStartPos (text : FileMap) (line : Nat) : String.Pos :=
if h : line < text.positions.size then
text.positions.get line, h
else if text.positions.isEmpty then
0
else
text.positions.back
/-- Computes an UTF-8 offset into `text.source`
from an LSP-style 0-indexed (ln, col) position. -/
def lspPosToUtf8Pos (text : FileMap) (pos : Lsp.Position) : String.Pos :=
let colPos :=
if h : pos.line < text.positions.size then
text.positions.get pos.line, h
else if text.positions.isEmpty then
0
else
text.positions.back
let chr := text.source.utf16PosToCodepointPosFrom pos.character colPos
text.source.codepointPosToUtf8PosFrom colPos chr
let lineStartPos := lineStartPos text pos.line
let chr := text.source.utf16PosToCodepointPosFrom pos.character lineStartPos
text.source.codepointPosToUtf8PosFrom lineStartPos chr
def leanPosToLspPos (text : FileMap) : Lean.Position Lsp.Position
| ln, col => ln-1, text.source.codepointPosToUtf16PosFrom col (text.positions.get! $ ln - 1)
| line, col =>
line - 1, text.source.codepointPosToUtf16PosFrom col (lineStartPos text (line - 1))
def utf8PosToLspPos (text : FileMap) (pos : String.Pos) : Lsp.Position :=
text.leanPosToLspPos (text.toPosition pos)

View File

@@ -69,4 +69,10 @@ def NameTrie.forMatchingM [Monad m] (t : NameTrie β) (k : Name) (f : β → m U
def NameTrie.forM [Monad m] (t : NameTrie β) (f : β m Unit) : m Unit :=
t.forMatchingM Name.anonymous f
def NameTrie.matchingToArray (t : NameTrie β) (k : Name) : Array β :=
Id.run <| t.foldMatchingM k #[] fun v acc => acc.push v
def NameTrie.toArray (t : NameTrie β) : Array β :=
Id.run <| t.foldM #[] fun v acc => acc.push v
end Lean

View File

@@ -97,7 +97,7 @@ def toList (m : SMap α β) : List (α × β) :=
end SMap
def List.toSMap [BEq α] [Hashable α] (es : List (α × β)) : SMap α β :=
def _root_.List.toSMap [BEq α] [Hashable α] (es : List (α × β)) : SMap α β :=
es.foldl (init := {}) fun s (a, b) => s.insert a b
instance {_ : BEq α} {_ : Hashable α} [Repr α] [Repr β] : Repr (SMap α β) where

View File

@@ -1261,7 +1261,7 @@ def elabExplicitUnivs (lvls : Array Syntax) : TermElabM (List Level) := do
- When we elaborate choice nodes (and overloaded identifiers), we track multiple results using the `observing x` combinator.
The `observing x` executes `x` and returns a `TermElabResult`.
`observing `x does not check for synthetic sorry's, just an exception. Thus, it may think `x` worked when it didn't
`observing x` does not check for synthetic sorry's, just an exception. Thus, it may think `x` worked when it didn't
if a synthetic sorry was introduced. We decided that checking for synthetic sorrys at `observing` is not a good solution
because it would not be clear to decide what the "main" error message for the alternative is. When the result contains
a synthetic `sorry`, it is not clear which error message corresponds to the `sorry`. Moreover, while executing `x`, many

View File

@@ -668,12 +668,11 @@ def elabLetDeclAux (id : Syntax) (binders : Array Syntax) (typeStx : Syntax) (va
let body instantiateMVars body
mkLetFVars #[x] body (usedLetOnly := usedLetOnly)
else
let f withLocalDecl id.getId (kind := kind) .default type fun x => do
withLocalDecl id.getId (kind := kind) .default type fun x => do
addLocalVarInfo id x
let body elabTermEnsuringType body expectedType?
let body instantiateMVars body
mkLambdaFVars #[x] body (usedLetOnly := false)
pure <| mkLetFunAnnotation (mkApp f val)
mkLetFun x val body
if elabBodyFirst then
forallBoundedTelescope type binders.size fun xs type => do
-- the original `fvars` from above are gone, so add back info manually

View File

@@ -118,6 +118,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
@@ -126,6 +184,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
@@ -135,9 +217,123 @@ private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM
for idStx in ids do
let id := idStx.getId
let declName resolveNameUsingNamespaces nss idStx
if ( getInfoState).enabled then
addConstInfo idStx declName
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
@@ -223,6 +419,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

View File

@@ -241,7 +241,8 @@ where
/--
Helper method for elaborating terms such as `(.+.)` where a constant name is expected.
This method is usually used to implement tactics that function names as arguments (e.g., `simp`).
This method is usually used to implement tactics that take function names as arguments
(e.g., `simp`).
-/
def elabCDotFunctionAlias? (stx : Term) : TermElabM (Option Expr) := do
let some stx liftMacroM <| expandCDotArg? stx | pure none
@@ -252,11 +253,21 @@ def elabCDotFunctionAlias? (stx : Term) : TermElabM (Option Expr) := do
try Term.resolveId? f catch _ => return none
else
return none
| `(fun $binders* => binop% $f $a $b) =>
| `(fun $binders* => binop% $f $a $b)
| `(fun $binders* => binop_lazy% $f $a $b)
| `(fun $binders* => leftact% $f $a $b)
| `(fun $binders* => rightact% $f $a $b)
| `(fun $binders* => binrel% $f $a $b)
| `(fun $binders* => binrel_no_prop% $f $a $b) =>
if binders == #[a, b] then
try Term.resolveId? f catch _ => return none
else
return none
| `(fun $binders* => unop% $f $a) =>
if binders == #[a] then
try Term.resolveId? f catch _ => return none
else
return none
| _ => return none
where
expandCDotArg? (stx : Term) : MacroM (Option Term) :=

View File

@@ -348,7 +348,7 @@ def elabMutual : CommandElab := fun stx => do
throwErrorAt bad "invalid 'decreasing_by' in 'mutual' block, it must be used after the 'end' keyword"
elabMutualDef stx[1].getArgs hints
else
throwError "invalid mutual block"
throwError "invalid mutual block: either all elements of the block must be inductive declarations, or they must all be definitions/theorems/abbrevs"
/- leading_parser "attribute " >> "[" >> sepBy1 (eraseAttr <|> Term.attrInstance) ", " >> "]" >> many1 ident -/
@[builtin_command_elab «attribute»] def elabAttr : CommandElab := fun stx => do

View File

@@ -24,15 +24,15 @@ where
| [] => ``(isTrue rfl)
| (a, b, recField, isProof) :: todo => withFreshMacroScope do
let rhs if isProof then
`(have h : $a = $b := rfl; by subst h; exact $( mkSameCtorRhs todo):term)
`(have h : @$a = @$b := rfl; by subst h; exact $( mkSameCtorRhs todo):term)
else
`(if h : $a = $b then
`(if h : @$a = @$b then
by subst h; exact $( mkSameCtorRhs todo):term
else
isFalse (by intro n; injection n; apply h _; assumption))
if let some auxFunName := recField then
-- add local instance for `a = b` using the function being defined `auxFunName`
`(let inst := $(mkIdent auxFunName) $a $b; $rhs)
`(let inst := $(mkIdent auxFunName) @$a @$b; $rhs)
else
return rhs
@@ -113,7 +113,11 @@ def mkDecEq (declName : Name) : CommandElabM Bool := do
return false -- nested inductive types are not supported yet
else
let cmds liftTermElabM <| mkDecEqCmds indVal
cmds.forM elabCommand
-- `cmds` can have a number of syntax nodes quadratic in the number of constructors
-- and thus create as many info tree nodes, which we never make use of but which can
-- significantly slow down e.g. the unused variables linter; avoid creating them
withEnableInfoTree false do
cmds.forM elabCommand
return true
partial def mkEnumOfNat (declName : Name) : MetaM Unit := do

View File

@@ -1595,7 +1595,7 @@ mutual
partial def doSeqToCode : List Syntax M CodeBlock
| [] => do liftMacroM mkPureUnitAction
| doElem::doElems => withIncRecDepth <| withRef doElem do
checkMaxHeartbeats "`do`-expander"
checkSystem "`do`-expander"
match ( liftMacroM <| expandMacro? doElem) with
| some doElem => doSeqToCode (doElem::doElems)
| none =>

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
Authors: Leonardo de Moura, Kyle Miller, Sebastian Ullrich
-/
import Lean.Elab.App
import Lean.Elab.BuiltinNotation
@@ -79,13 +79,30 @@ private def throwForInFailure (forInInstance : Expr) : TermElabM Expr :=
namespace Op
/-!
The elaborator for `binop%`, `binop_lazy%`, and `unop%` terms.
The elaborator for expression trees of `binop%`, `binop_lazy%`, `leftact%`, `rightact%`, and `unop%` terms.
It works as follows:
At a high level, the elaborator tries to solve for a type that each of the operands in the expression tree
can be coerced to, while taking into account the expected type for the entire expression tree.
Once this type is computed (and if it exists), it inserts coercions where needed.
Here are brief descriptions of each of the operator types:
- `binop% f a b` elaborates `f a b` as a binary operator with two operands `a` and `b`,
and each operand participates in the protocol.
- `binop_lazy% f a b` is like `binop%` but elaborates as `f a (fun () => b)`.
- `unop% f a` elaborates `f a` as a unary operator with one operand `a`, which participates in the protocol.
- `leftact% f a b` elaborates `f a b` as a left action (the `a` operand "acts upon" the `b` operand).
Only `b` participates in the protocol since `a` can have an unrelated type, for example scalar multiplication of vectors.
- `rightact% f a b` elaborates `f a b` as a right action (the `b` operand "acts upon" the `a` operand).
Only `a` participates in the protocol since `b` can have an unrelated type.
This is used by `HPow` since, for example, there are both `Real -> Nat -> Real` and `Real -> Real -> Real`
exponentiation functions, and we prefer the former in the case of `x ^ 2`, but `binop%` would choose the latter. (#2220)
- There are also `binrel%` and `binrel_no_prop%` (see the docstring for `elabBinRelCore`).
The elaborator works as follows:
1- Expand macros.
2- Convert `Syntax` object corresponding to the `binop%` (`binop_lazy%` and `unop%`) term into a `Tree`.
The `toTree` method visits nested `binop%` (`binop_lazy%` and `unop%`) terms and parentheses.
2- Convert `Syntax` object corresponding to the `binop%/...` term into a `Tree`.
The `toTree` method visits nested `binop%/...` terms and parentheses.
3- Synthesize pending metavariables without applying default instances and using the
`(mayPostpone := true)`.
4- Tries to compute a maximal type for the tree computed at step 2.
@@ -117,18 +134,29 @@ coercions inside of a `HAdd` instance.
Remarks:
In the new `binop%` and related elaborators the decision whether a coercion will be inserted or not
is made at `binop%` elaboration time. This was not the case in the old elaborator.
For example, an instance, such as `HAdd Int ?m ?n`, could be created when executing
the `binop%` elaborator, and only resolved much later. We try to minimize this problem
by synthesizing pending metavariables at step 3.
* In the new `binop%` and related elaborators the decision whether a coercion will be inserted or not
is made at `binop%` elaboration time. This was not the case in the old elaborator.
For example, an instance, such as `HAdd Int ?m ?n`, could be created when executing
the `binop%` elaborator, and only resolved much later. We try to minimize this problem
by synthesizing pending metavariables at step 3.
For types containing heterogeneous operators (e.g., matrix multiplication), step 4 will fail
and we will skip coercion insertion. For example, `x : Matrix Real 5 4` and `y : Matrix Real 4 8`,
there is no coercion `Matrix Real 5 4` from `Matrix Real 4 8` and vice-versa, but
`x * y` is elaborated successfully and has type `Matrix Real 5 8`.
* For types containing heterogeneous operators (e.g., matrix multiplication), step 4 will fail
and we will skip coercion insertion. For example, `x : Matrix Real 5 4` and `y : Matrix Real 4 8`,
there is no coercion `Matrix Real 5 4` from `Matrix Real 4 8` and vice-versa, but
`x * y` is elaborated successfully and has type `Matrix Real 5 8`.
* The `leftact%` and `rightact%` elaborators are to handle binary operations where only one of
the arguments participates in the protocol. For example, in `2 ^ n + y` with `n : Nat` and `y : Real`,
we do not want to coerce `n` to be a real as well, but we do want to elaborate `2 : Real`.
-/
private inductive BinOpKind where
| regular -- `binop%`
| lazy -- `binop_lazy%`
| leftact -- `leftact%`
| rightact -- `rightact%`
deriving BEq
private inductive Tree where
/--
Leaf of the tree.
@@ -137,9 +165,9 @@ private inductive Tree where
-/
| term (ref : Syntax) (infoTrees : PersistentArray InfoTree) (val : Expr)
/--
`ref` is the original syntax that expanded into `binop%`.
`ref` is the original syntax that expanded into `binop%/...`.
-/
| binop (ref : Syntax) (lazy : Bool) (f : Expr) (lhs rhs : Tree)
| binop (ref : Syntax) (kind : BinOpKind) (f : Expr) (lhs rhs : Tree)
/--
`ref` is the original syntax that expanded into `unop%`.
-/
@@ -164,9 +192,11 @@ private partial def toTree (s : Syntax) : TermElabM Tree := do
where
go (s : Syntax) := do
match s with
| `(binop% $f $lhs $rhs) => processBinOp (lazy := false) s f lhs rhs
| `(binop_lazy% $f $lhs $rhs) => processBinOp (lazy := true) s f lhs rhs
| `(binop% $f $lhs $rhs) => processBinOp s .regular f lhs rhs
| `(binop_lazy% $f $lhs $rhs) => processBinOp s .lazy f lhs rhs
| `(unop% $f $arg) => processUnOp s f arg
| `(leftact% $f $lhs $rhs) => processBinOp s .leftact f lhs rhs
| `(rightact% $f $lhs $rhs) => processBinOp s .rightact f lhs rhs
| `(($e)) =>
if hasCDot e then
processLeaf s
@@ -181,9 +211,12 @@ where
return .macroExpansion macroName s s' ( go s')
| none => processLeaf s
processBinOp (ref : Syntax) (f lhs rhs : Syntax) (lazy : Bool) := do
processBinOp (ref : Syntax) (kind : BinOpKind) (f lhs rhs : Syntax) := do
let some f resolveId? f | throwUnknownConstant f.getId
return .binop (lazy := lazy) ref f ( go lhs) ( go rhs)
-- treat corresponding argument as leaf for `leftact/rightact`
let lhs if kind == .leftact then processLeaf lhs else go lhs
let rhs if kind == .rightact then processLeaf rhs else go rhs
return .binop ref kind f lhs rhs
processUnOp (ref : Syntax) (f arg : Syntax) := do
let some f resolveId? f | throwUnknownConstant f.getId
@@ -229,6 +262,8 @@ where
unless ( get).hasUncomparable do
match t with
| .macroExpansion _ _ _ nested => go nested
| .binop _ .leftact _ _ rhs => go rhs
| .binop _ .rightact _ lhs _ => go lhs
| .binop _ _ _ lhs rhs => go lhs; go rhs
| .unop _ _ arg => go arg
| .term _ _ val =>
@@ -259,9 +294,9 @@ private def toExprCore (t : Tree) : TermElabM Expr := do
match t with
| .term _ trees e =>
modifyInfoState (fun s => { s with trees := s.trees ++ trees }); return e
| .binop ref lazy f lhs rhs =>
| .binop ref kind f lhs rhs =>
withRef ref <| withInfoContext' ref (mkInfo := mkTermInfo .anonymous ref) do
mkBinOp lazy f ( toExprCore lhs) ( toExprCore rhs)
mkBinOp (kind == .lazy) f ( toExprCore lhs) ( toExprCore rhs)
| .unop ref f arg =>
withRef ref <| withInfoContext' ref (mkInfo := mkTermInfo .anonymous ref) do
mkUnOp f ( toExprCore arg)
@@ -347,7 +382,11 @@ mutual
where
go (t : Tree) (f? : Option Expr) (lhs : Bool) (isPred : Bool) : TermElabM Tree := do
match t with
| .binop ref lazy f lhs rhs =>
| .binop ref .leftact f lhs rhs =>
return .binop ref .leftact f lhs ( go rhs none false false)
| .binop ref .rightact f lhs rhs =>
return .binop ref .rightact f ( go lhs none false false) rhs
| .binop ref kind f lhs rhs =>
/-
We only keep applying coercions to `maxType` if `f` is predicate or
`f` has a homogenous instance with `maxType`. See `hasHomogeneousInstance` for additional details.
@@ -355,10 +394,10 @@ mutual
Remark: We assume `binrel%` elaborator is only used with homogenous predicates.
-/
if ( pure isPred <||> hasHomogeneousInstance f maxType) then
return .binop ref lazy f ( go lhs f true false) ( go rhs f false false)
return .binop ref kind f ( go lhs f true false) ( go rhs f false false)
else
let r withRef ref do
mkBinOp lazy f ( toExpr lhs none) ( toExpr rhs none)
mkBinOp (kind == .lazy) f ( toExpr lhs none) ( toExpr rhs none)
let infoTrees getResetInfoTrees
return .term ref infoTrees r
| .unop ref f arg =>
@@ -396,22 +435,24 @@ end
def elabOp : TermElab := fun stx expectedType? => do
toExpr ( toTree stx) expectedType?
@[builtin_term_elab binop]
def elabBinOp : TermElab := elabOp
@[builtin_term_elab binop_lazy]
def elabBinOpLazy : TermElab := elabOp
@[builtin_term_elab unop]
def elabUnOp : TermElab := elabOp
@[builtin_term_elab binop] def elabBinOp : TermElab := elabOp
@[builtin_term_elab binop_lazy] def elabBinOpLazy : TermElab := elabOp
@[builtin_term_elab leftact] def elabLeftact : TermElab := elabOp
@[builtin_term_elab rightact] def elabRightact : TermElab := elabOp
@[builtin_term_elab unop] def elabUnOp : TermElab := elabOp
/--
Elaboration functionf for `binrel%` and `binrel_no_prop%` notations.
Elaboration functions for `binrel%` and `binrel_no_prop%` notations.
We use the infrastructure for `binop%` to make sure we propagate information between the left and right hand sides
of a binary relation.
Recall that the `binrel_no_prop%` notation is used for relations such as `==` which do not support `Prop`, but
we still want to be able to write `(5 > 2) == (2 > 1)`.
- `binrel% R x y` elaborates `R x y` using the `binop%/...` expression trees in both `x` and `y`.
It is similar to how `binop% R x y` elaborates but with a significant difference:
it does not use the expected type when computing the types of the operads.
- `binrel_no_prop% R x y` elaborates `R x y` like `binrel% R x y`, but if the resulting type for `x` and `y`
is `Prop` they are coerced to `Bool`.
This is used for relations such as `==` which do not support `Prop`, but we still want
to be able to write `(5 > 2) == (2 > 1)` for example.
-/
def elabBinRelCore (noProp : Bool) (stx : Syntax) (expectedType? : Option Expr) : TermElabM Expr := do
match ( resolveId? stx[1]) with
@@ -448,7 +489,7 @@ def elabBinRelCore (noProp : Bool) (stx : Syntax) (expectedType? : Option Expr)
-/
let lhs withRef stx[2] <| toTree stx[2]
let rhs withRef stx[3] <| toTree stx[3]
let tree := .binop (lazy := false) stx f lhs rhs
let tree := .binop stx .regular f lhs rhs
let r analyze tree none
trace[Elab.binrel] "hasUncomparable: {r.hasUncomparable}, maxType: {r.max?}"
if r.hasUncomparable || r.max?.isNone then

View File

@@ -102,7 +102,8 @@ def runFrontend
: IO (Environment × Bool) := do
let inputCtx := Parser.mkInputContext input fileName
let (header, parserState, messages) Parser.parseHeader inputCtx
let (env, messages) processHeader header opts messages inputCtx trustLevel
-- allow `env` to be leaked, which would live until the end of the process anyway
let (env, messages) processHeader (leakEnv := true) header opts messages inputCtx trustLevel
let env := env.setMainModule mainModuleName
let mut commandState := Command.mkState env messages opts

View File

@@ -16,10 +16,11 @@ def headerToImports (header : Syntax) : Array Import :=
let id := stx[2].getId
{ module := id, runtimeOnly := runtime }
def processHeader (header : Syntax) (opts : Options) (messages : MessageLog) (inputCtx : Parser.InputContext) (trustLevel : UInt32 := 0)
def processHeader (header : Syntax) (opts : Options) (messages : MessageLog)
(inputCtx : Parser.InputContext) (trustLevel : UInt32 := 0) (leakEnv := false)
: IO (Environment × MessageLog) := do
try
let env importModules (headerToImports header) opts trustLevel
let env importModules (leakEnv := leakEnv) (headerToImports header) opts trustLevel
pure (env, messages)
catch e =>
let env mkEmptyEnvironment

View File

@@ -142,7 +142,7 @@ def MacroExpansionInfo.format (ctx : ContextInfo) (info : MacroExpansionInfo) :
return f!"Macro expansion\n{stx}\n===>\n{output}"
def UserWidgetInfo.format (info : UserWidgetInfo) : Format :=
f!"UserWidget {info.widgetId}\n{Std.ToFormat.format info.props}"
f!"UserWidget {info.id}\n{Std.ToFormat.format <| info.props.run' {}}"
def FVarAliasInfo.format (info : FVarAliasInfo) : Format :=
f!"FVarAlias {info.userName.eraseMacroScopes}"

View File

@@ -9,6 +9,8 @@ import Lean.Data.OpenDecl
import Lean.MetavarContext
import Lean.Environment
import Lean.Data.Json
import Lean.Server.Rpc.Basic
import Lean.Widget.Types
namespace Lean.Elab
@@ -95,17 +97,12 @@ structure CustomInfo where
stx : Syntax
value : Dynamic
/-- An info that represents a user-widget.
User-widgets are custom pieces of code that run on the editor client.
You can learn about user widgets at `src/Lean/Widget/UserWidget`
-/
structure UserWidgetInfo where
/-- Information about a user widget associated with a syntactic span.
This must be a panel widget.
A panel widget is a widget that can be displayed
in the infoview alongside the goal state. -/
structure UserWidgetInfo extends Widget.WidgetInstance where
stx : Syntax
/-- Id of `WidgetSource` object to use. -/
widgetId : Name
/-- Json representing the props to be loaded in to the component. -/
props : Json
deriving Inhabited
/--
Specifies that the given free variables should be considered semantically identical.

View File

@@ -57,7 +57,7 @@ private def resolveNameUsingNamespacesCore (nss : List Name) (idStx : Syntax) :
else
withRef idStx do throwError "ambiguous identifier '{idStx.getId}', possible interpretations: {result.map mkConst}"
def elabOpenDecl [MonadResolveName m] (stx : TSyntax ``Parser.Command.openDecl) : m (List OpenDecl) := do
def elabOpenDecl [MonadResolveName m] [MonadInfoTree m] (stx : TSyntax ``Parser.Command.openDecl) : m (List OpenDecl) := do
StateRefT'.run' (s := { openDecls := ( getOpenDecls), currNamespace := ( getCurrNamespace) }) do
match stx with
| `(Parser.Command.openDecl| $nss*) =>
@@ -73,18 +73,25 @@ def elabOpenDecl [MonadResolveName m] (stx : TSyntax ``Parser.Command.openDecl)
let nss resolveNamespace ns
for idStx in ids do
let declName resolveNameUsingNamespacesCore nss idStx
if ( getInfoState).enabled then
addConstInfo idStx declName
addOpenDecl (OpenDecl.explicit idStx.getId declName)
| `(Parser.Command.openDecl| $ns hiding $ids*) =>
let ns resolveUniqueNamespace ns
activateScoped ns
for id in ids do
let _ resolveId ns id
let declName resolveId ns id
if ( getInfoState).enabled then
addConstInfo id declName
let ids := ids.map (·.getId) |>.toList
addOpenDecl (OpenDecl.simple ns ids)
| `(Parser.Command.openDecl| $ns renaming $[$froms -> $tos],*) =>
let ns resolveUniqueNamespace ns
for («from», to) in froms.zip tos do
let declName resolveId ns «from»
if ( getInfoState).enabled then
addConstInfo «from» declName
addConstInfo to declName
addOpenDecl (OpenDecl.explicit to.getId declName)
| _ => throwUnsupportedSyntax
return ( get).openDecls

View File

@@ -146,7 +146,6 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
```
def namedPattern := check... >> trailing_parser "@" >> optional (atomic (ident >> ":")) >> termParser
```
TODO: pattern variable for equality proof
-/
let id := stx[0]
discard <| processVar id

View File

@@ -62,7 +62,7 @@ def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : Attribu
for preDef in preDefs do
applyAttributesAt preDef.declName preDef.modifiers.attrs applicationTime
def abstractNestedProofs (preDef : PreDefinition) : MetaM PreDefinition :=
def abstractNestedProofs (preDef : PreDefinition) : MetaM PreDefinition := withRef preDef.ref do
if preDef.kind.isTheorem || preDef.kind.isExample then
pure preDef
else do

View File

@@ -21,16 +21,25 @@ structure EqnInfoCore where
value : Expr
deriving Inhabited
partial def expand : Expr Expr
| Expr.letE _ _ v b _ => expand (b.instantiate1 v)
| Expr.mdata _ b => expand b
| e => e
/--
Zeta reduces `let` and `let_fun` while consuming metadata.
Returns true if progress is made.
-/
partial def expand (progress : Bool) (e : Expr) : Bool × Expr :=
match e with
| Expr.letE _ _ v b _ => expand true (b.instantiate1 v)
| Expr.mdata _ b => expand true b
| e =>
if let some (_, _, v, b) := e.letFun? then
expand true (b.instantiate1 v)
else
(progress, e)
def expandRHS? (mvarId : MVarId) : MetaM (Option MVarId) := do
let target mvarId.getType'
let some (_, lhs, rhs) := target.eq? | return none
unless rhs.isLet || rhs.isMData do return none
return some ( mvarId.replaceTargetDefEq ( mkEq lhs (expand rhs)))
let (true, rhs') := expand false rhs | return none
return some ( mvarId.replaceTargetDefEq ( mkEq lhs rhs'))
def funext? (mvarId : MVarId) : MetaM (Option MVarId) := do
let target mvarId.getType'

View File

@@ -100,8 +100,8 @@ def addPreDefinitions (preDefs : Array PreDefinition) (hints : TerminationHints)
let preDefs preDefs.mapM ensureNoUnassignedMVarsAtPreDef
let preDefs betaReduceLetRecApps preDefs
let cliques := partitionPreDefs preDefs
let mut terminationBy liftMacroM <| WF.expandTerminationBy hints.terminationBy? (cliques.map fun ds => ds.map (·.declName))
let mut decreasingBy liftMacroM <| WF.expandTerminationHint hints.decreasingBy? (cliques.map fun ds => ds.map (·.declName))
let mut terminationBy liftMacroM <| WF.expandTerminationBy? hints.terminationBy? (cliques.map fun ds => ds.map (·.declName))
let mut decreasingBy liftMacroM <| WF.expandDecreasingBy? hints.decreasingBy? (cliques.map fun ds => ds.map (·.declName))
let mut hasErrors := false
for preDefs in cliques do
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"

View File

@@ -113,12 +113,12 @@ private partial def replaceRecApps (recFnName : Name) (recArgInfo : RecArgInfo)
| Expr.letE n type val body _ =>
withLetDecl n ( loop below type) ( loop below val) fun x => do
mkLetFVars #[x] ( loop below (body.instantiate1 x)) (usedLetOnly := false)
| Expr.mdata d b =>
if let some _ := getRecAppSyntax? e then
loop below b
| Expr.mdata d b =>
if let some stx := getRecAppSyntax? e then
withRef stx <| loop below b
else
return mkMData d ( loop below b)
| Expr.proj n i e => return mkProj n i ( loop below e)
| Expr.proj n i e => return mkProj n i ( loop below e)
| Expr.app _ _ =>
let processApp (e : Expr) : StateRefT (HasConstCache recFnName) M Expr :=
e.withApp fun f args => do

View File

@@ -23,7 +23,11 @@ private partial def replaceIndPredRecApps (recFnName : Name) (recArgInfo : RecAr
| Expr.letE n type val body _ =>
withLetDecl n ( loop type) ( loop val) fun x => do
mkLetFVars #[x] ( loop (body.instantiate1 x))
| Expr.mdata d e => return mkMData d ( loop e)
| Expr.mdata d b => do
if let some stx := getRecAppSyntax? e then
withRef stx <| loop b
else
return mkMData d ( loop b)
| Expr.proj n i e => return mkProj n i ( loop e)
| Expr.app _ _ =>
let processApp (e : Expr) : M Expr := do

View File

@@ -83,19 +83,22 @@ def structuralRecursion (preDefs : Array PreDefinition) : TermElabM Unit :=
else do
let ((recArgPos, preDefNonRec), state) run <| elimRecursion preDefs[0]!
let preDefNonRec eraseRecAppSyntax preDefNonRec
let preDef eraseRecAppSyntax preDefs[0]!
let mut preDef eraseRecAppSyntax preDefs[0]!
state.addMatchers.forM liftM
mapError (addNonRec preDefNonRec (applyAttrAfterCompilation := false)) fun msg =>
m!"structural recursion failed, produced type incorrect term{indentD msg}"
-- We create the `_unsafe_rec` before we abstract nested proofs.
-- Reason: the nested proofs may be referring to the _unsafe_rec.
addAndCompilePartialRec #[preDef]
unless preDef.kind.isTheorem do
unless ( isProp preDef.type) do
preDef abstractNestedProofs preDef
/-
Don't save predefinition info for equation generator
for theorems and definitions that are propositions.
See issue #2327
-/
registerEqnsInfo preDef recArgPos
mapError (addNonRec preDefNonRec (applyAttrAfterCompilation := false)) fun msg =>
m!"structural recursion failed, produced type incorrect term{indentD msg}"
addAndCompilePartialRec #[preDef]
addSmartUnfoldingDef preDef recArgPos
applyAttributesOf #[preDefNonRec] AttributeApplicationTime.afterCompilation

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Meta.Transform
import Lean.Elab.RecAppSyntax
namespace Lean.Elab.Structural
open Meta
@@ -15,21 +16,39 @@ private def shouldBetaReduce (e : Expr) (recFnName : Name) : Bool :=
false
/--
Beta reduce terms where the recursive function occurs in the lambda term.
This is useful to improve the effectiveness of `elimRecursion`.
Preprocesses the expessions to improve the effectiveness of `elimRecursion`.
* Beta reduce terms where the recursive function occurs in the lambda term.
Example:
```
def f : Nat → Nat
| 0 => 1
| i+1 => (fun x => f x) i
```
* Floats out the RecApp markers.
Example:
```
def f : Nat → Nat
| 0 => 1
| i+1 => (f x) i
```
-/
def preprocess (e : Expr) (recFnName : Name) : CoreM Expr :=
Core.transform e
fun e =>
if shouldBetaReduce e recFnName then
return .visit e.headBeta
else
return .continue
(pre := fun e =>
if shouldBetaReduce e recFnName then
return .visit e.headBeta
else
return .continue)
(post := fun e =>
match e with
| .app (.mdata m f) a =>
if m.isRecApp then
return .done (.mdata m (.app f a))
else
return .done e
| _ => return .done e)
end Lean.Elab.Structural

View File

@@ -122,7 +122,7 @@ where
match ( reduceRecMatcher? e) with
| some e' => return Simp.Step.done { expr := e' }
| none =>
match ( Simp.simpMatchCore? app e SplitIf.discharge?) with
match ( Simp.simpMatchCore? app.matcherName e SplitIf.discharge?) with
| some r => return r
| none => return Simp.Step.visit { expr := e }

View File

@@ -13,28 +13,25 @@ import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.BRecOn
import Lean.Data.Array
namespace Lean.Elab.WF
open Meta
private def applyDefaultDecrTactic (mvarId : MVarId) : TermElabM Unit := do
let remainingGoals Tactic.run mvarId do
Tactic.evalTactic ( `(tactic| decreasing_tactic))
remainingGoals.forM fun mvarId => Term.reportUnsolvedGoals [mvarId]
private def mkDecreasingProof (decreasingProp : Expr) (decrTactic? : Option Syntax) : TermElabM Expr := do
let mvar mkFreshExprSyntheticOpaqueMVar decreasingProp
/-
Creates a subgoal for a recursive call, as an unsolved `MVar`. The goal is cleaned up, and
the current syntax reference is stored in the `MVar`s type as a `RecApp` marker, for
use by `solveDecreasingGoals` below.
-/
private def mkDecreasingProof (decreasingProp : Expr) : TermElabM Expr := do
-- We store the current Ref in the MVar as a RecApp annotation around the type
let ref getRef
let mvar mkFreshExprSyntheticOpaqueMVar (mkRecAppWithSyntax decreasingProp ref)
let mvarId := mvar.mvarId!
let mvarId mvarId.cleanup
match decrTactic? with
| none => applyDefaultDecrTactic mvarId
| some decrTactic =>
-- make info from `runTactic` available
pushInfoTree (.hole mvarId)
Term.runTactic mvarId decrTactic
instantiateMVars mvar
let _mvarId mvarId.cleanup
return mvar
private partial def replaceRecApps (recFnName : Name) (fixedPrefixSize : Nat) (decrTactic? : Option Syntax) (F : Expr) (e : Expr) : TermElabM Expr := do
private partial def replaceRecApps (recFnName : Name) (fixedPrefixSize : Nat) (F : Expr) (e : Expr) : TermElabM Expr := do
trace[Elab.definition.wf] "replaceRecApps:{indentExpr e}"
trace[Elab.definition.wf] "{F} : {← inferType F}"
loop F e |>.run' {}
@@ -46,7 +43,7 @@ where
let args := e.getAppArgs
let r := mkApp F ( loop F args[fixedPrefixSize]!)
let decreasingProp := ( whnf ( inferType r)).bindingDomain!
let r := mkApp r ( mkDecreasingProof decreasingProp decrTactic?)
let r := mkApp r ( mkDecreasingProof decreasingProp)
return mkAppN r ( args[fixedPrefixSize+1:].toArray.mapM (loop F))
processApp (F : Expr) (e : Expr) : StateRefT (HasConstCache recFnName) TermElabM Expr := do
@@ -163,6 +160,47 @@ private partial def processPSigmaCasesOn (x F val : Expr) (k : (F : Expr) → (v
else
k F val
private def applyDefaultDecrTactic (mvarId : MVarId) : TermElabM Unit := do
let remainingGoals Tactic.run mvarId do
Tactic.evalTactic ( `(tactic| decreasing_tactic))
unless remainingGoals.isEmpty do
Term.reportUnsolvedGoals remainingGoals
/-
Given an array of MVars, assign MVars with equal type and subsumed local context to each other.
Returns those MVar that did not get assigned.
-/
def assignSubsumed (mvars : Array MVarId) : MetaM (Array MVarId) :=
mvars.filterPairsM fun mv₁ mv₂ => do
let mvdecl₁ mv₁.getDecl
let mvdecl₂ mv₂.getDecl
if mvdecl₁.type == mvdecl₂.type then
-- same goals; check contexts.
if mvdecl₁.lctx.isSubPrefixOf mvdecl₂.lctx then
-- mv₁ is better
mv₂.assign (.mvar mv₁)
return (true, false)
if mvdecl₂.lctx.isSubPrefixOf mvdecl₁.lctx then
-- mv₂ is better
mv₁.assign (.mvar mv₂)
return (false, true)
return (true, true)
def solveDecreasingGoals (decrTactic? : Option Syntax) (value : Expr) : MetaM Expr := do
let goals getMVarsNoDelayed value
let goals assignSubsumed goals
goals.forM fun goal => Lean.Elab.Term.TermElabM.run' <|
match decrTactic? with
| none => do
let some ref := getRecAppSyntax? ( goal.getType)
| throwError "MVar does not look like like a recursive call"
withRef ref <| applyDefaultDecrTactic goal
| some decrTactic => do
-- make info from `runTactic` available
pushInfoTree (.hole goal)
Term.runTactic goal decrTactic
instantiateMVars value
def mkFix (preDef : PreDefinition) (prefixArgs : Array Expr) (wfRel : Expr) (decrTactic? : Option Syntax) : TermElabM Expr := do
let type instantiateForall preDef.type prefixArgs
let (wfFix, varName) forallBoundedTelescope type (some 1) fun x type => do
@@ -185,7 +223,8 @@ def mkFix (preDef : PreDefinition) (prefixArgs : Array Expr) (wfRel : Expr) (dec
let F := xs[1]!
let val := preDef.value.beta (prefixArgs.push x)
let val processSumCasesOn x F val fun x F val => do
processPSigmaCasesOn x F val (replaceRecApps preDef.declName prefixArgs.size decrTactic?)
processPSigmaCasesOn x F val (replaceRecApps preDef.declName prefixArgs.size)
let val solveDecreasingGoals decrTactic? val
mkLambdaFVars prefixArgs (mkApp wfFix ( mkLambdaFVars #[x, F] val))
end Lean.Elab.WF

View File

@@ -0,0 +1,711 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
import Lean.Util.HasConstCache
import Lean.Meta.CasesOn
import Lean.Meta.Match.Match
import Lean.Meta.Tactic.Cleanup
import Lean.Meta.Tactic.Refl
import Lean.Elab.Quotation
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.WF.TerminationHint
import Lean.Elab.PreDefinition.WF.PackMutual
import Lean.Data.Array
/-!
This module finds lexicographic termination arguments for well-founded recursion.
Starting with basic measures (`sizeOf xᵢ` for all parameters `xᵢ`), it tries all combinations
until it finds one where all proof obligations go through with the given tactic (`decerasing_by`),
if given, or the default `decreasing_tactic`.
For mutual recursion, a single measure is not just one parameter, but one from each recursive
function. Enumerating these can lead to a combinatoric explosion, so we bound
the nubmer of measures tried.
In addition to measures derived from `sizeOf xᵢ`, it also considers measures
that assign an order to the functions themselves. This way we can support mutual
function definitions where no arguments decrease from one function to another.
The result of this module is a `TerminationWF`, which is then passed on to `wfRecursion`; this
design is crucial so that whatever we infer in this module could also be written manually by the
user. It would be bad if there are function definitions that can only be processed with the
guessed lexicographic order.
The following optimizations are applied to make this feasible:
1. The crucial optimiziation is to look at each argument of each recursive call
_once_, try to prove `<` and (if that fails `≤`), and then look at that table to
pick a suitable measure.
2. The next-crucial optimization is to fill that table lazily. This way, we run the (likely
expensive) tactics as few times as possible, while still being able to consider a possibly
large number of combinations.
3. Before we even try to prove `<`, we check if the arguments are equal (`=`). No well-founded
measure will relate equal terms, likely this check is faster than firing up the tactic engine,
and it adds more signal to the output.
4. Instead of traversing the whole function body over and over, we traverse it once and store
the arguments (in unpacked form) and the local `MetaM` state at each recursive call
(see `collectRecCalls`), which we then re-use for the possibly many proof attempts.
The logic here is based on “Finding Lexicographic Orders for Termination Proofs in Isabelle/HOL”
by Lukas Bulwahn, Alexander Krauss, and Tobias Nipkow, 10.1007/978-3-540-74591-4_5
<https://www21.in.tum.de/~nipkow/pubs/tphols07.pdf>.
-/
set_option autoImplicit false
open Lean Meta Elab
namespace Lean.Elab.WF.GuessLex
register_builtin_option showInferredTerminationBy : Bool := {
defValue := false
descr := "In recursive definitions, show the inferred `termination_by` measure."
}
/--
Given a predefinition, find good variable names for its parameters.
Use user-given parameter names if present; use x1...xn otherwise.
The length of the returned array is also used to determine the arity
of the function, so it should match what `packDomain` does.
The names ought to accessible (no macro scopes) and still fresh wrt to the current environment,
so that with `showInferredTerminationBy` we can print them to the user reliably.
We do that by appending `'` as needed.
-/
partial
def naryVarNames (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Array Name):= do
lambdaTelescope preDef.value fun xs _ => do
let xs := xs.extract fixedPrefixSize xs.size
let mut ns : Array Name := #[]
for h : i in [:xs.size] do
let n (xs[i]'h.2).fvarId!.getUserName
let n := if n.hasMacroScopes then .mkSimple s!"x{i+1}" else n
ns := ns.push ( freshen ns n)
return ns
where
freshen (ns : Array Name) (n : Name): MetaM Name := do
if !(ns.elem n) && ( resolveGlobalName n).isEmpty then
return n
else
freshen ns (n.appendAfter "'")
/-- Internal monad used by `withRecApps` -/
abbrev M (recFnName : Name) (α β : Type) : Type :=
StateRefT (Array α) (StateRefT (HasConstCache recFnName) MetaM) β
/--
Traverses the given expression `e`, and invokes the continuation `k`
at every saturated call to `recFnName`.
The expression `param` is passed along, and refined when going under a matcher
or `casesOn` application.
-/
partial def withRecApps {α} (recFnName : Name) (fixedPrefixSize : Nat) (param : Expr) (e : Expr)
(k : Expr Array Expr MetaM α) : MetaM (Array α) := do
trace[Elab.definition.wf] "withRecApps: {indentExpr e}"
let (_, as) loop param e |>.run #[] |>.run' {}
return as
where
processRec (param : Expr) (e : Expr) : M recFnName α Unit := do
if e.getAppNumArgs < fixedPrefixSize + 1 then
loop param ( etaExpand e)
else
let a k param e.getAppArgs
modifyThe (Array α) (·.push a)
processApp (param : Expr) (e : Expr) : M recFnName α Unit := do
e.withApp fun f args => do
args.forM (loop param)
if f.isConstOf recFnName then
processRec param e
else
loop param f
containsRecFn (e : Expr) : M recFnName α Bool := do
modifyGetThe (HasConstCache recFnName) (·.contains e)
loop (param : Expr) (e : Expr) : M recFnName α Unit := do
if !( containsRecFn e) then
return
match e with
| Expr.lam n d b c =>
loop param d
withLocalDecl n c d fun x => do
loop param (b.instantiate1 x)
| Expr.forallE n d b c =>
loop param d
withLocalDecl n c d fun x => do
loop param (b.instantiate1 x)
| Expr.letE n type val body _ =>
loop param type
loop param val
withLetDecl n type val fun x => do
loop param (body.instantiate1 x)
| Expr.mdata _d b =>
if let some stx := getRecAppSyntax? e then
withRef stx <| loop param b
else
loop param b
| Expr.proj _n _i e => loop param e
| Expr.const .. => if e.isConstOf recFnName then processRec param e
| Expr.app .. =>
match ( matchMatcherApp? e) with
| some matcherApp =>
if !Structural.recArgHasLooseBVarsAt recFnName fixedPrefixSize e then
processApp param e
else
if let some altParams matcherApp.refineThrough? param then
(Array.zip matcherApp.alts (Array.zip matcherApp.altNumParams altParams)).forM
fun (alt, altNumParam, altParam) =>
lambdaTelescope altParam fun xs altParam => do
-- TODO: Use boundedLambdaTelescope
unless altNumParam = xs.size do
throwError "unexpected `casesOn` application alternative{indentExpr alt}\nat application{indentExpr e}"
let altBody := alt.beta xs
loop altParam altBody
else
processApp param e
| none =>
match ( toCasesOnApp? e) with
| some casesOnApp =>
if !Structural.recArgHasLooseBVarsAt recFnName fixedPrefixSize e then
processApp param e
else
if let some altParams casesOnApp.refineThrough? param then
(Array.zip casesOnApp.alts (Array.zip casesOnApp.altNumParams altParams)).forM
fun (alt, altNumParam, altParam) =>
lambdaTelescope altParam fun xs altParam => do
-- TODO: Use boundedLambdaTelescope
unless altNumParam = xs.size do
throwError "unexpected `casesOn` application alternative{indentExpr alt}\nat application{indentExpr e}"
let altBody := alt.beta xs
loop altParam altBody
else
processApp param e
| none => processApp param e
| e => do
let _ ensureNoRecFn recFnName e
/--
A `SavedLocalContext` captures the state and local context of a `MetaM`, to be continued later.
-/
structure SavedLocalContext where
savedLocalContext : LocalContext
savedLocalInstances : LocalInstances
savedState : Meta.SavedState
/-- Capture the `MetaM` state including local context. -/
def SavedLocalContext.create : MetaM SavedLocalContext := do
let savedLocalContext getLCtx
let savedLocalInstances getLocalInstances
let savedState saveState
return { savedLocalContext, savedLocalInstances, savedState }
/-- Run a `MetaM` action in the saved state. -/
def SavedLocalContext.run {α} (slc : SavedLocalContext) (k : MetaM α) :
MetaM α :=
withoutModifyingState $ do
withLCtx slc.savedLocalContext slc.savedLocalInstances do
slc.savedState.restore
k
/-- A `RecCallWithContext` focuses on a single recursive call in a unary predefinition,
and runs the given action in the context of that call. -/
structure RecCallWithContext where
/-- Syntax location of reursive call -/
ref : Syntax
/-- Function index of caller -/
caller : Nat
/-- Parameters of caller -/
params : Array Expr
/-- Function index of callee -/
callee : Nat
/-- Arguments to callee -/
args : Array Expr
ctxt : SavedLocalContext
/-- Store the current recursive call and its context. -/
def RecCallWithContext.create (ref : Syntax) (caller : Nat) (params : Array Expr) (callee : Nat)
(args : Array Expr) : MetaM RecCallWithContext := do
return { ref, caller, params, callee, args, ctxt := ( SavedLocalContext.create) }
/--
The elaborator is prone to duplicate terms, including recursive calls, even if the user
only wrote a single one. This duplication is wasteful if we run the tactics on duplicated
calls, and confusing in the output of GuessLex. So prune the list of recursive calls,
and remove those where another call exists that has the same goal and context that is no more
specific.
-/
def filterSubsumed (rcs : Array RecCallWithContext ) : Array RecCallWithContext := Id.run do
rcs.filterPairsM fun rci rcj => do
if rci.caller == rcj.caller && rci.callee == rcj.callee &&
rci.params == rcj.params && rci.args == rcj.args then
-- same goals; check contexts.
let lci := rci.ctxt.savedLocalContext
let lcj := rcj.ctxt.savedLocalContext
if lci.isSubPrefixOf lcj then
-- rci is better
return (true, false)
else if lcj.isSubPrefixOf lci then
-- rcj is better
return (false, true)
return (true, true)
/-- Traverse a unary PreDefinition, and returns a `WithRecCall` closure for each recursive
call site.
-/
def collectRecCalls (unaryPreDef : PreDefinition) (fixedPrefixSize : Nat) (arities : Array Nat)
: MetaM (Array RecCallWithContext) := withoutModifyingState do
addAsAxiom unaryPreDef
lambdaTelescope unaryPreDef.value fun xs body => do
unless xs.size == fixedPrefixSize + 1 do
-- Maybe cleaner to have lambdaBoundedTelescope?
throwError "Unexpected number of lambdas in unary pre-definition"
-- trace[Elab.definition.wf] "collectRecCalls: {xs} {body}"
let param := xs[fixedPrefixSize]!
withRecApps unaryPreDef.declName fixedPrefixSize param body fun param args => do
unless args.size fixedPrefixSize + 1 do
throwError "Insufficient arguments in recursive call"
let arg := args[fixedPrefixSize]!
let (caller, params) unpackArg arities param
let (callee, args) unpackArg arities arg
RecCallWithContext.create ( getRef) caller params callee args
/-- A `GuessLexRel` described how a recursive call affects a measure; whether it
decreases strictly, non-strictly, is equal, or else. -/
inductive GuessLexRel | lt | eq | le | no_idea
deriving Repr, DecidableEq
instance : ToString GuessLexRel where
toString | .lt => "<"
| .eq => "="
| .le => ""
| .no_idea => "?"
instance : ToFormat GuessLexRel where
format r := toString r
/-- Given a `GuessLexRel`, produce a binary `Expr` that relates two `Nat` values accordingly. -/
def GuessLexRel.toNatRel : GuessLexRel Expr
| lt => mkAppN (mkConst ``LT.lt [levelZero]) #[mkConst ``Nat, mkConst ``instLTNat]
| eq => mkAppN (mkConst ``Eq [levelOne]) #[mkConst ``Nat]
| le => mkAppN (mkConst ``LE.le [levelZero]) #[mkConst ``Nat, mkConst ``instLENat]
| no_idea => unreachable!
/-- Given an expression `e`, produce `sizeOf e` with a suitable instance. -/
def mkSizeOf (e : Expr) : MetaM Expr := do
let ty inferType e
let lvl getLevel ty
let inst synthInstance (mkAppN (mkConst ``SizeOf [lvl]) #[ty])
let res := mkAppN (mkConst ``sizeOf [lvl]) #[ty, inst, e]
check res
return res
/--
For a given recursive call, and a choice of parameter and argument index,
try to prove equality, < or ≤.
-/
def evalRecCall (decrTactic? : Option Syntax) (rcc : RecCallWithContext) (paramIdx argIdx : Nat) :
MetaM GuessLexRel := do
rcc.ctxt.run do
let param := rcc.params[paramIdx]!
let arg := rcc.args[argIdx]!
trace[Elab.definition.wf] "inspectRecCall: {rcc.caller} ({param}) → {rcc.callee} ({arg})"
let arg mkSizeOf rcc.args[argIdx]!
let param mkSizeOf rcc.params[paramIdx]!
for rel in [GuessLexRel.eq, .lt, .le] do
let goalExpr := mkAppN rel.toNatRel #[arg, param]
trace[Elab.definition.wf] "Goal for {rel}: {goalExpr}"
check goalExpr
let mvar mkFreshExprSyntheticOpaqueMVar goalExpr
let mvarId := mvar.mvarId!
let mvarId mvarId.cleanup
-- logInfo m!"Remaining goals: {goalsToMessageData [mvarId]}"
try
if rel = .eq then
MVarId.refl mvarId
else do
Lean.Elab.Term.TermElabM.run' do
match decrTactic? with
| none =>
let remainingGoals Tactic.run mvarId do
Tactic.evalTactic ( `(tactic| decreasing_tactic))
remainingGoals.forM fun mvarId => Term.reportUnsolvedGoals [mvarId]
-- trace[Elab.definition.wf] "Found {rel} proof: {← instantiateMVars mvar}"
pure ()
| some decrTactic => Term.withoutErrToSorry do
-- make info from `runTactic` available
pushInfoTree (.hole mvarId)
Term.runTactic mvarId decrTactic
-- trace[Elab.definition.wf] "Found {rel} proof: {← instantiateMVars mvar}"
pure ()
trace[Elab.definition.wf] "inspectRecCall: success!"
return rel
catch _e =>
trace[Elab.definition.wf] "Did not find {rel} proof: {goalsToMessageData [mvarId]}"
continue
return .no_idea
/- A cache for `evalRecCall` -/
structure RecCallCache where mk'' ::
decrTactic? : Option Syntax
rcc : RecCallWithContext
cache : IO.Ref (Array (Array (Option GuessLexRel)))
/-- Create a cache to memoize calls to `evalRecCall descTactic? rcc` -/
def RecCallCache.mk (decrTactic? : Option Syntax) (rcc : RecCallWithContext) :
BaseIO RecCallCache := do
let cache IO.mkRef <| Array.mkArray rcc.params.size (Array.mkArray rcc.args.size Option.none)
return { decrTactic?, rcc, cache }
/-- Run `evalRecCall` and cache there result -/
def RecCallCache.eval (rc: RecCallCache) (paramIdx argIdx : Nat) : MetaM GuessLexRel := do
-- Check the cache first
if let Option.some res := ( rc.cache.get)[paramIdx]![argIdx]! then
return res
else
let res evalRecCall rc.decrTactic? rc.rcc paramIdx argIdx
rc.cache.modify (·.modify paramIdx (·.set! argIdx res))
return res
/-- Print a single cache entry as a string, without forcing it -/
def RecCallCache.prettyEntry (rcc : RecCallCache) (paramIdx argIdx : Nat) : MetaM String := do
let cachedEntries rcc.cache.get
return match cachedEntries[paramIdx]![argIdx]! with
| .some rel => toString rel
| .none => "_"
/-- The measures that we order lexicographically can be comparing arguments,
or numbering the functions -/
inductive MutualMeasure where
/-- For every function, the given argument index -/
| args : Array Nat MutualMeasure
/-- The given function index is assigned 1, the rest 0 -/
| func : Nat MutualMeasure
/-- Evaluate a recursive call at a given `MutualMeasure` -/
def inspectCall (rc : RecCallCache) : MutualMeasure MetaM GuessLexRel
| .args argIdxs => do
let paramIdx := argIdxs[rc.rcc.caller]!
let argIdx := argIdxs[rc.rcc.callee]!
rc.eval paramIdx argIdx
| .func funIdx => do
if rc.rcc.caller == funIdx && rc.rcc.callee != funIdx then
return .lt
if rc.rcc.caller != funIdx && rc.rcc.callee == funIdx then
return .no_idea
else
return .eq
/--
Given a predefinition with value `fun (x_₁ ... xₙ) (y_₁ : α₁)... (yₘ : αₘ) => ...`,
where `n = fixedPrefixSize`, return an array `A` s.t. `i ∈ A` iff `sizeOf yᵢ` reduces to a literal.
This is the case for types such as `Prop`, `Type u`, etc.
These arguments should not be considered when guessing a well-founded relation.
See `generateCombinations?`
-/
def getForbiddenByTrivialSizeOf (fixedPrefixSize : Nat) (preDef : PreDefinition) : MetaM (Array Nat) :=
lambdaTelescope preDef.value fun xs _ => do
let mut result := #[]
for x in xs[fixedPrefixSize:], i in [:xs.size] do
try
let sizeOf whnfD ( mkAppM ``sizeOf #[x])
if sizeOf.isLit then
result := result.push i
catch _ =>
result := result.push i
return result
/--
Generate all combination of arguments, skipping those that are forbidden.
Sorts the uniform combinations ([0,0,0], [1,1,1]) to the front; they are commonly most useful to
try first, when the mutually recursive functions have similar argument structures
-/
partial def generateCombinations? (forbiddenArgs : Array (Array Nat)) (numArgs : Array Nat)
(threshold : Nat := 32) : Option (Array (Array Nat)) :=
(do goUniform 0; go 0 #[]) |>.run #[] |>.2
where
isForbidden (fidx : Nat) (argIdx : Nat) : Bool :=
if h : fidx < forbiddenArgs.size then
forbiddenArgs[fidx] |>.contains argIdx
else
false
-- Enumerate all permissible uniform combinations
goUniform (argIdx : Nat) : OptionT (StateM (Array (Array Nat))) Unit := do
if numArgs.all (argIdx < ·) then
unless forbiddenArgs.any (·.contains argIdx) do
modify (·.push (Array.mkArray numArgs.size argIdx))
goUniform (argIdx + 1)
-- Enumerate all other permissible combinations
go (fidx : Nat) : OptionT (ReaderT (Array Nat) (StateM (Array (Array Nat)))) Unit := do
if h : fidx < numArgs.size then
let n := numArgs[fidx]
for argIdx in [:n] do
unless isForbidden fidx argIdx do
withReader (·.push argIdx) (go (fidx + 1))
else
let comb read
unless comb.all (· == comb[0]!) do
modify (·.push comb)
if ( get).size > threshold then
failure
/--
Enumerate all meausures we want to try: All arguments (resp. combinations thereof) and
possible orderings of functions (if more than one)
-/
def generateMeasures (forbiddenArgs : Array (Array Nat)) (arities : Array Nat) :
MetaM (Array MutualMeasure) := do
let some arg_measures := generateCombinations? forbiddenArgs arities
| throwError "Too many combinations"
let func_measures :=
if arities.size > 1 then
(List.range arities.size).toArray
else
#[]
return arg_measures.map .args ++ func_measures.map .func
/--
The core logic of guessing the lexicographic order
Given a matrix that for each call and measure indicates whether that measure is
decreasing, equal, less-or-equal or unknown, It finds a sequence of measures
that is lexicographically decreasing.
The matrix is implemented here as an array of monadic query methods only so that
we can fill is lazily. Morally, this is a pure function
-/
partial def solve {m} {α} [Monad m] (measures : Array α)
(calls : Array (α m GuessLexRel)) : m (Option (Array α)) := do
go measures calls #[]
where
go (measures : Array α) (calls : Array (α m GuessLexRel)) (acc : Array α) := do
if calls.isEmpty then return .some acc
-- Find the first measure that has at least one < and otherwise only = or <=
for h : measureIdx in [:measures.size] do
let measure := measures[measureIdx]'h.2
let mut has_lt := false
let mut all_le := true
let mut todo := #[]
for call in calls do
let entry call measure
if entry = .lt then
has_lt := true
else
todo := todo.push call
if entry != .le && entry != .eq then
all_le := false
break
-- No progress here? Try the next measure
if not (has_lt && all_le) then continue
-- We found a suitable measure, remove it from the list (mild optimization)
let measures' := measures.eraseIdx measureIdx
return go measures' todo (acc.push measure)
-- None found, we have to give up
return .none
/--
Create Tuple syntax (`()` if the array is empty, and just the value if its a singleton)
-/
def mkTupleSyntax : Array Term MetaM Term
| #[] => `(())
| #[e] => return e
| es => `(($(es[0]!), $(es[1:]),*))
/--
Given an array of `MutualMeasures`, creates a `TerminationWF` that specifies the lexicographic
combination of these measures.
-/
def buildTermWF (declNames : Array Name) (varNamess : Array (Array Name))
(measures : Array MutualMeasure) : MetaM TerminationWF := do
let mut termByElements := #[]
for h : funIdx in [:varNamess.size] do
let vars := (varNamess[funIdx]'h.2).map mkIdent
let body mkTupleSyntax ( measures.mapM fun
| .args varIdxs => do
let v := vars.get! (varIdxs[funIdx]!)
let sizeOfIdent := mkIdent ( unresolveNameGlobal ``sizeOf)
`($sizeOfIdent $v)
| .func funIdx' => if funIdx' == funIdx then `(1) else `(0)
)
let declName := declNames[funIdx]!
termByElements := termByElements.push
{ ref := .missing
declName, vars, body,
implicit := true
}
return termByElements
/--
Given a matrix (row-major) of strings, arranges them in tabular form.
First column is left-aligned, others right-aligned.
Single space as column separator.
-/
def formatTable : Array (Array String) String := fun xss => Id.run do
let mut colWidths := xss[0]!.map (fun _ => 0)
for i in [:xss.size] do
for j in [:xss[i]!.size] do
if xss[i]![j]!.length > colWidths[j]! then
colWidths := colWidths.set! j xss[i]![j]!.length
let mut str := ""
for i in [:xss.size] do
for j in [:xss[i]!.size] do
let s := xss[i]![j]!
if j > 0 then -- right-align
for _ in [:colWidths[j]! - s.length] do
str := str ++ " "
str := str ++ s
if j = 0 then -- left-align
for _ in [:colWidths[j]! - s.length] do
str := str ++ " "
if j + 1 < xss[i]!.size then
str := str ++ " "
if i + 1 < xss.size then
str := str ++ "\n"
return str
/-- Concise textual representation of the source location of a recursive call -/
def RecCallWithContext.posString (rcc : RecCallWithContext) : MetaM String := do
let fileMap getFileMap
let .some pos := rcc.ref.getPos? | return ""
let position := fileMap.toPosition pos
let endPosStr := match rcc.ref.getTailPos? with
| some endPos =>
let endPosition := fileMap.toPosition endPos
if endPosition.line = position.line then
s!"-{endPosition.column}"
else
s!"-{endPosition.line}:{endPosition.column}"
| none => ""
return s!"{position.line}:{position.column}{endPosStr}"
/-- Explain what we found out about the recursive calls (non-mutual case) -/
def explainNonMutualFailure (varNames : Array Name) (rcs : Array RecCallCache) : MetaM Format := do
let header := varNames.map (·.eraseMacroScopes.toString)
let mut table : Array (Array String) := #[#[""] ++ header]
for i in [:rcs.size], rc in rcs do
let mut row := #[s!"{i+1}) {← rc.rcc.posString}"]
for argIdx in [:varNames.size] do
row := row.push ( rc.prettyEntry argIdx argIdx)
table := table.push row
return formatTable table
/-- Explain what we found out about the recursive calls (mutual case) -/
def explainMutualFailure (declNames : Array Name) (varNamess : Array (Array Name))
(rcs : Array RecCallCache) : MetaM Format := do
let mut r := Format.nil
for rc in rcs do
let caller := rc.rcc.caller
let callee := rc.rcc.callee
r := r ++ f!"Call from {declNames[caller]!} to {declNames[callee]!} " ++
f!"at {← rc.rcc.posString}:\n"
let header := varNamess[caller]!.map (·.eraseMacroScopes.toString)
let mut table : Array (Array String) := #[#[""] ++ header]
if caller = callee then
-- For self-calls, only the diagonal is interesting, so put it into one row
let mut row := #[""]
for argIdx in [:varNamess[caller]!.size] do
row := row.push ( rc.prettyEntry argIdx argIdx)
table := table.push row
else
for argIdx in [:varNamess[callee]!.size] do
let mut row := #[]
row := row.push varNamess[callee]![argIdx]!.eraseMacroScopes.toString
for paramIdx in [:varNamess[caller]!.size] do
row := row.push ( rc.prettyEntry paramIdx argIdx)
table := table.push row
r := r ++ formatTable table ++ "\n"
return r
def explainFailure (declNames : Array Name) (varNamess : Array (Array Name))
(rcs : Array RecCallCache) : MetaM Format := do
let mut r : Format := "The arguments relate at each recursive call as follows:\n" ++
"(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)\n"
if declNames.size = 1 then
r := r ++ ( explainNonMutualFailure varNamess[0]! rcs)
else
r := r ++ ( explainMutualFailure declNames varNamess rcs)
return r
end Lean.Elab.WF.GuessLex
namespace Lean.Elab.WF
open Lean.Elab.WF.GuessLex
/--
Main entry point of this module:
Try to find a lexicographic ordering of the arguments for which the recursive definition
terminates. See the module doc string for a high-level overview.
-/
def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
(fixedPrefixSize : Nat) (decrTactic? : Option Syntax) :
MetaM TerminationWF := do
let varNamess preDefs.mapM (naryVarNames fixedPrefixSize ·)
let arities := varNamess.map (·.size)
trace[Elab.definition.wf] "varNames is: {varNamess}"
let forbiddenArgs preDefs.mapM fun preDef =>
getForbiddenByTrivialSizeOf fixedPrefixSize preDef
-- The list of measures, including the measures that order functions.
-- The function ordering measures come last
let measures generateMeasures forbiddenArgs arities
-- If there is only one plausible measure, use that
if let #[solution] := measures then
return buildTermWF (preDefs.map (·.declName)) varNamess #[solution]
-- Collect all recursive calls and extract their context
let recCalls collectRecCalls unaryPreDef fixedPrefixSize arities
let recCalls := filterSubsumed recCalls
let rcs recCalls.mapM (RecCallCache.mk decrTactic? ·)
let callMatrix := rcs.map (inspectCall ·)
match liftMetaM <| solve measures callMatrix with
| .some solution => do
let wf buildTermWF (preDefs.map (·.declName)) varNamess solution
let wfStx withoutModifyingState do
preDefs.forM (addAsAxiom ·)
wf.unexpand
if showInferredTerminationBy.get ( getOptions) then
logInfo m!"Inferred termination argument:{wfStx}"
return wf
| .none =>
let explanation explainFailure (preDefs.map (·.declName)) varNamess rcs
Lean.throwError <| "Could not find a decreasing measure.\n" ++
explanation ++ "\n" ++
"Please use `termination_by` to specify a decreasing measure."

View File

@@ -7,10 +7,12 @@ import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.WF.TerminationHint
import Lean.Elab.PreDefinition.WF.PackDomain
import Lean.Elab.PreDefinition.WF.PackMutual
import Lean.Elab.PreDefinition.WF.Preprocess
import Lean.Elab.PreDefinition.WF.Rel
import Lean.Elab.PreDefinition.WF.Fix
import Lean.Elab.PreDefinition.WF.Eqns
import Lean.Elab.PreDefinition.WF.Ite
import Lean.Elab.PreDefinition.WF.GuessLex
namespace Lean.Elab
open WF
@@ -79,6 +81,7 @@ private def isOnlyOneUnaryDef (preDefs : Array PreDefinition) (fixedPrefixSize :
return false
def wfRecursion (preDefs : Array PreDefinition) (wf? : Option TerminationWF) (decrTactic? : Option Syntax) : TermElabM Unit := do
let preDefs preDefs.mapM fun preDef => return { preDef with value := ( preprocess preDef.value) }
let (unaryPreDef, fixedPrefixSize) withoutModifyingEnv do
for preDef in preDefs do
addAsAxiom preDef
@@ -87,10 +90,17 @@ def wfRecursion (preDefs : Array PreDefinition) (wf? : Option TerminationWF) (de
let preDefsDIte preDefs.mapM fun preDef => return { preDef with value := ( iteToDIte preDef.value) }
let unaryPreDefs packDomain fixedPrefixSize preDefsDIte
return ( packMutual fixedPrefixSize preDefs unaryPreDefs, fixedPrefixSize)
let wf
if let .some wf := wf? then
pure wf
else
guessLex preDefs unaryPreDef fixedPrefixSize decrTactic?
let preDefNonRec forallBoundedTelescope unaryPreDef.type fixedPrefixSize fun prefixArgs type => do
let type whnfForall type
let packedArgType := type.bindingDomain!
elabWFRel preDefs unaryPreDef.declName fixedPrefixSize packedArgType wf? fun wfRel => do
elabWFRel preDefs unaryPreDef.declName fixedPrefixSize packedArgType wf fun wfRel => do
trace[Elab.definition.wf] "wfRel: {wfRel}"
let (value, envNew) withoutModifyingEnv' do
addAsAxiom unaryPreDef
@@ -107,10 +117,13 @@ def wfRecursion (preDefs : Array PreDefinition) (wf? : Option TerminationWF) (de
withEnableInfoTree false do
addNonRec preDefNonRec (applyAttrAfterCompilation := false)
addNonRecPreDefs preDefs preDefNonRec fixedPrefixSize
-- We create the `_unsafe_rec` before we abstract nested proofs.
-- Reason: the nested proofs may be referring to the _unsafe_rec.
addAndCompilePartialRec preDefs
let preDefs preDefs.mapM (abstractNestedProofs ·)
registerEqnsInfo preDefs preDefNonRec.declName fixedPrefixSize
for preDef in preDefs do
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
addAndCompilePartialRec preDefs
builtin_initialize registerTraceClass `Elab.definition.wf

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