Declarations with `@[elab_as_elim]` could elaborate as type-incorrect
expressions. Reported by Jireh Loreaux [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/bug.20in.20revert/near/450522157).
(In principle the elabAsElim routine could revert fvars appearing in the
expected type that depend on the discriminants (if the discriminants are
fvars) to increase the likelihood of type correctness, but that's at the
cost of some complexity to both the elaborator and to the user.)
Now it suggests using `@[ext (iff := false)]` to disable generating the
`ext_iff` lemma.
This PR also adjusts error messages and attribute documentation.
Additionally, to simplify the code now the `x` and `y` arguments can't
come in reverse order (this feature was was added in the refactor
#4543).
Closes#4758
the internal constructions for structural and well-founded recursion
use plenty of `PProd` and `MProd`, and reading these, deeply
nested and in prefix notation, is unnecessarily troublesome.
Therefore this introduces notations
```
a ×ₚ b -- PProd a b
a ×ₘ b -- MProd a b
()ₚ -- PUnit.unit
(x,y,z)ₚ -- PProd.mk x (PProd.mk y z)
(x,y,z)ₘ -- MProd.mk x (MProd.mk y z)
```
(This is the post-stage0-part 2.)
the internal constructions for structural and well-founded recursion
use plenty of `PProd` and `MProd`, and reading these, deeply
nested and in prefix notation, is unnecessarily troublesome.
Therefore this introduces notations
```
a ×ₚ b -- PProd a b
a ×ₘ b -- MProd a b
()ₚ -- PUnit.unit
(x,y,z)ₚ -- PProd.mk x (PProd.mk y z)
(x,y,z)ₘ -- MProd.mk x (MProd.mk y z)
```
(This is part 1, the rest will follow in #4730 after a stage0 update.)
This now works:
```lean
inductive Tree where | node : List Tree → Tree
mutual
def Tree.size : Tree → Nat
| node ts => list_size ts
def Tree.list_size : List Tree → Nat
| [] => 0
| t::ts => t.size + list_size ts
end
```
It is still out of scope to expect to be able to use nested recursion
(e.g. through `List.map` or `List.foldl`) here.
Depends on #4718.
---------
Co-authored-by: Tobias Grosser <tobias@grosser.es>
the support for mutual structural recursion (new since #4575) is
extended so that Lean tries to infer it even without annotations.
* The error message when termination checking fails looks quite
different now. Maybe a bit better, maybe with more room for
improvements.
* If there are too many combinations (with an arbitrary cut-off) for a
given argument type, it will just give up and ask the user to use
`termination_by structural`.
* It is now legal to specify `termination_by structural` on not
necessarily all functions of a clique; this simply restricts the
combinations of arguments that Lean considers.
---------
Co-authored-by: Tobias Grosser <tobias@grosser.es>
We now get `.below` and `.brecOn` definitions for nested inductives.
No surprises in the implementation: the kernel already gives us suitable
`.rec_1` etc. recursors, and our construction follows the structure of
this recursor.
---------
Co-authored-by: Tobias Grosser <tobias@grosser.es>
Adds a command and tactic to print the `Array <| DiscrTree.Key` for
equalities helping the user to debug perceived `simp` failures.
---------
Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
When the `decide` tactic fails, it can try to give hints about the
failure:
- It tells you which `Decidable` instances it unfolded, by making use of
the diagnostics feature.
- If it encounters `Eq.rec`, it gives you a hint that one of these
instances was likely defined using tactics.
- If it encounters `Classical.choice`, it hints that you might have
classical instances in scope.
- During this, it tries to process `Decidable.rec`s and matchers to pin
blame on a particular instance that failed to reduce.
This idea comes from discussion with Heather Macbeth [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Decidable.20with.20structures/near/449409870).
this code
```
inductive N where
| cons : (Nat -> N) -> N
mutual
def f : N -> Nat
| .cons a => g (a 32) + 1
termination_by structural n => n
def g : N -> Nat
| .cons a => f (a 42) + 1
termination_by structural n => n
end
```
would break. When searching for the right `belowDict` we now have to,
evne after instantiating the paramters for a reflexive argument, again
search through a bunch of `PProd`s.
(Instead of searching we could pass down the index, but since we are
searching anyways in this function let's just re-use.)
Fixes: #4726
if will fail otherwise, but with a worse error message, and it's helpful
in later transformation to know that the parameters are the same for the
whole group.
Upstreaming of basic material on `List.Pairwise` and `List.Nodup`. More
complete API to follow later, this is just a first approximation of what
leansat will need.
I'll update `list_simp.lean` (simp normal form testing) and add missing
lemmas in follow-up PRs.
This just upstreams the material, and reorders the lemmas to match the
other sections.
This PR refactors the 'ext' attribute and implements the following
features:
- The 'local' and 'scoped' attribute kinds are now usable.
- The attribute realizes the `ext`/`ext_iff` lemmas when they do not
already exist, rather than always generating them. This is useful in
conjunction with `@[local ext]`.
- Adding `@[ext]` to a user ext lemma now realizes an `ext_iff` lemma as
well; formerly this was only for structures. The name of the generated
`ext_iff` theorem for a user `ext` theorem named `A.B.myext` is
`A.B.myext_iff`. If this process leads to an error, the user can write
`@[ext (iff := false)]` to disable this feature.
Breaking changes:
- Now the "x" and "y" term arguments to the realized `ext` and `ext_iff`
lemmas are implicit.
- Now the realized `ext` and `ext_iff` lemmas are protected.
Bootstrapping notes:
- There are a few `ext_iff` lemmas to address after the next stage0
update.
Closes https://github.com/leanprover/lean4/issues/3643
Suggested by Floris [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.22Missing.20Tactics.22.20list/near/446267660).
right now, in order to find out how many auxilary datatype are in a
mutual group of inductive with nested data type, one has to jump
through hoops like this:
```
private def numNestedInducts (indName : Name) : MetaM Nat := do
let .inductInfo indVal ← getConstInfo indName | panic! "{indName} is an inductive"
let .recInfo recVal ← getConstInfo (mkRecName indName) | panic! "{indName} has a recursor"
return recVal.numMotives - indVal.all.lengt
```
The `InductiveVal` data structure already has `.isNested : Bool`, so it
seems to be a natural extension to beef that up to `.numNested: Nat`.
This touched kernel code.
This adds support for mutual structural recursive functions.
For now this is opt-in: The functions must have a `termination_by
structural …` annotation (new since #4542) for this to work:
```lean
mutual
inductive A
| self : A → A
| other : B → A
| empty
inductive B
| self : B → B
| other : A → B
| empty
end
mutual
def A.size : A → Nat
| .self a => a.size + 1
| .other b => b.size + 1
| .empty => 0
termination_by structural x => x
def B.size : B → Nat
| .self b => b.size + 1
| .other a => a.size + 1
| .empty => 0
termination_by structural x => x
end
```
The recursive functions don’t have to be in a one-to-one relation to a
set of mutually recursive inductive data types. It is possible to ignore
some of the types:
```lean
def A.self_size : A → Nat
| .self a => a.self_size + 1
| .other _ => 0
| .empty => 0
termination_by structural x => x
```
or have more than one function per argument type:
```lean
def isEven : Nat → Prop
| 0 => True
| n+1 => ¬ isOdd n
termination_by structural x => x
def isOdd : Nat → Prop
| 0 => False
| n+1 => ¬ isEven n
termination_by structural x => x
```
This does not include
* Support for nested inductive data types or nested recursion
* Inferring mutual structural recursion in the absence of
`termination_by`.
* Functional induction principles for these.
* Mutually recursive functions that live in different universes. This
may be possible,
maybe after beefing up the `.below` and `.brecOn` functions; we can look
into this some
other time, maybe when there are concrete use cases.
---------
Co-authored-by: Richard Kiss <him@richardkiss.com>
Co-authored-by: Tobias Grosser <tobias@grosser.es>
Generalizes #3556 to not suppressing errors in tactic steps either when
the parse error is in a later step, as otherwise changes to the end of a
proof would affect (correctness or effectiveness of) incrementality of
preceding steps.
Fixes#4623, in combination with #4643
As we do not build multiple shared libraries on non-Windows anymore,
count the max exported symbols per static library instead.
Unfortunately, this still does seem to match the number on Windows.
The previous check, looking only at the type of the parameter, was too
permissive and led to ill-typed terms later on.
This fixes#4671.
In some cases the previous code might have worked by accident, in this
sense this is a breaking change. Affected functions can be fixed by
reordering their parameters to that all the function parameters that
occur in the parameter of the inductive type of the parameter that the
function recurses on come first.
### Preliminary PRs:
- [x] #4597
- [x] #4599
- [x] #4600
- [x] #4602
- [x] #4603
- [x] #4604
- [x] #4605
- [x] #4607
- [x] #4627
- [x] #4629
### Quick overview over API/naming changes compared to `Lean.HashMap`
and `Batteries.HashMap`:
#### Lean
* `find?` -> `get?`/`getElem?`
* `find!` -> `get!`/`gtetElem!`
* `findD` -> `getD`
* `findEntry?` -> not implemented for now
* `insert'` -> `containsThenInsert` (order reversed in result)
* `insertIfNew` -> `getThenInsertIfNew?` (order reversed in result)
* `numBuckets` -> `Internal.numBuckets`
* `ofListWith` -> not implemented for now
* `Array.groupByKey` -> not implemented for now
* `merge` -> not implemented for now, but you can use `insertMany`
#### Batteries
* `modify` -> not implemented for now
* `mergeWith` -> not implemented for now
* `mergeWithM` -> not implemented for now
I made a mistake in #4517, fixed here, so about time to add a test.
I wonder if this generic level optimization should be moved into
`mkLevelMax'`, but not today.
fixes#4650
This is an auxiliary procedured used by `rw` and `apply` tactics. It
synthesizes pending type class instances.
The new test contains an example where it failed. The comment at
`synthAppInstances.step` explains why, and the fix.
Now syntax nodes have their formatters run even if the parsers they wrap
are all arity zero. This fixes an issue where if `ppSpace` appears in a
`macro`/`elab` then it does not format with a space due to the fact that
macro argument processing wraps this as `group(ppSpace)`, and `ppSpace`
has arity zero.
Implementation note: the fix is to make the `visitArgs` formatter
combinator always visit the last child, even if it does not exist (in
which case the visited node will be `Syntax.missing`). To compensate,
parser combinators like many and optional need to be sure to keep track
of whether there any children. Only optional's needed to be modified.
Closes#4561
Summary:
- Adds configuration option `exponentiation.threshold`
- An expression `b^n` where `b` and `n` are literals is not reduced by
`whnf`, `simp`, and `isDefEq` if `n > exponentiation.threshold`.
Motivation: prevents system from becoming irresponsive and/or crashing
without memory.
TODO: improve support in the kernel. It is using a hard-coded limit for
now.
Before, `pp.instantiateMVars` generally had no effect because most call
sites for the pretty printer instantiated metavariables first, but now
this functionality is entrusted upon the `pp.instantiateMVars` option.
This also has an effect in hovers, where metavariables can be unfolded
one assignment at a time. However, the goal state still sees all
metavariables instantiated due to the fact that the algorithm relies on
expression equality post-instantiation (see
`Lean.Widget.goalToInteractive`).
Closes#4406
Closes#2736
See comment at `ExprDefEq.lean` for explanation.
Side effects:
- Improved error messages in two tests.
- Had to improve `getSuccesses` procedure at `App.lean`. It now
discards candidates that contain postponed elaboration problems.
If it is too disruptive for Mathlib, we should try to discard the
ones that have postponed metavariables.