Compare commits

...

217 Commits

Author SHA1 Message Date
Leonardo de Moura
c272ee245a perf: Replacement.apply
Avoid potentially expensive `e.replace` if it is not applicable.
2024-07-17 08:51:00 -07:00
Leonardo de Moura
1bd0c0deaf feat: add RBMap.isSingleton 2024-07-17 08:34:44 -07:00
Markus Himmel
8e396068e4 doc: mention linearity in hash map docstring (#4771) 2024-07-17 09:26:38 +00:00
Markus Himmel
c1df7564ce fix: resolve instances for HashMap via unification (#4759) 2024-07-17 08:02:22 +00:00
Markus Himmel
ba3565f441 chore: fix BEq argument order in hash map lemmas (#4732)
The previous argument order was a conscious choice, but I had missed
#3056.
2024-07-17 04:25:21 +00:00
Kim Morrison
af03af5037 feat: simprocs for #[1,2,3,4,5][2] (#4765)
None of these were working previously:

```
#check_simp #[1,2,3,4,5][2]  ~> 3
#check_simp #[1,2,3,4,5][2]? ~> some 3
#check_simp #[1,2,3,4,5][7]? ~> none
#check_simp #[][0]? ~> none
#check_simp #[1,2,3,4,5][2]! ~> 3
#check_simp #[1,2,3,4,5][7]! ~> (default : Nat)
#check_simp (#[] : Array Nat)[0]! ~> (default : Nat)
```
2024-07-17 03:05:17 +00:00
Leonardo de Moura
f6666fe266 chore: add missing withTraceNode (#4769)
Motivation: improve `trace.profiler`
2024-07-17 02:32:32 +00:00
Leonardo de Moura
c580684c22 perf: add ShareCommon.shareCommon' (#4767)
A more restrictive but efficient max sharing primitive.

**Motivation:** Some software verification proofs may contain
significant redundancy that can be eliminated using hash-consing (also
known as `shareCommon`). For example, [theorem
`sha512_block_armv8_test_4_sym`](460fe5d74c/Proofs/SHA512/SHA512Sym.lean (L29))
took a few seconds at [`addPreDefinitions`
](1a12f63f74/src/Lean/Elab/PreDefinition/Main.lean (L155))
and one second at `fixLevelParams` on a MacBook Pro (with M1 Pro). The
proof term initially had over 16 million subterms, but the redundancy
was indirectly and inefficiently eliminated using `Core.transform` at
`addPreDefinitions`. I tried to use `shareCommon` method to fix the
performance issue, but it was too inefficient. This PR introduces a new
`shareCommon'` method that, although less flexible (e.g., it uses only a
local cache and hash-consing table), is much more efficient. The new
procedure minimizes the number of RC operations and optimizes the
caching strategy. It is 20 times faster than the old `shareCommon`
procedure for theorem `sha512_block_armv8_test_4_sym`.
2024-07-17 01:33:54 +00:00
Joachim Breitner
1a12f63f74 refactor: move Synax.hasIdent, shake dependencies (#4766)
I noticed that a change to `Lean.PrettyPrinter.Delaborator.Builtins`
rebuilt more modules than I expected, so I moved a definition and
reduced some dependcies.

More reduction would be possible to move const-delaboration out of the
big `Lean.PrettyPrinter`, and import from `Lean.PrettyPrinter`
selectively.
2024-07-16 21:19:26 +00:00
Joachim Breitner
95b8095fa6 feat: PProd syntax (part 3) (#4756)
reworks #4730 based on feedback from @kmill:

 * Uses `×'` for PProd
 * No syntax for MProd for now
 * Angle brackets (without nesting) for the values
2024-07-16 21:06:04 +00:00
Kyle Miller
94cc8eb863 chore: add comment for why anonymous constructor notation isn't flattened during pretty printing (#4764) 2024-07-16 19:04:51 +00:00
Kim Morrison
1cf47bce5a chore: rename TC to Relation.TransGen (#4760)
This is barely used in Lean, and this rename is both more readable, and
consistent with further developments downstream.

See
[zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Relation.2ETransGen.20vs.2E.20TC.20from.20Init.2ECore/near/448941824)
discussion.
2024-07-16 17:06:49 +00:00
Leonardo de Moura
b73fe04710 feat: add Lean.Expr.numObjs (#4754)
Add helper function for computing the number of allocated
sub-expressions in a given expression. Note: Use this function primarily
for diagnosing performance issues.
2024-07-16 15:52:33 +00:00
Leonardo de Moura
f986a2e9ef chore: missing profileitM (#4753)
This PR addresses the absence of the `profileitM` function in two
auxiliary functions. The added `profileitM` instances are particularly
useful for diagnosing performance issues in declarations that contain
many repeated sub-terms.
2024-07-16 15:43:23 +00:00
Markus Himmel
1a9cbc96f1 chore: rename HashMap.remove to HashMap.erase (#4725)
The name `remove` was chosen because it is more popular in mainstream
programming languages, but being consistent with other Lean container
types (including `Lean.HashMap` and `Batteries.HashMap`) is more
important, so let's change the name while we still can.
2024-07-16 08:14:56 +00:00
Sebastian Ullrich
7aec6c9ae7 chore: temporarily remove test broken by #4746 2024-07-16 09:43:26 +02:00
Lean stage0 autoupdater
31de2494fb chore: update stage0 2024-07-15 21:53:05 +00:00
Lean stage0 autoupdater
d679591880 chore: update stage0 2024-07-15 21:29:34 +00:00
Sebastian Ullrich
f167cfba71 chore: exclude more symbols to get below Windows symbol limit 2024-07-15 23:19:04 +02:00
Joachim Breitner
180c6aaa5e feat: PProd and MProd syntax (part 2) (#4730)
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.)
2024-07-15 15:40:42 +00:00
Lean stage0 autoupdater
ab0241dac8 chore: update stage0 2024-07-15 15:02:32 +00:00
Joachim Breitner
dc65f03c41 feat: PProd and MProd syntax (part 1) (#4747)
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.)
2024-07-15 14:21:11 +00:00
Joachim Breitner
de96b6d8a7 feat: structural recursion over nested datatypes (#4733)
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>
2024-07-15 11:49:53 +00:00
Joachim Breitner
3ab2c714ec feat: infer mutual structural recursion (#4718)
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>
2024-07-15 09:34:06 +00:00
Joachim Breitner
f99427bd1a test: extend test for #4671 with nice example reported on zulip (#4740)
from
<https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.E2.9C.94.20Doubly-nested.20inductive/near/451204850>

I really like when I can respond to bug report with “will fix in -1
weeks”
2024-07-13 22:22:40 +00:00
Joachim Breitner
1118978cbb refactor: IndGroupInfo and IndGroupInst (#4738)
This adds the types
* `IndGroupInfo`, a variant of `InductiveVal` with information that
   applies to a whole group of mutual inductives and
* `IndGroupInst` which extends `IndGroupInfo` with levels and parameters
   to indicate a instantiation of the group.

One purpose of this abstraction is to make it clear when a fuction
operates on a group as a whole, rather than a specific inductive within
the group.

This is extracted from #4718 and #4733 to reduce PR size and improve
bisectability.
2024-07-13 08:30:09 +00:00
James Sully
4ea8c5ad8d doc: fix misplaced docstring for getThe (#4737) 2024-07-13 08:10:05 +00:00
Mac Malone
a6ae49c3ab feat: lake: cleaner release handling & related touchups (#4735)
Improves a number of elements related to Git checkouts, cloud releases,
and related error handling.

* On error, Lake now prints all top-level logs. Top-level logs are those
produced by Lake outside of the job monitor (e.g., when cloning
dependencies).
* When fetching a remote for a dependency, Lake now forcibly fetches
tags. This prevents potential errors caused by a repository recreating
tags already fetched.
* Tweaked Git error handling to hopefully be more informative.
* The builtin package facets `release`, `optRelease`, `extraDep` are now
caption in the same manner as other facets. Previously, they were
attempting to be too clever.
* `afterReleaseSync` and `afterReleaseAsync` now fetch `optRelease`
rather than `release`.
* Added support for optional jobs, whose failure does not cause the
whole build to failure (and made `optRelease` such a job).

Closes #4302.
2024-07-13 01:10:41 +00:00
Joachim Breitner
2ad6d397f8 refactor: use indVal.numNested or indVal.numTypeFormers where applicable (#4734)
follow-up to #4684
2024-07-12 22:07:25 +00:00
Joachim Breitner
891824bc51 feat: .below and .brecOn for nested inductive (#4658)
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>
2024-07-12 21:26:35 +00:00
Matthew Robert Ballard
f35c562ef8 feat: add #discr_tree_key command and discr_tree_key tactic (#4447)
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>
2024-07-12 15:05:10 +00:00
Joachim Breitner
bcd8517307 feat: Meta.withErasedFVars (#4731)
this idiom shows up multiple times, is non-trivial (in the sense that
the `localInsts` has to be updated, and I am about to use it once more.
Hence time to abstract this out.
2024-07-12 14:58:04 +00:00
Kyle Miller
ce73bbe277 feat: detailed feedback on decide tactic failure (#4674)
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).
2024-07-11 20:08:29 +00:00
Joachim Breitner
f0eab4b7b1 fix: nested structural recursion over reflexive data type (#4728)
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
2024-07-11 15:25:48 +00:00
Kyle Miller
5f70c1ca64 fix: make matcher pretty printer sensitive to pp.explicit (#4724)
Matchers usually have implicit arguments, and even if they don't the
notation hides the name of the matcher function.

Now when hovering over `match` expressions you can see the actual
underlying matcher expression.
2024-07-11 01:49:49 +00:00
Kim Morrison
fce82eba40 feat: further theorems for List.erase (#4723) 2024-07-10 22:19:12 +00:00
grunweg
9d14e4423c chore: fix typo in doc-string (#4719)
Fix a typo "to at" in a doc-string.
2024-07-10 22:03:11 +00:00
Kim Morrison
0c7859a7dd feat: upstream more erase API (#4720)
This should complete leansat's requirements.
2024-07-10 20:26:51 +00:00
Joachim Breitner
c01e003b49 fix: mutual structural recursion: check that datatype parameters agree (#4715)
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.
2024-07-10 08:14:57 +00:00
Leonardo de Moura
ce8a130724 fix: deprecated warnings for overloaded symbols (#4713)
closes #4636
2024-07-10 04:06:25 +00:00
Kyle Miller
3c18d151a6 fix: make iff theorem generated by @[ext] preserve inst implicits (#4710)
Previously all arguments from the ext theorem were made implicit, but
now only default and strict implicits are made implicit.
2024-07-10 03:48:39 +00:00
Leonardo de Moura
0f48e926eb fix: decide tactic transparency (#4711)
closes #4644
2024-07-10 01:40:32 +00:00
Leonardo de Moura
850964999e fix: Repr instances for Int and Float (#4709)
closes #4677
2024-07-10 00:10:58 +00:00
Kim Morrison
57b8b32c72 chore: reorganise lemmas on list getters (#4708)
Just reordering lemmas, sorting into subsections.
2024-07-09 22:55:20 +00:00
Kim Morrison
bd2aefee01 feat: simp normal form tests for Pairwise and Nodup (#4707) 2024-07-09 22:26:27 +00:00
Kim Morrison
74dcd6c2a9 feat: lemmas for List.head and List.getLast (#4678) 2024-07-09 22:13:41 +00:00
Kyle Miller
23b893f778 doc: update release checklist for new release notes workflow (#4458)
This makes it reflect how we are writing release notes for 4.9.0,
including how to handle the `releases_drafts` folder and how and when to
update `RELEASES.md`.

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-07-09 21:44:15 +00:00
Kim Morrison
1e02c08111 feat: basic material on List.Pairwise and Nodup (#4706)
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.
2024-07-09 21:39:08 +00:00
Kim Morrison
0f6a802314 feat: characterisations of List.Sublist (#4704) 2024-07-09 21:34:29 +00:00
Mario Carneiro
be197cd431 fix: prefer original module in const2ModIdx (#4652)
When a definition is redeclared, the original code would clobber the
value of `const2ModIdx` every time, meaning that a constant would be
attributed to a module which occurs later than the modules for constants
referencing this one. Preferring the original module ensures that these
module indexes are dependency-ordered. This originally came up as a bug
in `shake`, which assumes this property, see
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/check.20for.20unused.20imports.20doesn't.20stop/near/449139309).
2024-07-09 14:14:39 +00:00
Kim Morrison
f531f4e5db feat: chore upstream List.Sublist and API from Batteries (#4697)
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.
2024-07-09 12:57:09 +00:00
Kim Morrison
8229b28cc9 feat: omega doesn't push coercion over multiplication unnecessarily (#4695) 2024-07-09 12:49:31 +00:00
Sebastian Ullrich
582d6e7f71 chore: CI: update download-artifact actions 2024-07-09 10:17:19 +02:00
Mac Malone
4daa29e71d feat: lake: require @ git (#4692)
Adds syntactic sugar specifying a git revision as a dependency version
in a `require` command. For example:

```
require "leanprover-community" / "proofwidgets" @ git "v0.0.39"
```
2024-07-09 02:50:50 +00:00
Kim Morrison
9124426c55 chore: upstream eq_iff_true_of_subsingleton (#4689) 2024-07-08 21:09:33 +00:00
Kyle Miller
cb0755bac0 chore: make use of ext_iff realization now that stage0 is updated (#4694)
This is a followup to #4543. This also adds "go to definition" for
generated lemmas.
2024-07-08 21:05:53 +00:00
Lean stage0 autoupdater
4b32d9b9a1 chore: update stage0 2024-07-08 20:22:24 +00:00
Kyle Miller
7602265923 feat: make @[ext] derive ext_iff theorems from user ext theorems (#4543)
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).
2024-07-08 19:37:56 +00:00
Lean stage0 autoupdater
6ba5704e00 chore: update stage0 2024-07-08 21:18:50 +02:00
Joachim Breitner
98ee789990 refactor: InductiveVal.numNested instead of .isNested
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.
2024-07-08 21:18:50 +02:00
Kim Morrison
e08a562c48 chore: add step to release checklist (#4693) 2024-07-08 18:58:18 +00:00
Kim Morrison
84c40d9999 chore: improve compatibility of tests/list_simp with Mathlib (#4691)
I'd like to be able to automatically re-test simp normal forms
post-Mathlib. This makes the file compatible with Mathlib.
2024-07-08 17:17:28 +00:00
Kim Morrison
aecebaab74 chore: upstream SMap.foldM (#4690) 2024-07-08 17:11:58 +00:00
Kim Morrison
3b3901b824 chore: forward and backward directions of not_exists (#4688)
These are added in Batteries.
2024-07-08 16:31:04 +00:00
Kim Morrison
811c1e3685 chore: upstream ToExpr FilePath and compile_time_search_path% (#4453) 2024-07-08 15:41:03 +00:00
Kim Morrison
27e85cc947 chore: adjust List.replicate simp lemmas (#4687) 2024-07-08 15:29:19 +00:00
Henrik Böving
9a852595c4 feat: Process.tryWait (#4660)
Reopen of #4659 due to "processing updates" bug.
2024-07-08 15:14:13 +00:00
Joachim Breitner
1311e36a98 feat: structural mutual recursion (#4575)
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>
2024-07-08 14:39:50 +00:00
Joachim Breitner
db7a01d126 chore: update comments in kernel/declaration.h (#4683)
This file has comments that recall the data type definitions in Lean.
Most of them were still using lean3 syntax, and at least one of them was
out of date (one field missing), so I updated them.

I took the liberty to shorten the comments from the original file, or
omit them if they don’t add much over the field names.
2024-07-08 14:39:43 +00:00
Sebastian Ullrich
4d2f2d7cc5 fix: calculate error suppression per snapshot (#4657)
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
2024-07-08 09:54:43 +00:00
Sebastian Ullrich
f6265e25f4 chore: fix "max dynamic symbols" metric (#4669)
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.
2024-07-08 09:09:19 +00:00
Markus Himmel
d6c6e16254 fix: unorphan modules in Std.Data (#4679) 2024-07-08 07:57:56 +00:00
Markus Himmel
818b9d7de0 chore: update codeowners (#4681) 2024-07-08 07:57:54 +00:00
Markus Himmel
34e6579190 chore: Inhabited instances for Std.HashMap (#4682) 2024-07-08 07:57:51 +00:00
Markus Himmel
2b0ed751bd fix: unorphan modules in Init (#4680) 2024-07-08 07:57:50 +00:00
Kim Morrison
6ed26dcf8f chore: cleanup unused arguments (from linter) (#4621) 2024-07-07 21:20:06 +00:00
Joachim Breitner
955135b3f9 chore: bump actions/checkout and actions/upload-artifacts (#4664)
to reduce warnings on CI. Based on the changelogs/migrations guides I
found they should work as before.
2024-07-07 20:26:59 +00:00
Joachim Breitner
f36bbc8d56 fix: hasBadParamDep? to look at term, not type (#4672)
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.
2024-07-07 16:00:00 +00:00
Kim Morrison
64eeba726a chore: make Antisymm a Prop (#4666)
As pointed out on
[zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.60Antisymm.60.20is.20in.20.60Type.60/near/449084812).
2024-07-07 12:31:35 +00:00
Sebastian Ullrich
4ed79472af fix: explicitly initialize Std in lean_initialize (#4668)
Fixes the stage 2 build, which runs with `prefer_native=true`
2024-07-06 13:17:30 +00:00
Sebastian Ullrich
55d09a39b4 fix: move Std from libleanshared to much smaller libInit_shared (#4661)
Fixes the Windows build. As libLean is by far the biggest component,
there is no need for a separate libStd_shared for now.

```
$ find build/release/stage1/lib/lean -name '*.a' -exec bash -c 'echo -n "{} " ; nm {} | grep " T " | wc -l' \;
build/release/stage1/lib/lean/libleanrt.a 497
build/release/stage1/lib/lean/libleancpp.a 1320
build/release/stage1/lib/lean/libInit.a 7476
build/release/stage1/lib/lean/libStd.a 1696
build/release/stage1/lib/lean/libLean.a 64339
build/release/stage1/lib/lean/libLake.a 5722
```
2024-07-06 11:43:09 +02:00
Kyle Miller
7de0c58dc1 fix: don't set pp.tagAppFns when pretty printing signatures (#4665)
In #3911, a refactor to share `MessageData` code between `ppConst` and
the signature pretty printer unintentionally caused the signature pretty
printer to use the `pp.tagAppFns` option. This causes, for example, `+`
in `a + b` to independently have its own hover information due to the
fact that `notation` app unexpanders use the head function's syntax as
the `ref` when constructing the notation syntax. This behavior of
`pp.tagAppFns` is intentional, and it is used by docgen, but it should
not be activated for signatures.

This affects `#check` and was reported by Kevin Buzzard [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/degraded.20hover.20experience.20on.20.23check/near/449380674).

This PR also makes sure the initial `ref` when applying app unexpanders
is `.missing`, rather than whatever random value might be present in the
`CoreM` context.
2024-07-05 23:02:39 +00:00
Joachim Breitner
04fefdd728 chore: restart-on-label: wait for 30s (#4663)
this doesn't work reliable when it cancels jobs. Maybe sleeping helps.
2024-07-05 19:50:14 +00:00
Lean stage0 autoupdater
75fe520562 chore: update stage0 2024-07-05 16:43:21 +00:00
Markus Himmel
3e0ea762b8 feat: Std.HashMap (#4583)
### 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
2024-07-05 10:14:20 +00:00
Joachim Breitner
5ce886cf96 refactor: Split Constructions module (#4656)
for better build paralleization and less rebuilding when editing one of
these files.
2024-07-05 08:25:44 +00:00
Joachim Breitner
5ad5c2cf04 fix: universe level in .below and .brecOn construction (#4651)
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
2024-07-04 18:19:43 +00:00
Markus Himmel
d4e141e233 feat: EquivBEq and LawfulHashable classes (#4607)
Split from #4583

There are two open questions, opinions appreciated:

- Should this material be part of `Init` or `Std`?
- Should the typeclasses be in the `Std` namespace?
2024-07-04 05:07:18 +00:00
Markus Himmel
05f78939f6 feat: additional lemmas for arrays (#4627)
Split from #4583
2024-07-04 05:06:34 +00:00
Leonardo de Moura
7b965f3f18 fix: improve synthAppInstances (#4646)
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.
2024-07-03 19:14:25 +00:00
Joachim Breitner
0594bc4e5a refactor: lambdaBoundedTelescope (#4642)
we have a `forallBoundedTelescope`, and for a long while I was
wondering why we also don't have `lambdaBoundedTelescope`, and every now
and then felt the need for it. So let's just add it.
2024-07-03 15:57:12 +00:00
Sebastian Ullrich
3fb7f632a5 fix: snapshot subtree was not restored on reuse (#4643)
This could lead to nested error messages and info trees vanishing on
edits (strictly) below them

Fixes the second issue in #4623
2024-07-03 15:27:15 +00:00
Joachim Breitner
f6deaa8fb2 chore: update stage0 2024-07-03 14:54:58 +02:00
Joachim Breitner
15a41ffc1c refactor: include declNames in Structural.EqnInfo
this is in preparation for #4575. Because we use the EqnInfo in
attributes, this should be followed by a stage0 update before the field
is used.
2024-07-03 14:54:58 +02:00
Joachim Breitner
cc5c95f377 chore: CI: restart-on-label: view run more often (#4640)
this job sometimes fails, maybe a race condition with the `gh run
cancel` not happenign quickly enough. Maybe more verbose output will
help understand this better.
2024-07-03 12:35:50 +00:00
Sebastian Ullrich
62c5bc5d0d chore: manual nightly trigger (#4638)
Now that nightly releases may fail more often because of more checks,
let's make it possible to retry them on a later commit
2024-07-03 08:19:07 +00:00
Kyle Miller
c2edae92c8 fix: make sure syntax nodes always run their formatters (#4631)
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
2024-07-03 07:45:34 +00:00
Lean stage0 autoupdater
7ef95cd30b chore: update stage0 2024-07-03 07:48:23 +00:00
Alok Singh
2cd2364974 chore: typo (#4635) 2024-07-03 05:14:09 +00:00
Leonardo de Moura
f5fd962a25 feat: safe exponentiation (#4637)
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.
2024-07-03 05:12:53 +00:00
Markus Himmel
d72fcb6b2a feat: Option.or (#4600)
Split from #4583
2024-07-03 01:30:15 +00:00
Markus Himmel
a2a73e9611 feat: USize.and_toNat (#4629)
Split from #4583
2024-07-03 01:28:36 +00:00
Kyle Miller
3f2cf8bf27 fix: set default value of pp.instantiateMVars to true and make the option be effective (#4558)
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
2024-07-02 22:59:44 +00:00
Joachim Breitner
d39c4d6a1c chore: pr-release: use right tag name (#4632)
this improves upon #4598
2024-07-02 21:06:55 +00:00
Sebastian Ullrich
b28cfb9336 fix: Windows build (#4628) 2024-07-02 20:32:00 +00:00
Leonardo de Moura
6080e3dd5c fix: enforce isDefEqStuckEx at unstuckMVar procedure (#4596)
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.
2024-07-02 13:42:47 +00:00
Markus Himmel
8959b2ca87 chore: make constructor-as-variable test more robust (#4625)
The test tended to fail every time the number of structures in `Init`
changes, which turns out to be quite often.
2024-07-02 11:44:46 +00:00
Kim Morrison
554e723433 chore: add 'since' dates to deprecated (#4617) 2024-07-02 04:30:09 +00:00
Kim Morrison
9cc1164305 chore: follow simpNF linter's advice (#4620)
We can run the `simpNF` environment linter from Batteries. Nearly all
its advice is good.
2024-07-02 04:30:00 +00:00
Kim Morrison
0c6f83eb6d chore: satisfy duplicate namespace linter (#4616) 2024-07-02 04:29:56 +00:00
Kim Morrison
1225b0f651 chore: delete deprecations from 2022 (#4618) 2024-07-02 03:47:33 +00:00
Kim Morrison
75e11ecf7c chore: defs that should be theorems (#4619) 2024-07-02 03:03:11 +00:00
Markus Himmel
4055aecba2 feat: additional lemmas for bounded integers (#4605)
Split from #4583
2024-07-02 02:03:13 +00:00
Markus Himmel
1681b2fa67 feat: additional lemmas for cond (#4604)
Split from #4583
2024-07-02 02:02:41 +00:00
Markus Himmel
c97f958ecf feat: getElem_congr (#4603)
Split from #4583

Rewriting the `i` in `xs[i]` usually fails with `motive is not type
correct`, but with this lemma it works.
2024-07-02 02:02:17 +00:00
Markus Himmel
e2dc85274b feat: additional lemmas for lists (#4602)
Split from #4583

`exists_of_set` appears in Batteries as `exists_of_set'`. The
`exists_of_set` version is unused in batteries and mathlib at least and
I would argue that the primed version (i.e., the one added in this PR)
is always better anyway.

`isEmpty_iff` appears in mathlib as `isEmpty_iff_eq_nil`.
2024-07-02 02:01:44 +00:00
Markus Himmel
e12999bcf6 feat: additional lemmas for Option (#4599)
Split from #4583

Mathlib has `isSome_map'` but calls it `isSome_map`.
2024-07-02 01:58:33 +00:00
Markus Himmel
7a0fe6f54c feat: Nat.and_le_(left|right) (#4597)
Split from #4583
2024-07-02 01:55:12 +00:00
Mario Carneiro
4a2210b7e6 fix: unresolve name avoiding locals (#4593)
Fixes #4591. The extra code already existed in the only other user of
`unresolveNameGlobal` (in the pretty printer), although I did not make
it use this function because it has some additional behavior around
universes and in pattern position.
2024-07-02 01:15:39 +00:00
Siddharth
e9d2f8f5f2 feat: mul recurrence theorems for LeanSAT (#4568)
This implements the recurrence theorems `getLsb_mul`, `mulRec_zero_eq`,
`mulRec_succ_eq` to allow bitblasting multiplication.

---------

Co-authored-by: Kim Morrison <scott@tqft.net>
2024-07-01 23:47:29 +00:00
Lean stage0 autoupdater
d5a45dfa8b chore: update stage0 2024-07-01 17:36:58 +00:00
Joachim Breitner
fb0c46a011 feat: termination_by structural (#4542)
This implements the `termination_by structural` syntax proposed in
#3909.

I went with `termination_by structural` over, say,
`termination_by (config := {method := .structural})` mainly because it
was
easier to get going (otherwise I’d have to look into how to define
recursive
parsers, as `Parser.config` depends on `term` and `termination_by` is
part of
term. But also because I find it more ergonomic and aesthetic as a user.
But syntax can still change.

The `termination_by?` syntax will no longer force well-founded
recursion,
and instead the inferred `termination_by structurally` annotation will
be shown
if structural termination is possible.

While I was it, this fixes #4546 the easy way (log errors about but
otherwise
ignore incomplete `termination_by` sets for mutual recursion). Maybe we
get
multiple replacements (#4551), but even then this this good behavior.

Involves a bit of shuffling around `TerimationHints` (now validated for
a
clique already by `PreDefinition.main`) and `TerminationArguments` (now
lifted
out of the `WF` namespace, and a bit simplified).

Fixes #3909

---------

Co-authored-by: Richard Kiss <him@richardkiss.com>
2024-07-01 16:51:30 +00:00
Joachim Breitner
0635b277ec fix: diagnostics: show kernel diags even if it is the only section (#4611) 2024-07-01 16:45:39 +00:00
Joachim Breitner
087054172c feat: omega error message: normalize constraint order (#4612)
using the order as it comes out of the `HashMap` led to annying test
suite output variations. Moreover, sorting by the canonical order leads
to messages that are probably easier to digest as a user.
2024-07-01 16:11:15 +00:00
Sebastian Ullrich
7f00767b1e fix: adapt kernel interruption to new cancellation system (#4584)
Kernel checks were not canceled on edit after #3014
2024-07-01 14:52:42 +00:00
Joachim Breitner
be54ccd246 chore: pr-release: adjust to new lakefile.lean syntax (#4598)
as per

f5c3f06aa7
2024-07-01 08:48:33 +00:00
Kim Morrison
4d0b7cf66c chore: begin development cycle for v4.11.0 (#4594) 2024-06-30 23:28:48 +00:00
Joachim Breitner
0629eebc09 chore: release triggers update of release.lean-lang.org (#4531) 2024-06-30 10:39:32 +00:00
Wojciech Nawrocki
9248ada3a8 feat: total ByteArray.toList/findIdx? (#4582)
This is to enable proving facts about these functions.
2024-06-30 07:09:08 +00:00
Kyle Miller
144a3d9463 fix: typo hearbeats -> heartbeats (#4590)
Reported [on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/is.20.60trace.2Eprofiler.2EuseHeartbeats.60.20a.20thing.3F/near/447950838).
2024-06-30 07:07:11 +00:00
L
a7bbe7416b feat: upstream List.attach and Array.attach from Batteries (#4586)
Source material:

555ec79bc6/Batteries/Data/List/Init/Attach.lean

555ec79bc6/Batteries/Data/Array/Basic.lean (L133-L148)

Closes RFC #4414
2024-06-30 07:06:26 +00:00
Leonardo de Moura
f31d4dc128 chore: update stage0 2024-06-29 19:18:53 +02:00
Leonardo de Moura
fb97275dcb feat: add Simp.Config.implicitDefEqProofs
This commit does **not** implement this feature.
2024-06-29 19:18:53 +02:00
Leni Aniva
d4d7c72365 fix: Add linking of -lStd back into nix build flags on darwin (#4587)
Adds linkage to `Std` so the build behaviour on darwin is in line with
linux

I'm not sure why linking with `Std` is needed. I deleted it in the
previous patch https://github.com/leanprover/lean4/pull/3811 and Lean
still builds and runs. @tydeu mentioned this issue so I created this PR.
2024-06-29 08:12:57 +00:00
Mac Malone
93c9ae7c20 feat: lake: reservoir require (#4495)
Adds a new type of `require` which fetches package metadata from a
registry API endpoint (i.e., Reservoir) and then clones a Git package
using the information provided. To require such a dependency, the new
syntax is:

```lean
require <scope> / <pkg-name> [@ "git#<rev>"] -- e.g., require "leanprover" / "doc-gen4"
```

Or in TOML:

```toml
[[require]]
name = "<pkg-name>"
scope = "<scope>"
rev = "<rev>"
```

Unlike with Git dependencies, Lake can make use of the richer
information provided by the registry to determine the default branch of
the package. This means for repositories of packages like `doc-gen4`
which have a default branch that is not `master`, Lake will now use said
default branch (e.g., in `doc-gen4`'s case, `main`).

Lake also supports configuring the registry endpoint via an environment
variable: `RESERVIOR_API_URL`. Thus, any server providing a similar
interface to Reservoir can be used as the registry. Further
configuration options paralleling those of Cargo's [Alternative
Registries](https://doc.rust-lang.org/cargo/reference/registries.html)
and [Source
Replacement](https://doc.rust-lang.org/cargo/reference/source-replacement.html)
will come in the future.

Updated and split from #3174.
2024-06-29 01:40:54 +00:00
Leni Aniva
b8dd51500f fix: nix: add platform dependent flag to lib target (#3811)
Closes #3810
2024-06-28 10:40:11 +00:00
Kim Morrison
bd091f119b chore: fix bv_omega regression since v4.9.0 (#4579)
This example, reported from LNSym, started failing when we changed the
definition of `Fin.sub` in
https://github.com/leanprover/lean4/pull/4421.

When we use the new definition, `omega` produces a proof term that the
kernel is very slow on.

To work around this for now, I've removed `BitVec.toNat_sub` from the
`bv_toNat` simp set,
and replaced it with `BitVec.toNat_sub'` which uses the old definition
for subtraction.

This is only a workaround, and I would like to understand why the term
chokes the kernel.

```
example
    (n : Nat)
    (addr2 addr1 : BitVec 64)
    (h0 : n ≤ 18446744073709551616)
    (h1 : addr2 + 18446744073709551615#64 - addr1 ≤ BitVec.ofNat 64 (n - 1))
    (h2 : addr2 - addr1 ≤ addr2 + 18446744073709551615#64 - addr1) :
    n = 18446744073709551616 := by
  bv_omega
```
2024-06-28 01:20:08 +00:00
Leonardo de Moura
d8e719f9ab feat: add set_option debug.skipKernelTC true
The new option `set_option debug.skipKernelTC true` is meant for
temporarily working around kernel performance issues.
It compromises soundness because a buggy tactic may produce an invalid
proof, and the kernel will not catch it if the new option is set to true.
2024-06-28 00:55:47 +02:00
Leonardo de Moura
93d2ad5fa7 chore: update stage0 2024-06-28 00:55:47 +02:00
Leonardo de Moura
7b56eb20a0 feat: prepare for adding new option debug.skipKernelTC
Remark: I had to comment
```
if debug.skipKernelTC.get opts then
  addDeclWithoutChecking env decl
else
```
because the build was crashing when trying to compile Lake.
Going to perform `update-stage0` and try again.
2024-06-28 00:55:47 +02:00
Leonardo de Moura
30a922a7e9 feat: add option debug.byAsSorry true (#4576) 2024-06-27 18:29:26 +00:00
Mac Malone
294f7fbec5 fix: lake: computation of precompiled libs (#4566)
Addresses a few issues with precompile library computation. 

* Fixes a bug where Lake would always precompile the package of a
module.
* If a module is precompiled, it now precompiles its imports.
Previously, it would only do this if imported.

Closes #4565.
2024-06-27 15:08:52 +00:00
Sebastian Ullrich
f3cb8a6c2d fix: interrupt exception was swallowed by some tryCatchRuntimeEx uses (#4569)
This appears to have been a semantic merge conflict between #3940 and
#4129. The effect on the language server is that if two edits are
sufficiently close in time to create an interrupt, some elaboration
steps like `simp` may accidentally catch the exception when it is
triggered during their execution, which makes incrementality assume that
elaboration of the body was successful, which can lead to incorrect
reuse, presenting the interrupted state to the user with symptoms such
as "uses sorry" without accompanying errors and incorrect lints.
2024-06-27 10:03:22 +00:00
Kim Morrison
5c978a2e24 feat: remove Decidable instances from GetElem (#4560) 2024-06-27 02:09:29 +00:00
Leonardo de Moura
ee42c3ca56 fix: discrepancy in the elaborators for theorem, def, and example (#4482)
When the type of a definition or example is a proposition,
we should elaborate on them as we elaborate on theorems.
This is particularly important for examples that are often
used in educational material.

Recall that when elaborating theorem headers, we convert unassigned
universe metavariables into universe parameters. The motivation is
that the proof of a theorem should not influence its statement.
However, before this commit, this was not the case for definitions and
examples when their type was a proposition. This discrepancy often
confused users.

Additionally, we considered extending the above behavior whenever
the type of a definition is provided. That is, we would keep the
current behavior only if `: <type>` was omitted in a definition.
However, this proved to be too restrictive.
For example, the following instance in `Core.lean` would fail:
```
instance {α : Sort u} [Setoid α] : HasEquiv α :=
  ⟨Setoid.r⟩
```
and we would have to write instead:
```
instance {α : Sort u} [Setoid α] : HasEquiv.{u, 0} α :=
  ⟨Setoid.r⟩
```
There are other failures like this in the core, and we assume many more
in Mathlib.

closes #4398

@semorrison @jcommelin: what do you think?
2024-06-27 00:58:58 +00:00
Joachim Breitner
18c97926a1 refactor: extract withRecArgInfo from findRecArg (#4549)
this is  in preparation for #4542, and extracts from `findRecArg` the
functionality for trying one particular argument.

It also refactors the code a bit. In particular

 * It reports errors in the order of the parameters, not the order of
   in which they are tried (it tries non-indices first).
 * For every argument it will say why it wasn't tried, even if the
   reason is quite obviously (fixed prefix, or `Prop`-typed etc.)

Therefore there is some error message churn.
2024-06-26 11:10:57 +00:00
Joachim Breitner
ea22ef4485 refactor: port below and brecOn construction to Lean (#4517)
This ports the `.below` and `.brecOn` constructions to lean.

I kept them in the same file, as they were in the C code, because they
are
highly coupled and the constructions are very analogous.

For validation I developed this in a separate repository at
https://github.com/nomeata/lean-constructions/tree/fad715e
and checked that all declarations found in Lean and Mathlib are
equivalent, up to

    def canon (e : Expr) : CoreM Expr := do
      Core.transform (← Core.betaReduce e) (pre := fun
        | .const n ls  => return .done (.const n (ls.map (·.normalize)))
        | .sort l => return .done (.sort l.normalize)
        | _ => return .continue)

It was not feasible to make them completely equal, because the kernel's
type inference code seem to optimize level expressions a bit less
aggressively, and beta-reduces less in inference.

The private helper functions about `PProd` can later move into their own
file, used by these constructions as well as the structural recursion
module.
2024-06-26 11:10:39 +00:00
Leonardo de Moura
62b6e58789 fix: avoid unnecessary proof steps in simp (#4567)
closes #4534
2024-06-26 05:48:03 +00:00
Mac Malone
714dc6d2bb fix: lake: non-ident script names (#4564)
Fixes a bug where non-identifier script names could not be entered on
the CLI without French quotes. [Reported on
Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Running.20.60lake.60.20scripts.20from.20the.20command.20line/near/446944450).

Also adds support for string literal script names in a `lakefile.lean`.
2024-06-26 04:24:01 +00:00
Mac Malone
5e7d2c34dc fix: lake: exe bad import errors & related touchups (#4529)
Fixes some issues with the executable build and bad imports.

**Release notes:** 
* A bad import in an executable no longer prevents the executable's root
module from being built., This also fixes a problem where the location
of a transitive bad import would not been shown.
 * The root module of the executable now respects `nativeFacets`.

**Technical touchups:**

* Expanded and better documented `tests/badImport`.
* Use `ensureJob` in `recBuildDeps` to catch import errors instead of
individual `try ... catch` blocks.
2024-06-26 03:39:39 +00:00
Leonardo de Moura
fb6d29e260 fix: IndPredBelow should not add auxiliary declarations containing sorry (#4563)
Issue #4535 is being affected by a bug in the structural inductive
predicate termination checker (`IndPred.lean`). This module did not
exist in Lean 3, and it is buggy in Lean 4. In the given example, it
introduces an auxiliary declaration containing a `sorry`, and the fails.
This PR ensures this kind of declaration is not added to the
environment.

Closes #4535

TODO: we need a new maintainer for the `IndPred.lean`.
2024-06-25 20:57:32 +00:00
Leonardo de Moura
4964ce3ce8 fix: two functions with the same name in a where/let rec block (#4562)
closes #4547
2024-06-25 20:03:53 +00:00
Kyle Miller
230f335702 fix: block implicit lambda feature for type-free type ascription (#4536)
The implicit lambda feature is already blocked for type ascriptions, but
there is an oversight where it was not blocked for the `(x :)` type
ascription as well. Reported on
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.60refine.60.20with.20implicit.20variables/near/446327230).
2024-06-25 18:18:23 +00:00
Sebastian Ullrich
875e4b1904 fix: tactics in terms in tactic combinators breaking incrementality (#4554)
Fixes #4553
2024-06-25 08:59:38 +00:00
Kyle Miller
49249b9107 feat: introduce pp.maxSteps (#4556)
The `pp.maxSteps` option is a hard limit on the complexity of pretty
printer output, which is necessary to prevent the LSP from crashing when
there are accidental large terms. We're using the default value from the
corresponding Lean 3 option.

This PR also sets `pp.deepTerms` to `false` by default.
2024-06-24 19:19:45 +00:00
Kim Morrison
3b67e15827 feat: maximum?_eq_some_iff' (#4550)
Requested by @hargoniX.
2024-06-24 11:57:27 +00:00
Leonardo de Moura
e3578c2f36 fix: discrepancy theorem vs example (#4493)
When the type of an `example` is a proposition,
we should elaborate on them as we elaborate on theorems.
This is particularly important for examples that are often
used in educational material.

Recall that when elaborating theorem headers, we convert unassigned
universe metavariables into universe parameters. The motivation is
that the proof of a theorem should not influence its statement.
However, before this commit, this was not the case for examples when
their type was a proposition.
This discrepancy often confused users.

Additionally, we considered extending the above behavior to definitions
when
1- When their type is a proposition. However, it still caused disruption
in Mathlib.
2- When their type is provided. That is, we would keep the current
behavior only if `: <type>` was omitted. This would make the elaborator
for `def` much closer to the one for `theorem`, but it proved to be too
restrictive.
For example, the following instance in `Core.lean` would fail:
```
instance {α : Sort u} [Setoid α] : HasEquiv α :=
  ⟨Setoid.r⟩
```
and we would have to write instead:
```
instance {α : Sort u} [Setoid α] : HasEquiv.{u, 0} α :=
  ⟨Setoid.r⟩
```
There are other failures like this in the core, and we assume many more
in Mathlib.

closes #4398
closes #4482 Remark: PR #4482 implements option 1 above. We may consider
it again in the future.
2024-06-24 01:18:41 +00:00
Kim Morrison
0f416c6a83 chore: mark releases as prerelease (#4544) 2024-06-24 01:04:04 +00:00
Markus Schmaus
5178c4b6da feat: change succ to + 1 (#4532)
The simp normal form of `succ` is `+ 1`, this changes additional
theorems to use that normal form.
2024-06-24 00:38:22 +00:00
Siddharth
bc6188a70a feat: BitVec.twoPow and lemmas, toward bitblasting multiplication for LeanSAT (#4417)
We add a new definition `BitVec.twoPow w i` to represent `(1#w <<< i)`.
This expression is used to test bits when building the multiplication
bitblaster.

Patch 1/?, being peeled from https://github.com/opencompl/lean4/pull/6.

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
2024-06-23 22:37:02 +00:00
Leonardo de Moura
33f7865bbb fix: cached results at synthInstance? (#4530)
Synthesized type class instances may introduce new metavariables, and we
should actually cache `AbstractMVarsResult`.

closes #2283
2024-06-23 17:54:35 +00:00
Lean stage0 autoupdater
968aff403b chore: update stage0 2024-06-23 10:09:59 +00:00
Joachim Breitner
1076ca1ead chore: unset parseQuotWithCurrentStage in stage1’s src/stdlib_flags.h (#4537) 2024-06-23 09:44:14 +00:00
Bhavik Mehta
43a9c73556 chore: fix typo and incorrect name in doc (#4404)
Fixes typo "reflexivitiy" to "reflexivity", and changes exact Eq.rfl to
exact rfl, since Eq.rfl does not exist.

(I got something confused wrt the bot message on #4367 and accidentally
closed that one, so making this one instead, which I think satisfies the
requirements it wanted.)

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-06-23 09:06:50 +00:00
Kim Morrison
a92e9c7944 chore: move @[simp] from pred_le to sub_one_le (#4522)
(We already have a simp lemma unfolding `pred` to `· - 1`.)

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2024-06-23 07:58:38 +00:00
Joachim Breitner
378b02921d refactor: port recOn construction to Lean (#4516)
this is the simplest of the constructions to be ported from C++ to Lean,
so I’ll PR this one first.

This begins to put each construction into its own file, as it was the
case with C++.

For validation I developed this in a separate repository at
https://github.com/nomeata/lean-constructions/tree/fad715e
and checked that all `.recOn` declarations found in Lean and Mathlib are
identical (per `==`) to the ones produced by the C code.
2024-06-23 07:36:27 +00:00
Bolton Bailey
5426a5c8b3 chore: Remove simp from Option.elim, replace with individal simp lemmas (#4504)
This PR removes the `simp` attribute from `Option.elim` and adds it to
two related simp lemmas, `Option.elim_none` and `Option.elim_some`.

This PR comes from some discussion
[here](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/optionEquivLeft_apply.20simp/near/438321459)
about `simps!` feeling too aggressive in unfolding this lemma.
2024-06-23 00:58:25 +00:00
Kim Morrison
d7da45cbe6 chore: fix explicitness of Prod.map lemmas (#4533) 2024-06-22 11:05:19 +00:00
Mac Malone
24d51b90cc fix: lake: remove module dynlib from platform-independent trace (#4478)
Fixes a bug where Lake incorrectly included the module dynlib in a
platform-independent trace. It was incorrectly excluded only external
native libraries from the trace. Also adds a test.
2024-06-22 01:24:23 +00:00
Mac Malone
0d529e18a6 feat: expose flags for the bundled C compiler (#4477)
Expose the C compiler and linker flags used with the bundled compiler
(clang) to Lean code. This is needed to skip the use of `leanc` in Lake.
2024-06-22 01:23:33 +00:00
Sebastian Ullrich
4808eb7c4b chore: Nix: fix cacheRoots 2024-06-21 23:35:38 +02:00
Sebastian Ullrich
5767a597d4 chore: update stage0 2024-06-21 22:04:02 +02:00
Sebastian Ullrich
e665a0d716 chore: Nix: fix update-stage0 2024-06-21 22:02:10 +02:00
Joachim Breitner
073b2cfc83 fix: cdot parser error message range (#4528)
as #4527 describes there is inconsistency between `by`, `case` and
`next` on the one hand who, if the goal isn’t closed, put squiggly
underlines on the first line, and `.`, which so far only squiggled the
dot (which is a very short symbol!)

With this change the same mechanism as used by `case`, namely
`withCaseRef`, is also used for `.`.

There is an argument for the status quo: The `.` tactic is more commonly
used
with further tactics on the same line, and thus there is now a higher
risk that
the user might think that the first tactic is broken. But 

* the same argument does apply to `by` and `case` where there was an
intentional
  choice to do it this way
* consistency and
* a squiggly line just under the short `.` is easy to miss, so it is
actually
better to underlining more here (at least until we have a better way to
  indicate incomplete proofs, which I have hopes for)

Fixes #4527, at least most of it.
2024-06-21 15:06:07 +00:00
David Thrane Christiansen
84e46162b5 feat: more infrastructure for tactic documentation (#4490)
This is the groundwork for a tactic index in generated documentation, as
there was in Lean 3. There are a few challenges to getting this to work
well in Lean 4:
* There's no natural notion of *tactic identity* - a tactic may be
specified by multiple syntax rules (e.g. the pattern-matching version of
`intro` is specified apart from the default version, but both are the
same from a user perspective)
* There's no natural notion of *tactic name* - here, we take the
pragmatic choice of using the first keyword atom in the tactic's syntax
specification, but this may need to be overridable someday.
* Tactics are extensible, but we don't want to allow arbitrary imports
to clobber existing tactic docstrings, which could become unpredictable
in practice.

For tactic identity, this PR introduces the notion of a *tactic
alternative*, which is a `syntax` specification that is really "the same
as" an existing tactic, but needs to be separate for technical reasons.
This provides a notion of tactic identity, which we can use as the basis
of a tactic index in generated documentation. Alternative forms of
tactics are specified using a new `@[tactic_alt IDENT]` attribute,
applied to the new tactic syntax. It is an error to declare a tactic
syntax rule to be an alternative of another one that is itself an
alternative. Documentation hovers now take alternatives into account,
and display the docs for the canonical name.

*Tactic tags*, created with the `register_tactic_tag` command, specify
tags that may be applied to tactics. This is intended to be used by
doc-gen and Verso. Tags may be applied using the `@[tactic_tag TAG1 TAG2
...]` attribute on a canonical tactic parser, which may be used in any
module to facilitate downstream projects introducing tags that apply to
pre-existing tactics. Tags may not be removed, but it's fine to
redundantly add them. The collection of tags, and the tactics to which
they're applied, can be seen using the `#print tactic tags` command.

*Extension documentation* provides a structured way to document
extensions to tactics. The resulting documentation is gathered into a
bulleted list at the bottom of the tactic's docstring. Extensions are
added using the `tactic_extension TAC` command. This can be used when
adding new interpretations of a tactic via `macro_rules`, when extending
some table or search index used by the tactic, or in any other way. It
is a command to facilitate its flexible use with various extension
mechanisms.
2024-06-21 12:49:30 +00:00
Kim Morrison
a1a245df40 chore: missing Prod.map lemmas (#4526) 2024-06-21 11:53:50 +00:00
Kim Morrison
07ee719761 chore: fix statement of List.filter_congr (#4525) 2024-06-21 11:36:07 +00:00
Kim Morrison
ee9996ec89 chore: fix statement of List.filter_congr (#4524) 2024-06-21 11:35:43 +00:00
Markus Schmaus
d2ae678fbf feat: change List.length_cons to use + 1 instead of succ (#4500)
The simp normal form of `succ` is `+ 1`, this changes `List.length_cons`
to use that normal form.
2024-06-21 11:25:07 +00:00
David Thrane Christiansen
2a00d6cf70 doc: more detailed docstring for PersistentEnvExtension (#4501)
Describes the intended modes of use, potential performance tradeoffs,
and data representation in more detail.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
Co-authored-by: Mac Malone <tydeu@hatpress.net>
2024-06-21 08:34:04 +00:00
Sebastian Ullrich
d020a9c5a6 feat: introduce Std (#4499)
Situated between `Init` and `Lean`, provides functionality not in the
prelude to both Lean's implementation and external users
2024-06-21 07:08:45 +00:00
Kim Morrison
301a89aba4 feat: lemmas about List.map (#4521) 2024-06-21 06:40:30 +00:00
Mac Malone
f32780d863 refactor: lake: more robust trace reading (#4518)
The recent change of the trace format exposed some unexpected issues
with Lake's tracing handling. This aims to fix that.

Lake will now perform a rebuild if the trace file is invalid/unreadable.
However, it will still fall back to modification times if the trace file
is missing. Also, Lake is now backwards compatible with the previous
pure numeric traces (and tolerates the absence of a `log` field in the
JSON trace).
2024-06-21 01:43:05 +00:00
Sebastian Ullrich
d6eab393f4 chore: fix benchmark 2024-06-20 18:18:41 +02:00
Sebastian Ullrich
1f732bb3b7 fix: missing unboxing in interpreter when loading initialized value (#4512)
Fixes #4457
2024-06-20 10:06:24 +00:00
Joe Hendrix
7d7f378e02 feat: complete Int div/mod simprocs (#3850)
This PR introduces complete simprocs for all the Int versions of
div/mod, and makes some small refactoring of Int lemmas and
library_search.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2024-06-20 04:42:31 +00:00
Kim Morrison
95db616cb6 chore: cherry-pick stray test from #3850 (#4509)
#3850 included a commit that added an extra test for `exact?`, but was
otherwise unrelated the to PR. It also removed a test. I've
cherry-picked that test over, and restored the deleted test, and next
will remove the commit from #3850.
2024-06-20 01:00:45 +00:00
Leonardo de Moura
45c5d009d6 fix: dsimp missing theorems for literals (#4467) 2024-06-20 00:35:53 +00:00
Leonardo de Moura
458835360f fix: [implemented_by] at functions defined by well-founded recursion (#4508)
closes #2899
2024-06-20 00:06:38 +00:00
Leonardo de Moura
3e05b0641b chore: fix test 2024-06-20 01:05:52 +02:00
Leonardo de Moura
53be53f5ae chore: update stage0 2024-06-20 01:05:52 +02:00
Leonardo de Moura
dac1dacc5b feat: add Rewrite.Config.newGoals field
It is not used yet. We need a update-stage0.
2024-06-20 01:05:52 +02:00
Leonardo de Moura
d3a7569c97 refactor: move ApplyNewGoals and ApplyConfig to Init 2024-06-20 01:05:52 +02:00
Leonardo de Moura
49f058cb76 feat: open _root_.<namespace> (#4505)
closes #3045
2024-06-19 21:59:46 +00:00
Joachim Breitner
bc047b8530 refactor: port mk_definition_inferring_unsafe to Lean (#4498)
this already can be used in two places, and will be used more as I port
more constructions. Hope the location in `Lean.Environment` is ok.
2024-06-19 18:26:19 +00:00
JovanGerb
c7c50a8bec chore: fix linter errors (#4502)
The linters in Batteries can be used to spot mistakes in Lean. See the
message on
[Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Go-to-def.20on.20typeclass.20fields.20and.20type-dependent.20notation/near/442613564).
These are the different linters with errors:

- unusedArguments:
There are many unused instance arguments, especially a redundant `[Monad
m]` is very common
- checkUnivs:
There was a problem with universes in a definition in
`Init.Control.StateCps`. I fixed it by adding a `variable` statement for
the implicit arguments in the file.
- defLemma:
many proofs are written as `def` instead of `theorem`, most notably
`rfl`. Because `rfl` is used as a match pattern, it must be a def. Is
this desirable?
The keyword `abbrev` is sometimes used for an alias of a theorem, which
also results in a def. I would want to replace it with the `alias`
keyword to fix this, but it isn't available.
- dupNamespace:
I fixed some of these, but left `Tactic.Tactic` and `Parser.Parser` as
they are as these seem intended.
- unusedHaveSuffices:
  I cleaned up a few proofs with unused `have` or `suffices`
- explicitVarsOfIff:
  I didn't fix any of these, because that would be a breaking change.
- simpNF:
I didn't fix any of these, because I think that requires knowing the
intended simplification order.
2024-06-19 18:24:08 +00:00
Leonardo de Moura
de269060d1 chore: fix tests 2024-06-19 20:21:34 +02:00
Leonardo de Moura
e8f768f9fd chore: cleanup PersistentHashMap.lean 2024-06-19 20:21:34 +02:00
Leonardo de Moura
0783d0fcbe chore: re-enable #reduce elaborator 2024-06-19 20:21:34 +02:00
Leonardo de Moura
2518105bd9 chore: update stage0 2024-06-19 20:21:34 +02:00
Leonardo de Moura
9096d6fc71 fix: remove PersistentHashMap.size
It is buggy and was unnecessary overhead.

closes #3029
2024-06-19 20:21:34 +02:00
Mario Carneiro
0a1a855ba8 fix: validate UTF-8 at C++ -> Lean boundary (#3963)
Continuation of #3958. To ensure that lean code is able to uphold the
invariant that `String`s are valid UTF-8 (which is assumed by the lean
model), we have to make sure that no lean objects are created with
invalid UTF-8. #3958 covers the case of lean code creating strings via
`fromUTF8Unchecked`, but there are still many cases where C++ code
constructs strings from a `const char *` or `std::string` with unclear
UTF-8 status.

To address this and minimize accidental missed validation, the
`(lean_)mk_string` function is modified to validate UTF-8. The original
function is renamed to `mk_string_unchecked`, with several other
variants depending on whether we know the string is UTF-8 or ASCII and
whether we have the length and/or utf8 char count on hand. I reviewed
every function which leads to `mk_string` or its variants in the C code,
and used the appropriate validation function, defaulting to `mk_string`
if the provenance is unclear.

This PR adds no new error handling paths, meaning that incorrect UTF-8
will still produce incorrect results in e.g. IO functions, they are just
not causing unsound behavior anymore. A subsequent PR will handle adding
better error reporting for bad UTF-8.
2024-06-19 14:05:48 +00:00
Joachim Breitner
c4718a87ab refactor: constructions: modify environment in lean world (#4474)
this is a first step towards porting the code `constructions.cpp` to
Lean: It leaves the construction of the `Declaration` untouched, but
moves adding the declarations to the environment, and setting various
attributes, to the Lean world.

This allows the remaining logic (the construction of the `Declaration`)
to be implemented in Lean separately and easily compared to the C++
implementation, before we replace that too.

To that end, `Declaraion` gains an `BEq` instance.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
Co-authored-by: Arthur Adjedj <arthur.adjedj@ens-paris-saclay.fr>
2024-06-19 08:58:53 +00:00
Leonardo de Moura
357b52928f fix: global definition shadowing a local one when using dot-notation (#4497)
closes #3079
2024-06-19 05:52:45 +00:00
Mac Malone
bd45c0cd04 fix: lake: visit direct deps before adding manifest entries (#4485)
Fixes a bug in #4371 where the version of a package used by a dependency
would take precedence over that of a the same package as a direct
dependency if that package had a a manifest. This was because the direct
dependency's manifest entries were added before all the direct
dependencies were visited.
2024-06-19 02:49:59 +00:00
Mac Malone
f9952e8c39 refactor: touchup require syntax & docs (#4496)
A set of general tweaks of the `require` syntax and docs that provide a
base for #4495.

The sole significant behavioral change is that the `name` field of a
`require` in TOML now falls back to being interpreted as a simple string
name if the value is not a valid Lean identifier. This means that a
require for a package like `doc-gen4` can be written without French
quotes.
2024-06-19 02:49:57 +00:00
Kyle Miller
1b5b91cccf doc: add docstrings for dsimp configuration (#4258)
The dsimp configuration is a subset of the simp configuration, and so
it's a matter of copying the docstrings.
2024-06-19 00:05:25 +00:00
JovanGerb
74f1373706 chore: remove redundant if-else in isDefEqQuickOther (#4388)
I removed a redundant `if tFn.isMVar || sFn.isMVar then ... else return
LBool.undef` in the `else` clause of
```
if !tFn.isMVar && !sFn.isMVar then
  return LBool.undef
else
```
2024-06-18 23:44:29 +00:00
JovanGerb
c87205bc9b fix: eta reduce mvar assignments in isDefEq (#4387)
I made a modification to the `mkLambdaFVars` function, adding a
`etaReduce : Bool` parameter that determines whether a new lambda of the
form `fun x => f x` should be replaced by `f`. I then set this option to
true at `isDefEq` when processing metavariable assignments.

This means that many unnecessary eta unreduced expression are now
reduced. This is beneficial for users, so that they do not have to deal
with such unreduced expressions. It is also beneficial for performance,
leading to a 0.6% improvement in build instructions. Most notably,
`Mathlib.Algebra.DirectLimit`, previously a top 50 slowest file, has
sped up by 40%.

Quite a number of proof in mathlib broke. Many of these involve removing
a now unnecessary `simp only`. In other cases, a simp or rewrite doesn't
work anymore, such as a `simp_rw [mul_comm]` that was used to rewrite
`fun x => 2*x`, but now this term has turned into `HMul.hMul 2`.

Closes #4386
2024-06-18 23:41:40 +00:00
Leonardo de Moura
294b1d5839 chore: cleanup (#4494)
closes #4287
closes #4288
2024-06-18 23:39:16 +00:00
Leonardo de Moura
167771923e test: for isDefEq issue (#4492)
The issue has already been fixed in previous PRs.

closes #2461
2024-06-18 17:54:55 +00:00
Sebastian Ullrich
eb67654ae6 feat: incremental next and tactic if (#4459) 2024-06-18 12:36:59 +00:00
Joachim Breitner
6a8cb7ffa0 chore: nix shell: add gdb (#4476)
seems to work better when it comes from the same nixpkgs that we use for
building, it seems. And is plain convenient.
2024-06-18 11:07:20 +00:00
Kim Morrison
face4cef75 feat: complete API for List.replicate (#4487)
This is not the most exciting place to start, but I started here to:
* pick a function with little development in Batteries and Mathlib, so I
wouldn't have conflicts
* that is easy!
* to see how much effort it is to get fairly complete coverage
* and to set up some infrastructure to be used later, i.e.
`tests/lean/run/list_simp.lean`
2024-06-18 08:30:09 +00:00
Kim Morrison
6cad341764 chore: @[simp] List.getElem?_eq_getElem (#4488)
This is often helpful, and Mathlib doesn't mind.
2024-06-18 08:29:51 +00:00
Kim Morrison
2995e74133 chore: missing withSynthesize in #check_tactic (#4489)
Ran into this writing `#check_simp` tests for `List`. I guess it wasn't
a probably for `Bool` / `Prop`.
2024-06-18 05:24:45 +00:00
Kim Morrison
d768f46ba6 chore: @[simp] Nat.min_assoc (#4486) 2024-06-18 03:08:34 +00:00
1326 changed files with 23566 additions and 3780 deletions

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: actionlint
uses: raven-actions/actionlint@v1
with:

View File

@@ -9,6 +9,17 @@ on:
merge_group:
schedule:
- cron: '0 7 * * *' # 8AM CET/11PM PT
# for manual re-release of a nightly
workflow_dispatch:
inputs:
action:
description: 'Action'
required: true
default: 'release nightly'
type: choice
options:
- release nightly
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
@@ -41,11 +52,11 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
- name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
id: set-nightly
run: |
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
@@ -386,7 +397,7 @@ jobs:
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.release
with:
name: build-${{ matrix.name }}
@@ -459,7 +470,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Release
@@ -467,8 +478,14 @@ jobs:
with:
files: artifacts/*/*
fail_on_unmatched_files: true
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-rc') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update release.lean-lang.org
run: |
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
# This job creates nightly releases during the cron job.
# It is responsible for creating the tag, and automatically generating a changelog.
@@ -478,12 +495,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# needed for tagging
fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare Nightly Release
@@ -511,3 +528,8 @@ jobs:
repository: ${{ github.repository_owner }}/lean4-nightly
env:
GITHUB_TOKEN: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- name: Update release.lean-lang.org
run: |
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}

View File

@@ -50,7 +50,7 @@ jobs:
NIX_BUILD_ARGS: --print-build-logs --fallback
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# 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 }}

View File

@@ -234,7 +234,7 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
@@ -291,7 +291,7 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: leanprover-community/mathlib4
token: ${{ secrets.MATHLIB4_BOT }}
@@ -328,7 +328,7 @@ jobs:
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
git add lean-toolchain
sed -i "s/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \".\+\"/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
sed -i 's,require "leanprover-community" / "batteries" @ ".\+",require "leanprover-community" / "batteries" @ "git#nightly-testing-'"${MOST_RECENT_NIGHTLY}"'",' lakefile.lean
lake update batteries
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"

View File

@@ -20,10 +20,12 @@ jobs:
gh run view "$run_id"
echo "Cancelling (just in case)"
gh run cancel "$run_id" || echo "(failed)"
echo "Waiting for 10s"
sleep 10
echo "Waiting for 30s"
sleep 30
gh run view "$run_id"
echo "Rerunning"
gh run rerun "$run_id"
gh run view "$run_id"
shell: bash
env:
head_ref: ${{ github.head_ref }}

View File

@@ -23,7 +23,7 @@ jobs:
# 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
- uses: actions/checkout@v4
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"

View File

@@ -42,4 +42,4 @@
/src/Lean/Elab/Tactic/Guard.lean @digama0
/src/Init/Guard.lean @digama0
/src/Lean/Server/CodeActions/ @digama0
/src/Std/ @TwoFX

View File

@@ -8,13 +8,326 @@ This file contains work-in-progress notes for the upcoming release, as well as p
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
of each version.
v4.10.0
v4.11.0
----------
Development in progress.
v4.10.0
----------
Release candidate, release notes will be copied from branch `releases/v4.10.0` once completed.
v4.9.0
----------
Release candidate, release notes will be copied from branch `releases/v4.9.0` once completed.
### Language features, tactics, and metaprograms
* **Definition transparency**
* [#4053](https://github.com/leanprover/lean4/pull/4053) adds the `seal` and `unseal` commands, which make definitions locally be irreducible or semireducible.
* [#4061](https://github.com/leanprover/lean4/pull/4061) marks functions defined by well-founded recursion with `@[irreducible]` by default,
which should prevent the expensive and often unfruitful unfolding of such definitions (see breaking changes below).
* **Incrementality**
* [#3940](https://github.com/leanprover/lean4/pull/3940) extends incremental elaboration into various steps inside of declarations:
definition headers, bodies, and tactics.
![Recording 2024-05-10](https://github.com/leanprover/lean4/assets/109126/c9d67b6f-c131-4bc3-a0de-7d63eaf1bfc9).
* [250994](https://github.com/leanprover/lean4/commit/250994166ce036ab8644e459129f51ea79c1c2d2)
and [67338b](https://github.com/leanprover/lean4/commit/67338bac2333fa39a8656e8f90574784e4c23d3d)
add `@[incremental]` attribute to mark an elaborator as supporting incremental elaboration.
* [#4259](https://github.com/leanprover/lean4/pull/4259) improves resilience by ensuring incremental commands and tactics are reached only in supported ways.
* [#4268](https://github.com/leanprover/lean4/pull/4268) adds special handling for `:= by` so that stray tokens in tactic blocks do not inhibit incrementality.
* [#4308](https://github.com/leanprover/lean4/pull/4308) adds incremental `have` tactic.
* [#4340](https://github.com/leanprover/lean4/pull/4340) fixes incorrect info tree reuse.
* [#4364](https://github.com/leanprover/lean4/pull/4364) adds incrementality for careful command macros such as `set_option in theorem`, `theorem foo.bar`, and `lemma`.
* [#4395](https://github.com/leanprover/lean4/pull/4395) adds conservative fix for whitespace handling to avoid incremental reuse leading to goals in front of the text cursor being shown.
* [#4407](https://github.com/leanprover/lean4/pull/4407) fixes non-incremental commands in macros blocking further incremental reporting.
* [#4436](https://github.com/leanprover/lean4/pull/4436) fixes incremental reporting when there are nested tactics in terms.
* **Functional induction**
* [#4135](https://github.com/leanprover/lean4/pull/4135) ensures that the names used for functional induction are reserved.
* [#4327](https://github.com/leanprover/lean4/pull/4327) adds support for structural recursion on reflexive types.
For example,
```lean4
inductive Many (α : Type u) where
| none : Many α
| more : α → (Unit → Many α) → Many α
def Many.map {α β : Type u} (f : α → β) : Many α → Many β
| .none => .none
| .more x xs => .more (f x) (fun _ => (xs ()).map f)
#check Many.map.induct
/-
Many.map.induct {α β : Type u} (f : α → β) (motive : Many α → Prop)
(case1 : motive Many.none)
(case2 : ∀ (x : α) (xs : Unit → Many α), motive (xs ()) → motive (Many.more x xs)) :
∀ (a : Many α), motive a
-/
```
* [#3903](https://github.com/leanprover/lean4/pull/3903) makes the Lean frontend normalize all line endings to LF before processing.
This lets Lean be insensitive to CRLF vs LF line endings, improving the cross-platform experience and making Lake hashes be faithful to what Lean processes.
* [#4130](https://github.com/leanprover/lean4/pull/4130) makes the tactic framework be able to recover from runtime errors (for example, deterministic timeouts or maximum recursion depth errors).
* `split` tactic
* [#4211](https://github.com/leanprover/lean4/pull/4211) fixes `split at h` when `h` has forward dependencies.
* [#4349](https://github.com/leanprover/lean4/pull/4349) allows `split` for `if`-expressions to work on non-propositional goals.
* `apply` tactic
* [#3929](https://github.com/leanprover/lean4/pull/3929) makes error message for `apply` show implicit arguments in unification errors as needed.
Modifies `MessageData` type (see breaking changes below).
* `cases` tactic
* [#4224](https://github.com/leanprover/lean4/pull/4224) adds support for unification of offsets such as `x + 20000 = 20001` in `cases` tactic.
* `omega` tactic
* [#4073](https://github.com/leanprover/lean4/pull/4073) lets `omega` fall back to using classical `Decidable` instances when setting up contradiction proofs.
* [#4141](https://github.com/leanprover/lean4/pull/4141) and [#4184](https://github.com/leanprover/lean4/pull/4184) fix bugs.
* [#4264](https://github.com/leanprover/lean4/pull/4264) improves `omega` error message if no facts found in local context.
* [#4358](https://github.com/leanprover/lean4/pull/4358) improves expression matching in `omega` by using `match_expr`.
* `simp` tactic
* [#4176](https://github.com/leanprover/lean4/pull/4176) makes names of erased lemmas clickable.
* [#4208](https://github.com/leanprover/lean4/pull/4208) adds a pretty printer for discrimination tree keys.
* [#4202](https://github.com/leanprover/lean4/pull/4202) adds `Simp.Config.index` configuration option,
which controls whether to use the full discrimination tree when selecting candidate simp lemmas.
When `index := false`, only the head function is taken into account, like in Lean 3.
This feature can help users diagnose tricky simp failures or issues in code from libraries
developed using Lean 3 and then ported to Lean 4.
In the following example, it will report that `foo` is a problematic theorem.
```lean
opaque f : Nat → Nat → Nat
@[simp] theorem foo : f x (x, y).2 = y := by sorry
example : f a b ≤ b := by
set_option diagnostics true in
simp (config := { index := false })
/-
[simp] theorems with bad keys
foo, key: f _ (@Prod.mk _ _).2
-/
```
With the information above, users can annotate theorems such as `foo` using `no_index` for problematic subterms. Example:
```lean
opaque f : Nat → Nat → Nat
@[simp] theorem foo : f x (no_index (x, y).2) = y := by sorry
example : f a b ≤ b := by
simp -- `foo` is still applied with `index := true`
```
* [#4274](https://github.com/leanprover/lean4/pull/4274) prevents internal `match` equational theorems from appearing in simp trace.
* [#4177](https://github.com/leanprover/lean4/pull/4177) and [#4359](https://github.com/leanprover/lean4/pull/4359) make `simp` continue even if a simp lemma does not elaborate, if the tactic state is in recovery mode.
* [#4341](https://github.com/leanprover/lean4/pull/4341) fixes panic when applying `@[simp]` to malformed theorem syntax.
* [#4345](https://github.com/leanprover/lean4/pull/4345) fixes `simp` so that it does not use the forward version of a user-specified backward theorem.
* [#4352](https://github.com/leanprover/lean4/pull/4352) adds missing `dsimp` simplifications for fixed parameters of generated congruence theorems.
* [#4362](https://github.com/leanprover/lean4/pull/4362) improves trace messages for `simp` so that constants are hoverable.
* **Elaboration**
* [#4046](https://github.com/leanprover/lean4/pull/4046) makes subst notation (`he ▸ h`) try rewriting in both directions even when there is no expected type available.
* [#3328](https://github.com/leanprover/lean4/pull/3328) adds support for identifiers in autoparams (for example, `rfl` in `(h : x = y := by exact rfl)`).
* [#4096](https://github.com/leanprover/lean4/pull/4096) changes how the type in `let` and `have` is elaborated, requiring that any tactics in the type be evaluated before proceeding, improving performance.
* [#4215](https://github.com/leanprover/lean4/pull/4215) ensures the expression tree elaborator commits to the computed "max type" for the entire arithmetic expression.
* [#4267](https://github.com/leanprover/lean4/pull/4267) cases signature elaboration errors to show even if there are parse errors in the body.
* [#4368](https://github.com/leanprover/lean4/pull/4368) improves error messages when numeric literals fail to synthesize an `OfNat` instance,
including special messages warning when the expected type of the numeral can be a proposition.
* **Metaprogramming**
* [#4167](https://github.com/leanprover/lean4/pull/4167) adds `Lean.MVarId.revertAll` to revert all free variables.
* [#4169](https://github.com/leanprover/lean4/pull/4169) adds `Lean.MVarId.ensureNoMVar` to ensure the goal's target contains no expression metavariables.
* [#4180](https://github.com/leanprover/lean4/pull/4180) adds `cleanupAnnotations` parameter to `forallTelescope` methods.
* [#4307](https://github.com/leanprover/lean4/pull/4307) adds support for parser aliases in syntax quotations.
* Work toward implementing `grind` tactic
* [0a515e](https://github.com/leanprover/lean4/commit/0a515e2ec939519dafb4b99daa81d6bf3c411404)
and [#4164](https://github.com/leanprover/lean4/pull/4164)
add `grind_norm` and `grind_norm_proc` attributes and `@[grind_norm]` theorems.
* [#4170](https://github.com/leanprover/lean4/pull/4170), [#4221](https://github.com/leanprover/lean4/pull/4221),
and [#4249](https://github.com/leanprover/lean4/pull/4249) create `grind` preprocessor and core module.
* [#4235](https://github.com/leanprover/lean4/pull/4235) and [d6709e](https://github.com/leanprover/lean4/commit/d6709eb1576c5d40fc80462637dc041f970e4d9f)
add special `cases` tactic to `grind` along with `@[grind_cases]` attribute to mark types that this `cases` tactic should automatically apply to.
* [#4243](https://github.com/leanprover/lean4/pull/4243) adds special `injection?` tactic to `grind`.
* **Other fixes or improvements**
* [#4065](https://github.com/leanprover/lean4/pull/4065) fixes a bug in the `Nat.reduceLeDiff` simproc.
* [#3969](https://github.com/leanprover/lean4/pull/3969) makes deprecation warnings activate even for generalized field notation ("dot notation").
* [#4132](https://github.com/leanprover/lean4/pull/4132) fixes the `sorry` term so that it does not activate the implicit lambda feature
* [9803c5](https://github.com/leanprover/lean4/commit/9803c5dd63dc993628287d5f998525e74af03839)
and [47c8e3](https://github.com/leanprover/lean4/commit/47c8e340d65b01f4d9f011686e3dda0d4bb30a20)
move `cdot` and `calc` parsers to `Lean` namespace.
* [#4252](https://github.com/leanprover/lean4/pull/4252) fixes the `case` tactic so that it is usable in macros by having it erase macro scopes from the tag.
* [26b671](https://github.com/leanprover/lean4/commit/26b67184222e75529e1b166db050aaebee323d2d)
and [cc33c3](https://github.com/leanprover/lean4/commit/cc33c39cb022d8a3166b1e89677c78835ead1fc7)
extract `haveId` syntax.
* [#4335](https://github.com/leanprover/lean4/pull/4335) fixes bugs in partial `calc` tactic when there is mdata or metavariables.
* [#4329](https://github.com/leanprover/lean4/pull/4329) makes `termination_by?` report unused each unused parameter as `_`.
* **Docs:** [#4238](https://github.com/leanprover/lean4/pull/4238), [#4294](https://github.com/leanprover/lean4/pull/4294),
[#4338](https://github.com/leanprover/lean4/pull/4338).
### Language server, widgets, and IDE extensions
* [#4066](https://github.com/leanprover/lean4/pull/4066) fixes features like "Find References" when browsing core Lean sources.
* [#4254](https://github.com/leanprover/lean4/pull/4254) allows embedding user widgets in structured messages.
Companion PR is [vscode-lean4#449](https://github.com/leanprover/vscode-lean4/pull/449).
* [#4445](https://github.com/leanprover/lean4/pull/4445) makes watchdog more resilient against badly behaving clients.
### Library
* [#4059](https://github.com/leanprover/lean4/pull/4059) upstreams many `List` and `Array` operations and theorems from Batteries.
* [#4055](https://github.com/leanprover/lean4/pull/4055) removes the unused `Inhabited` instance for `Subtype`.
* [#3967](https://github.com/leanprover/lean4/pull/3967) adds dates in existing `@[deprecated]` attributes.
* [#4231](https://github.com/leanprover/lean4/pull/4231) adds boilerplate `Char`, `UInt`, and `Fin` theorems.
* [#4205](https://github.com/leanprover/lean4/pull/4205) fixes the `MonadStore` type classes to use `semiOutParam`.
* [#4350](https://github.com/leanprover/lean4/pull/4350) renames `IsLawfulSingleton` to `LawfulSingleton`.
* `Nat`
* [#4094](https://github.com/leanprover/lean4/pull/4094) swaps `Nat.zero_or` and `Nat.or_zero`.
* [#4098](https://github.com/leanprover/lean4/pull/4098) and [#4145](https://github.com/leanprover/lean4/pull/4145)
change the definition of `Nat.mod` so that `n % (m + n)` reduces when `n` is literal without relying on well-founded recursion,
which becomes irreducible by default in [#4061](https://github.com/leanprover/lean4/pull/4061).
* [#4188](https://github.com/leanprover/lean4/pull/4188) redefines `Nat.testBit` to be more performant.
* Theorems: [#4199](https://github.com/leanprover/lean4/pull/4199).
* `Array`
* [#4074](https://github.com/leanprover/lean4/pull/4074) improves the functional induction principle `Array.feraseIdx.induct`.
* `List`
* [#4172](https://github.com/leanprover/lean4/pull/4172) removes `@[simp]` from `List.length_pos`.
* `Option`
* [#4037](https://github.com/leanprover/lean4/pull/4037) adds theorems to simplify `Option`-valued dependent if-then-else.
* [#4314](https://github.com/leanprover/lean4/pull/4314) removes `@[simp]` from `Option.bind_eq_some`.
* `BitVec`
* Theorems: [#3920](https://github.com/leanprover/lean4/pull/3920), [#4095](https://github.com/leanprover/lean4/pull/4095),
[#4075](https://github.com/leanprover/lean4/pull/4075), [#4148](https://github.com/leanprover/lean4/pull/4148),
[#4165](https://github.com/leanprover/lean4/pull/4165), [#4178](https://github.com/leanprover/lean4/pull/4178),
[#4200](https://github.com/leanprover/lean4/pull/4200), [#4201](https://github.com/leanprover/lean4/pull/4201),
[#4298](https://github.com/leanprover/lean4/pull/4298), [#4299](https://github.com/leanprover/lean4/pull/4299),
[#4257](https://github.com/leanprover/lean4/pull/4257), [#4179](https://github.com/leanprover/lean4/pull/4179),
[#4321](https://github.com/leanprover/lean4/pull/4321), [#4187](https://github.com/leanprover/lean4/pull/4187).
* [#4193](https://github.com/leanprover/lean4/pull/4193) adds simprocs for reducing `x >>> i` and `x <<< i` where `i` is a bitvector literal.
* [#4194](https://github.com/leanprover/lean4/pull/4194) adds simprocs for reducing `(x <<< i) <<< j` and `(x >>> i) >>> j` where `i` and `j` are natural number literals.
* [#4229](https://github.com/leanprover/lean4/pull/4229) redefines `rotateLeft`/`rotateRight` to use modulo reduction of shift offset.
* [0d3051](https://github.com/leanprover/lean4/commit/0d30517dca094a07bcb462252f718e713b93ffba) makes `<num>#<term>` bitvector literal notation global.
* `Char`/`String`
* [#4143](https://github.com/leanprover/lean4/pull/4143) modifies `String.substrEq` to avoid linter warnings in downstream code.
* [#4233](https://github.com/leanprover/lean4/pull/4233) adds simprocs for `Char` and `String` inequalities.
* [#4348](https://github.com/leanprover/lean4/pull/4348) upstreams Mathlib lemmas.
* [#4354](https://github.com/leanprover/lean4/pull/4354) upstreams basic `String` lemmas.
* `HashMap`
* [#4248](https://github.com/leanprover/lean4/pull/4248) fixes implicitness of typeclass arguments in `HashMap.ofList`.
* `IO`
* [#4036](https://github.com/leanprover/lean4/pull/4036) adds `IO.Process.getCurrentDir` and `IO.Process.setCurrentDir` for adjusting the current process's working directory.
* **Cleanup:** [#4077](https://github.com/leanprover/lean4/pull/4077), [#4189](https://github.com/leanprover/lean4/pull/4189),
[#4304](https://github.com/leanprover/lean4/pull/4304).
* **Docs:** [#4001](https://github.com/leanprover/lean4/pull/4001), [#4166](https://github.com/leanprover/lean4/pull/4166),
[#4332](https://github.com/leanprover/lean4/pull/4332).
### Lean internals
* **Defeq and WHNF algorithms**
* [#4029](https://github.com/leanprover/lean4/pull/4029) remove unnecessary `checkpointDefEq`
* [#4206](https://github.com/leanprover/lean4/pull/4206) fixes `isReadOnlyOrSyntheticOpaque` to respect metavariable depth.
* [#4217](https://github.com/leanprover/lean4/pull/4217) fixes missing occurs check for delayed assignments.
* **Definition transparency**
* [#4052](https://github.com/leanprover/lean4/pull/4052) adds validation to application of `@[reducible]`/`@[semireducible]`/`@[irreducible]` attributes (with `local`/`scoped` modifiers as well).
Setting `set_option allowUnsafeReductibility true` turns this validation off.
* **Inductive types**
* [#3591](https://github.com/leanprover/lean4/pull/3591) fixes a bug where indices could be incorrectly promoted to parameters.
* [#3398](https://github.com/leanprover/lean4/pull/3398) fixes a bug in the injectivity theorem generator.
* [#4342](https://github.com/leanprover/lean4/pull/4342) fixes elaboration of mutual inductives with instance parameters.
* **Diagnostics and profiling**
* [#3986](https://github.com/leanprover/lean4/pull/3986) adds option `trace.profiler.useHeartbeats` to switch `trace.profiler.threshold` to being in terms of heartbeats instead of milliseconds.
* [#4082](https://github.com/leanprover/lean4/pull/4082) makes `set_option diagnostics true` report kernel diagnostic information.
* **Typeclass resolution**
* [#4119](https://github.com/leanprover/lean4/pull/4119) fixes multiple issues with TC caching interacting with `synthPendingDepth`, adds `maxSynthPendingDepth` option with default value `1`.
* [#4210](https://github.com/leanprover/lean4/pull/4210) ensures local instance cache does not contain multiple copies of the same instance.
* [#4216](https://github.com/leanprover/lean4/pull/4216) fix handling of metavariables, to avoid needing to set the option `backward.synthInstance.canonInstances` to `false`.
* **Other fixes or improvements**
* [#4080](https://github.com/leanprover/lean4/pull/4080) fixes propagation of state for `Lean.Elab.Command.liftCoreM` and `Lean.Elab.Command.liftTermElabM`.
* [#3944](https://github.com/leanprover/lean4/pull/3944) makes the `Repr` deriving handler be consistent between `structure` and `inductive` for how types and proofs are erased.
* [#4113](https://github.com/leanprover/lean4/pull/4113) propagates `maxHeartbeats` to kernel to control "(kernel) deterministic timeout" error.
* [#4125](https://github.com/leanprover/lean4/pull/4125) reverts [#3970](https://github.com/leanprover/lean4/pull/3970) (monadic generalization of `FindExpr`).
* [#4128](https://github.com/leanprover/lean4/pull/4128) catches stack overflow in auto-bound implicits feature.
* [#4129](https://github.com/leanprover/lean4/pull/4129) adds `tryCatchRuntimeEx` combinator to replace `catchRuntimeEx` reader state.
* [#4155](https://github.com/leanprover/lean4/pull/4155) simplifies the expression canonicalizer.
* [#4151](https://github.com/leanprover/lean4/pull/4151) and [#4369](https://github.com/leanprover/lean4/pull/4369)
add many missing trace classes.
* [#4185](https://github.com/leanprover/lean4/pull/4185) makes congruence theorem generators clean up type annotations of argument types.
* [#4192](https://github.com/leanprover/lean4/pull/4192) fixes restoration of infotrees when auto-bound implicit feature is activated,
fixing a pretty printing error in hovers and strengthening the unused variable linter.
* [dfb496](https://github.com/leanprover/lean4/commit/dfb496a27123c3864571aec72f6278e2dad1cecf) fixes `declareBuiltin` to allow it to be called multiple times per declaration.
* Cleanup: [#4112](https://github.com/leanprover/lean4/pull/4112), [#4126](https://github.com/leanprover/lean4/pull/4126), [#4091](https://github.com/leanprover/lean4/pull/4091), [#4139](https://github.com/leanprover/lean4/pull/4139), [#4153](https://github.com/leanprover/lean4/pull/4153).
* Tests: [030406](https://github.com/leanprover/lean4/commit/03040618b8f9b35b7b757858483e57340900cdc4), [#4133](https://github.com/leanprover/lean4/pull/4133).
### Compiler, runtime, and FFI
* [#4100](https://github.com/leanprover/lean4/pull/4100) improves reset/reuse algorithm; it now runs a second pass relaxing the constraint that reused memory cells must only be for the exact same constructor.
* [#2903](https://github.com/leanprover/lean4/pull/2903) fixes segfault in old compiler from mishandling `noConfusion` applications.
* [#4311](https://github.com/leanprover/lean4/pull/4311) fixes bug in constant folding.
* [#3915](https://github.com/leanprover/lean4/pull/3915) documents the runtime memory layout for inductive types.
### Lake
* [#4057](https://github.com/leanprover/lean4/pull/4057) adds support for docstrings on `require` commands.
* [#4088](https://github.com/leanprover/lean4/pull/4088) improves hovers for `family_def` and `library_data` commands.
* [#4147](https://github.com/leanprover/lean4/pull/4147) adds default `README.md` to package templates
* [#4261](https://github.com/leanprover/lean4/pull/4261) extends `lake test` help page, adds help page for `lake check-test`,
adds `lake lint` and tag `@[lint_driver]`, adds support for specifying test and lint drivers from dependencies,
adds `testDriverArgs` and `lintDriverArgs` options, adds support for library test drivers,
makes `lake check-test` and `lake check-lint` only load the package without dependencies.
* [#4270](https://github.com/leanprover/lean4/pull/4270) adds `lake pack` and `lake unpack` for packing and unpacking Lake build artifacts from an archive.
* [#4083](https://github.com/leanprover/lean4/pull/4083)
Switches the manifest format to use `major.minor.patch` semantic
versions. Major version increments indicate breaking changes (e.g., new
required fields and semantic changes to existing fields). Minor version
increments (after `0.x`) indicate backwards-compatible extensions (e.g.,
adding optional fields, removing fields). This change is backwards
compatible. Lake will still successfully read old manifests with numeric
versions. It will treat the numeric version `N` as semantic version
`0.N.0`. Lake will also accept manifest versions with `-` suffixes
(e.g., `x.y.z-foo`) and then ignore the suffix.
* [#4273](https://github.com/leanprover/lean4/pull/4273) adds a lift from `JobM` to `FetchM` for backwards compatibility reasons.
* [#4351](https://github.com/leanprover/lean4/pull/4351) fixes `LogIO`-to-`CliM`-lifting performance issues.
* [#4343](https://github.com/leanprover/lean4/pull/4343) make Lake store the dependency trace for a build in
the cached build long and then verifies that it matches the trace of the current build before replaying the log.
* [#4402](https://github.com/leanprover/lean4/pull/4402) moves the cached log into the trace file (no more `.log.json`).
This means logs are no longer cached on fatal errors and this ensures that an out-of-date log is not associated with an up-to-date trace.
Separately, `.hash` file generation was changed to be more reliable as well.
The `.hash` files are deleted as part of the build and always regenerate with `--rehash`.
* **Other fixes or improvements**
* [#4056](https://github.com/leanprover/lean4/pull/4056) cleans up tests
* [#4244](https://github.com/leanprover/lean4/pull/4244) fixes `noRelease` test when Lean repo is tagged
* [#4346](https://github.com/leanprover/lean4/pull/4346) improves `tests/serve`
* [#4356](https://github.com/leanprover/lean4/pull/4356) adds build log path to the warning for a missing or invalid build log.
### DevOps
* [#3984](https://github.com/leanprover/lean4/pull/3984) adds a script (`script/rebase-stage0.sh`) for `git rebase -i` that automatically updates each stage0.
* [#4108](https://github.com/leanprover/lean4/pull/4108) finishes renamings from transition to Std to Batteries.
* [#4109](https://github.com/leanprover/lean4/pull/4109) adjusts the Github bug template to mention testing using [live.lean-lang.org](https://live.lean-lang.org).
* [#4136](https://github.com/leanprover/lean4/pull/4136) makes CI rerun only when `full-ci` label is added or removed.
* [#4175](https://github.com/leanprover/lean4/pull/4175) and [72b345](https://github.com/leanprover/lean4/commit/72b345c621a9a06d3a5a656da2b793a5eea5f168)
switch to using `#guard_msgs` to run tests as much as possible.
* [#3125](https://github.com/leanprover/lean4/pull/3125) explains the Lean4 `pygments` lexer.
* [#4247](https://github.com/leanprover/lean4/pull/4247) sets up a procedure for preparing release notes.
* [#4032](https://github.com/leanprover/lean4/pull/4032) modernizes build instructions and workflows.
* [#4255](https://github.com/leanprover/lean4/pull/4255) moves some expensive checks from merge queue to releases.
* [#4265](https://github.com/leanprover/lean4/pull/4265) adds aarch64 macOS as native compilation target for CI.
* [f05a82](https://github.com/leanprover/lean4/commit/f05a82799a01569edeb5e2594cd7d56282320f9e) restores macOS aarch64 install suffix in CI
* [#4317](https://github.com/leanprover/lean4/pull/4317) updates build instructions for macOS.
* [#4333](https://github.com/leanprover/lean4/pull/4333) adjusts workflow to update Batteries in manifest when creating `lean-pr-testing-NNNN` Mathlib branches.
* [#4355](https://github.com/leanprover/lean4/pull/4355) simplifies `lean4checker` step of release checklist.
* [#4361](https://github.com/leanprover/lean4/pull/4361) adds installing elan to `pr-release` CI step.
### Breaking changes
While most changes could be considered to be a breaking change, this section makes special note of API changes.
* `Nat.zero_or` and `Nat.or_zero` have been swapped ([#4094](https://github.com/leanprover/lean4/pull/4094)).
* `IsLawfulSingleton` is now `LawfulSingleton` ([#4350](https://github.com/leanprover/lean4/pull/4350)).
* `BitVec.rotateLeft` and `BitVec.rotateRight` now take the shift modulo the bitwidth ([#4229](https://github.com/leanprover/lean4/pull/4229)).
* These are no longer simp lemmas:
`List.length_pos` ([#4172](https://github.com/leanprover/lean4/pull/4172)),
`Option.bind_eq_some` ([#4314](https://github.com/leanprover/lean4/pull/4314)).
* Types in `let` and `have` (both the expressions and tactics) may fail to elaborate due to new restrictions on what sorts of elaboration problems may be postponed ([#4096](https://github.com/leanprover/lean4/pull/4096)).
In particular, tactics embedded in the type will no longer make use of the type of `value` in expressions such as `let x : type := value; body`.
* Now functions defined by well-founded recursion are marked with `@[irreducible]` by default ([#4061](https://github.com/leanprover/lean4/pull/4061)).
Existing proofs that hold by definitional equality (e.g. `rfl`) can be
rewritten to explictly unfold the function definition (using `simp`,
`unfold`, `rw`), or the recursive function can be temporarily made
semireducible (using `unseal f in` before the command), or the function
definition itself can be marked as `@[semireducible]` to get the previous
behavior.
* Due to [#3929](https://github.com/leanprover/lean4/pull/3929):
* The `MessageData.ofPPFormat` constructor has been removed.
Its functionality has been split into two:
- for lazy structured messages, please use `MessageData.lazy`;
- for embedding `Format` or `FormatWithInfos`, use `MessageData.ofFormatWithInfos`.
An example migration can be found in [#3929](https://github.com/leanprover/lean4/pull/3929/files#diff-5910592ab7452a0e1b2616c62d22202d2291a9ebb463145f198685aed6299867L109).
* The `MessageData.ofFormat` constructor has been turned into a function.
If you need to inspect `MessageData`, you can pattern-match on `MessageData.ofFormatWithInfos`.
v4.8.0
---------

View File

@@ -5,7 +5,8 @@ See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example.
- One week before the planned release, ensure that someone has written the first draft of the release blog post
- One week before the planned release, ensure that (1) someone has written the release notes and (2) someone has written the first draft of the release blog post.
If there is any material in `./releases_drafts/`, then the release notes are not done. (See the section "Writing the release notes".)
- `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.)
- `git pull`
@@ -13,13 +14,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)`
- (both of these should already be in place from the release candidates)
- It is possible that the `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Run `git diff master RELEASES.md`.
- You should expect to see additons on `master` in the `v4.7.0-rc1` section; ignore these.
(i.e. the new release notes for the upcoming release candidate).
- Reconcile discrepancies in the `v4.6.0` section,
usually via copy and paste and a commit to `releases/v4.6.0`.
- `git tag v4.6.0`
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
- Now wait, while CI runs.
@@ -30,8 +24,9 @@ We'll use `v4.6.0` as the intended release version as a running example.
you may want to start on the release candidate checklist now.
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
- Edit the release notes on Github to select the "Set as the latest release".
- Copy and paste the Github release notes from the previous releases candidate for this version
(e.g. `v4.6.0-rc1`), and quickly sanity check.
- Follow the instructions in creating a release candidate for the "GitHub release notes" step,
now that we have a written `RELEASES.md` section.
Do a quick sanity check.
- Next, we will move a curated list of downstream repos to the latest stable release.
- For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
@@ -94,6 +89,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest
- Create and push the tag
- Merge the tag into `stable`
- The `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
and commit this to `master`.
- Merge the release announcement PR for the Lean website - it will be deployed automatically
- Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
@@ -104,7 +103,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
## Optimistic(?) time estimates:
- Initial checks and push the tag: 30 minutes.
- Note that if `RELEASES.md` has discrepancies this could take longer!
- Waiting for the release: 60 minutes.
- Fixing release notes: 10 minutes.
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
@@ -131,29 +129,26 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0
```
- In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
- Our current goal is to have written release notes only about major language features or breaking changes,
and to rely on automatically generated release notes for bugfixes and minor changes.
- Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
- Commit your changes to `RELEASES.md`, and push.
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.`
- We will rely on automatically generated release notes for release candidates,
and the written release notes will be used for stable versions only.
It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion.
- In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
- Commit your changes to `src/CMakeLists.txt`, and push.
- `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1`
- Ping the FRO Zulip that release notes need to be written. The release notes do not block completing the rest of this checklist.
- Now wait, while CI runs.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
- This step can take up to an hour.
- Once the release appears at https://github.com/leanprover/lean4/releases/
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/
- Edit the release notes on Github to select the "Set as a pre-release box".
- Copy the section of `RELEASES.md` for this version into the Github release notes.
- Use the title "Changes since v4.6.0 (from RELEASES.md)"
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
- This will add a list of all the commits since the last stable version.
- If release notes have been written already, copy the section of `RELEASES.md` for this version into the Github release notes
and use the title "Changes since v4.6.0 (from RELEASES.md)".
- Otherwise, in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
This will add a list of all the commits since the last stable version.
- Delete anything already mentioned in the hand-written release notes above.
- Delete "update stage0" commits, and anything with a completely inscrutable commit message.
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
@@ -179,6 +174,9 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- We do this for the same list of repositories as for stable releases, see above.
As above, there are dependencies between these, and so the process above is iterative.
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
for the next development cycle.
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
@@ -189,8 +187,17 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
Please also make sure that whoever is handling social media knows the release is out.
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
- Removes `(in development)` from the section heading in `RELEASES.md` for `v4.7.0`,
and creates a new `v4.8.0 (in development)` section heading.
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
```
Release candidate, release notes will be copied from `branch releases/v4.7.0` once completed.
```
and inserts the following section before that section:
```
v4.8.0
----------
Development in progress.
```
- Removes all the entries from the `./releases_drafts/` folder.
## Time estimates:
Slightly longer than the corresponding steps for a stable release.
@@ -224,3 +231,18 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
* It is always okay to merge in the following directions:
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
Please remember to push any merges you make to intermediate steps!
# Writing the release notes
We are currently trying a system where release notes are compiled all at once from someone looking through the commit history.
The exact steps are a work in progress.
Here is the general idea:
* The work is done right on the `releases/v4.6.0` branch sometime after it is created but before the stable release is made.
The release notes for `v4.6.0` will be copied to `master`.
* There can be material for release notes entries in commit messages.
* There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
See `./releases_drafts/README.md` for more information.
* The release notes should be written from a downstream expert user's point of view.
This section will be updated when the next release notes are written (for `v4.10.0`).

View File

@@ -35,27 +35,28 @@
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; inherit nix lean4-mode; };
devShellWithDist = pkgsDist: pkgs.mkShell.override {
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp ccache
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
# TODO: only add when proven to not affect the flakification
#pkgs.python3
tree # for CI
];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
# more convenient `ctest` output
CTEST_OUTPUT_ON_FAILURE = 1;
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
GMP = pkgsDist.gmp.override { withStatic = true; };
GLIBC = pkgsDist.glibc;
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
GDB = pkgsDist.gdb;
});
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp ccache
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
# TODO: only add when proven to not affect the flakification
#pkgs.python3
tree # for CI
];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
# more convenient `ctest` output
CTEST_OUTPUT_ON_FAILURE = 1;
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
GMP = pkgsDist.gmp.override { withStatic = true; };
GLIBC = pkgsDist.glibc;
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
GDB = pkgsDist.gdb;
});
in {
packages = lean-packages // rec {
debug = lean-packages.override { debug = true; };

View File

@@ -87,7 +87,8 @@ rec {
leanFlags = [ "-DwarningAsError=true" ];
} // args);
Init' = build { name = "Init"; deps = []; };
Lean' = build { name = "Lean"; deps = [ Init' ]; };
Std' = build { name = "Std"; deps = [ Init' ]; };
Lean' = build { name = "Lean"; deps = [ Std' ]; };
attachSharedLib = sharedLib: pkg: pkg // {
inherit sharedLib;
mods = mapAttrs (_: m: m // { inherit sharedLib; propagatedLoadDynlibs = []; }) pkg.mods;
@@ -95,7 +96,8 @@ rec {
in (all: all // all.lean) rec {
inherit (Lean) emacs-dev emacs-package vscode-dev vscode-package;
Init = attachSharedLib leanshared Init';
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Init ]; };
Std = attachSharedLib leanshared Std' // { allExternalDeps = [ Init ]; };
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Std ]; };
Lake = build {
name = "Lake";
src = src + "/src/lake";
@@ -109,23 +111,24 @@ rec {
linkFlags = lib.optional stdenv.isLinux "-rdynamic";
src = src + "/src/lake";
};
stdlib = [ Init Lean Lake ];
stdlib = [ Init Std Lean Lake ];
modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); };
depRoots = symlinkJoin { name = "depRoots"; paths = map (l: l.depRoots) stdlib; };
iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; };
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; };
stdlibLinkFlags = "-L${Init.staticLib} -L${Lean.staticLib} -L${Lake.staticLib} -L${leancpp}/lib/lean";
stdlibLinkFlags = "${lib.concatMapStringsSep " " (l: "-L${l.staticLib}") stdlib} -L${leancpp}/lib/lean";
libInit_shared = runCommand "libInit_shared" { buildInputs = [ stdenv.cc ]; libName = "libInit_shared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
-Wl,--whole-archive -lInit ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
touch empty.c
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
'';
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
${libInit_shared}/* -Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
${if stdenv.isDarwin
then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Std.staticLib}/libStd.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
else "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} \
-lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
'';
@@ -151,11 +154,9 @@ rec {
'';
meta.mainProgram = "lean";
};
cacheRoots = linkFarmFromDrvs "cacheRoots" [
cacheRoots = linkFarmFromDrvs "cacheRoots" ([
stage0 lean leanc lean-all iTree modDepsFiles depRoots Leanc.src
# .o files are not a runtime dependency on macOS because of lack of thin archives
Lean.oTree Lake.oTree
];
] ++ map (lib: lib.oTree) stdlib);
test = buildCMake {
name = "lean-test-${desc}";
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
@@ -178,7 +179,7 @@ rec {
'';
};
update-stage0 =
let cTree = symlinkJoin { name = "cs"; paths = [ Init.cTree Lean.cTree Lake.cTree ]; }; in
let cTree = symlinkJoin { name = "cs"; paths = map (lib: lib.cTree) stdlib; }; in
writeShellScriptBin "update-stage0" ''
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"}
'';

View File

@@ -5,7 +5,7 @@ let lean-final' = lean-final; in
lib.makeOverridable (
{ name, src, fullSrc ? src, srcPrefix ? "", srcPath ? "$PWD/${srcPrefix}",
# Lean dependencies. Each entry should be an output of buildLeanPackage.
deps ? [ lean.Lean ],
deps ? [ lean.Init lean.Std lean.Lean ],
# Static library dependencies. Each derivation `static` should contain a static library in the directory `${static}`.
staticLibDeps ? [],
# Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved.
@@ -224,7 +224,8 @@ with builtins; let
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;
objects = mapAttrs (_: m: m.obj) mods';
staticLib = runCommand "${name}-lib" { buildInputs = [ stdenv.cc.bintools.bintools ]; } ''
bintools = if stdenv.isDarwin then darwin.cctools else stdenv.cc.bintools.bintools;
staticLib = runCommand "${name}-lib" { buildInputs = [ bintools ]; } ''
mkdir -p $out
ar Trcs $out/lib${libName}.a ${lib.concatStringsSep " " (map (drv: "${drv}/${drv.oPath}") (attrValues objects))};
'';
@@ -249,7 +250,7 @@ in rec {
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.libInit_shared}/* ${lean-final.leanshared}/*";
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*";
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
mkdir -p $out/bin
leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \

View File

@@ -9,7 +9,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 10)
set(LEAN_VERSION_MINOR 11)
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'")
@@ -300,11 +300,11 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lleanrt")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lnodefs.js -lleanrt")
else()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -lStd -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
endif()
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
@@ -510,15 +510,15 @@ file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
# set up libInit_shared only on Windows; see also stdlib.make.in
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libInit.a.export ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libInit.a.export ${CMAKE_BINARY_DIR}/lib/temp/libStd.a.export ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libStd.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLean.a.export -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
else()
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
@@ -540,7 +540,7 @@ add_custom_target(make_stdlib ALL
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
# for a parallelized nested build, but CMake doesn't let us do that.
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Lean
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean
VERBATIM)
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode

View File

@@ -131,7 +131,7 @@ protected def adapt {ε' α : Type u} (f : ε → ε') : ExceptT ε m α → Exc
end ExceptT
@[always_inline]
instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [Monad m] [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where
instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where
throw e := ExceptT.mk <| throwThe ε₁ e
tryCatch x handle := ExceptT.mk <| tryCatchThe ε₁ x handle

View File

@@ -9,7 +9,7 @@ import Init.Meta
open Function
@[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
@[simp] theorem monadLift_self {m : Type u Type v} (x : m α) : monadLift x = x :=
rfl
/--

View File

@@ -14,7 +14,7 @@ open Function
namespace ExceptT
theorem ext [Monad m] {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
simp [run] at h
assumption
@@ -50,7 +50,7 @@ theorem run_bind [Monad m] (x : ExceptT ε m α)
protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
protected theorem bind_pure_comp [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
protected theorem bind_pure_comp [Monad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
@@ -188,23 +188,23 @@ theorem ext {x y : StateT σ m α} (h : ∀ s, x.run s = y.run s) : x = y :=
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[simp] theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ)
: (monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ) :
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _
simp
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
@[simp] theorem run_seqRight [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _
simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _
simp

View File

@@ -67,7 +67,7 @@ instance : MonadExceptOf Unit (OptionT m) where
throw := fun _ => OptionT.fail
tryCatch := OptionT.tryCatch
instance (ε : Type u) [Monad m] [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
instance (ε : Type u) [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
throw e := OptionT.mk <| throwThe ε e
tryCatch x handle := OptionT.mk <| tryCatchThe ε x handle

View File

@@ -32,7 +32,7 @@ instance : MonadControl m (ReaderT ρ m) where
restoreM x _ := x
@[always_inline]
instance ReaderT.tryFinally [MonadFinally m] [Monad m] : MonadFinally (ReaderT ρ m) where
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id

View File

@@ -87,7 +87,7 @@ protected def lift {α : Type u} (t : m α) : StateT σ m α :=
instance : MonadLift m (StateT σ m) := StateT.lift
@[always_inline]
instance (σ m) [Monad m] : MonadFunctor m (StateT σ m) := fun f x s => f (x s)
instance (σ m) : MonadFunctor m (StateT σ m) := fun f x s => f (x s)
@[always_inline]
instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {

View File

@@ -14,16 +14,18 @@ def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type
namespace StateCpsT
variable {α σ : Type u} {m : Type u Type v}
@[always_inline, inline]
def runK {α σ : Type u} {m : Type u Type v} (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k
@[always_inline, inline]
def run {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s))
@[always_inline, inline]
def run' {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a)
@[always_inline]
@@ -48,29 +50,29 @@ protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
instance [Monad m] : MonadLift m (StateCpsT σ m) where
monadLift := StateCpsT.lift
@[simp] theorem runK_pure {m : Type u Type v} (a : α) (s : σ) (k : α σ m β) : (pure a : StateCpsT σ m α).runK s k = k a s := rfl
@[simp] theorem runK_pure (a : α) (s : σ) (k : α σ m β) : (pure a : StateCpsT σ m α).runK s k = k a s := rfl
@[simp] theorem runK_get {m : Type u Type v} (s : σ) (k : σ σ m β) : (get : StateCpsT σ m σ).runK s k = k s s := rfl
@[simp] theorem runK_get (s : σ) (k : σ σ m β) : (get : StateCpsT σ m σ).runK s k = k s s := rfl
@[simp] theorem runK_set {m : Type u Type v} (s s' : σ) (k : PUnit σ m β) : (set s' : StateCpsT σ m PUnit).runK s k = k s' := rfl
@[simp] theorem runK_set (s s' : σ) (k : PUnit σ m β) : (set s' : StateCpsT σ m PUnit).runK s k = k s' := rfl
@[simp] theorem runK_modify {m : Type u Type v} (f : σ σ) (s : σ) (k : PUnit σ m β) : (modify f : StateCpsT σ m PUnit).runK s k = k (f s) := rfl
@[simp] theorem runK_modify (f : σ σ) (s : σ) (k : PUnit σ m β) : (modify f : StateCpsT σ m PUnit).runK s k = k (f s) := rfl
@[simp] theorem runK_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) (k : α σ m β) : (StateCpsT.lift x : StateCpsT σ m α).runK s k = x >>= (k . s) := rfl
@[simp] theorem runK_lift [Monad m] (x : m α) (s : σ) (k : α σ m β) : (StateCpsT.lift x : StateCpsT σ m α).runK s k = x >>= (k . s) := rfl
@[simp] theorem runK_monadLift {σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) (k : α σ m β)
@[simp] theorem runK_monadLift [Monad m] [MonadLiftT n m] (x : n α) (s : σ) (k : α σ m β)
: (monadLift x : StateCpsT σ m α).runK s k = (monadLift x : m α) >>= (k . s) := rfl
@[simp] theorem runK_bind_pure {α σ : Type u} [Monad m] (a : α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) : (pure a >>= f).runK s k = (f a).runK s k := rfl
@[simp] theorem runK_bind_pure (a : α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) : (pure a >>= f).runK s k = (f a).runK s k := rfl
@[simp] theorem runK_bind_lift {α σ : Type u} [Monad m] (x : m α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ)
@[simp] theorem runK_bind_lift [Monad m] (x : m α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ)
: (StateCpsT.lift x >>= f).runK s k = x >>= fun a => (f a).runK s k := rfl
@[simp] theorem runK_bind_get {σ : Type u} [Monad m] (f : σ StateCpsT σ m β) (s : σ) (k : β σ m γ) : (get >>= f).runK s k = (f s).runK s k := rfl
@[simp] theorem runK_bind_get (f : σ StateCpsT σ m β) (s : σ) (k : β σ m γ) : (get >>= f).runK s k = (f s).runK s k := rfl
@[simp] theorem runK_bind_set {σ : Type u} [Monad m] (f : PUnit StateCpsT σ m β) (s s' : σ) (k : β σ m γ) : (set s' >>= f).runK s k = (f ).runK s' k := rfl
@[simp] theorem runK_bind_set (f : PUnit StateCpsT σ m β) (s s' : σ) (k : β σ m γ) : (set s' >>= f).runK s k = (f ).runK s' k := rfl
@[simp] theorem runK_bind_modify {σ : Type u} [Monad m] (f : σ σ) (g : PUnit StateCpsT σ m β) (s : σ) (k : β σ m γ) : (modify f >>= g).runK s k = (g ).runK (f s) k := rfl
@[simp] theorem runK_bind_modify (f : σ σ) (g : PUnit StateCpsT σ m β) (s : σ) (k : β σ m γ) : (modify f >>= g).runK s k = (g ).runK (f s) k := rfl
@[simp] theorem run_eq [Monad m] (x : StateCpsT σ m α) (s : σ) : x.run s = x.runK s (fun a s => pure (a, s)) := rfl

View File

@@ -34,22 +34,22 @@ protected def lift (x : m α) : StateRefT' ω σ m α :=
instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (ReaderT _ _))
instance : MonadLift m (StateRefT' ω σ m) := StateRefT'.lift
instance (σ m) [Monad m] : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
@[inline]
protected def get [Monad m] [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
fun ref => ref.get
@[inline]
protected def set [Monad m] [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
fun ref => ref.set s
@[inline]
protected def modifyGet [Monad m] [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
fun ref => ref.modifyGet f
instance [MonadLiftT (ST ω) m] [Monad m] : MonadStateOf σ (StateRefT' ω σ m) where
instance [MonadLiftT (ST ω) m] : MonadStateOf σ (StateRefT' ω σ m) where
get := StateRefT'.get
set := StateRefT'.set
modifyGet := StateRefT'.modifyGet
@@ -64,5 +64,5 @@ end StateRefT'
instance (ω σ : Type) (m : Type Type) : MonadControl m (StateRefT' ω σ m) :=
inferInstanceAs (MonadControl m (ReaderT _ _))
instance {m : Type Type} {ω σ : Type} [MonadFinally m] [Monad m] : MonadFinally (StateRefT' ω σ m) :=
instance {m : Type Type} {ω σ : Type} [MonadFinally m] : MonadFinally (StateRefT' ω σ m) :=
inferInstanceAs (MonadFinally (ReaderT _ _))

View File

@@ -642,7 +642,7 @@ instance : LawfulBEq String := inferInstance
/-! # Logical connectives and equality -/
@[inherit_doc True.intro] def trivial : True :=
@[inherit_doc True.intro] theorem trivial : True :=
theorem mt {a b : Prop} (h₁ : a b) (h₂ : ¬b) : ¬a :=
fun ha => h₂ (h₁ ha)
@@ -1089,15 +1089,18 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
fun a₁ a₂ => r (f a₁) (f a₂)
/--
The transitive closure `r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `r a z` if and only if there exists a sequence
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
-/
inductive TC {α : Sort u} (r : α α Prop) : α α Prop where
/-- If `r a b` then `r a b`. This is the base case of the transitive closure. -/
| base : a b, r a b TC r a b
inductive Relation.TransGen {α : Sort u} (r : α α Prop) : α α Prop
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/
| single {a b} : r a b TransGen r a b
/-- The transitive closure is transitive. -/
| trans : a b c, TC r a b TC r b c TC r a c
| tail {a b c} : TransGen r a b r b c TransGen r a c
/-- Deprecated synonym for `Relation.TransGen`. -/
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
/-! # Subtype -/
@@ -1173,7 +1176,7 @@ def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
s.1 < t.1 (s.1 = t.1 s.2 < t.2)
instance Prod.lexLtDec
[LT α] [LT β] [DecidableEq α] [DecidableEq β]
[LT α] [LT β] [DecidableEq α]
[(a b : α) Decidable (a < b)] [(a b : β) Decidable (a < b)]
: (s t : α × β) Decidable (Prod.lexLt s t) :=
fun _ _ => inferInstanceAs (Decidable (_ _))
@@ -1191,6 +1194,11 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
(f : α₁ α₂) (g : β₁ β₂) : α₁ × β₁ α₂ × β₂
| (a, b) => (f a, g b)
@[simp] theorem Prod.map_apply (f : α β) (g : γ δ) (x) (y) :
Prod.map f g (x, y) = (f x, g y) := rfl
@[simp] theorem Prod.map_fst (f : α β) (g : γ δ) (x) : (Prod.map f g x).1 = f x.1 := rfl
@[simp] theorem Prod.map_snd (f : α β) (g : γ δ) (x) : (Prod.map f g x).2 = g x.2 := rfl
/-! # Dependent products -/
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
@@ -1357,6 +1365,9 @@ theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_fa
theorem of_iff_true (h : a True) : a := h.mpr trivial
theorem iff_true_intro (h : a) : a True := iff_of_true h trivial
theorem eq_iff_true_of_subsingleton [Subsingleton α] (x y : α) : x = y True :=
iff_true_intro (Subsingleton.elim ..)
theorem not_of_iff_false : (p False) ¬p := Iff.mp
theorem iff_false_intro (h : ¬a) : a False := iff_of_false h id
@@ -1862,7 +1873,7 @@ instance : Subsingleton (Squash α) where
/--
`Antisymm (·≤·)` says that `(·≤·)` is antisymmetric, that is, `a ≤ b → b ≤ a → a = b`.
-/
class Antisymm {α : Sort u} (r : α α Prop) where
class Antisymm {α : Sort u} (r : α α Prop) : Prop where
/-- An antisymmetric relation `(·≤·)` satisfies `a ≤ b → b ≤ a → a = b`. -/
antisymm {a b : α} : r a b r b a a = b

View File

@@ -35,3 +35,4 @@ import Init.Data.Queue
import Init.Data.Channel
import Init.Data.Cast
import Init.Data.Sum
import Init.Data.BEq

View File

@@ -10,5 +10,7 @@ import Init.Data.Array.BinSearch
import Init.Data.Array.InsertionSort
import Init.Data.Array.DecidableEq
import Init.Data.Array.Mem
import Init.Data.Array.Attach
import Init.Data.Array.BasicAux
import Init.Data.Array.Lemmas
import Init.Data.Array.TakeDrop

View File

@@ -0,0 +1,29 @@
/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner, Mario Carneiro
-/
prelude
import Init.Data.Array.Mem
import Init.Data.List.Attach
namespace Array
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`Array {x // P x}` is the same as the input `Array α`.
-/
@[inline] private unsafe def attachWithImpl
(xs : Array α) (P : α Prop) (_ : x xs, P x) : Array {x // P x} := unsafeCast xs
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
with the same elements but in the type `{x // P x}`. -/
@[implemented_by attachWithImpl] def attachWith
(xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} :=
xs.data.attachWith P fun x h => H x (Array.Mem.mk h)
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
with the same elements but in the type `{x // x ∈ xs}`. -/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
end Array

View File

@@ -60,8 +60,6 @@ def uget (a : @& Array α) (i : USize) (h : i.toNat < a.size) : α :=
instance : GetElem (Array α) USize α fun xs i => i.toNat < xs.size where
getElem xs i h := xs.uget i h
instance : LawfulGetElem (Array α) USize α fun xs i => i.toNat < xs.size where
def back [Inhabited α] (a : Array α) : α :=
a.get! (a.size - 1)

View File

@@ -51,7 +51,7 @@ theorem foldlM_eq_foldlM_data.aux [Monad m]
simp [foldlM_eq_foldlM_data.aux f arr i (j+1) H]
rw (config := {occs := .pos [2]}) [ List.get_drop_eq_drop _ _ _]
rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
theorem foldlM_eq_foldlM_data [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
@@ -141,7 +141,7 @@ where
· rw [ List.get_drop_eq_drop _ i _]
simp only [aux (i + 1), map_eq_pure_bind, data_length, List.foldlM_cons, bind_assoc, pure_bind]
rfl
· rw [List.drop_length_le (Nat.ge_of_not_lt _)]; rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
termination_by arr.size - i
decreasing_by decreasing_trivial_pre_omega
@@ -220,7 +220,7 @@ theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size ≤ i) : a[i]? = no
theorem getD_get? (a : Array α) (i : Nat) (d : α) :
Option.getD a[i]? d = if p : i < a.size then a[i]'p else d := by
if h : i < a.size then
simp [setD, h, getElem?]
simp [setD, h, getElem?_def]
else
have p : i a.size := Nat.le_of_not_gt h
simp [setD, getElem?_len_le _ p, h]
@@ -383,18 +383,18 @@ theorem get?_push {a : Array α} : (a.push x)[i]? = if i = a.size then some x el
| Or.inl g =>
have h1 : i < a.size + 1 := by omega
have h2 : i a.size := by omega
simp [getElem?, size_push, g, h1, h2, get_push_lt]
simp [getElem?_def, size_push, g, h1, h2, get_push_lt]
| Or.inr (Or.inl heq) =>
simp [heq, getElem?_pos, get_push_eq]
| Or.inr (Or.inr g) =>
simp only [getElem?, size_push]
simp only [getElem?_def, size_push]
have h1 : ¬ (i < a.size) := by omega
have h2 : ¬ (i < a.size + 1) := by omega
have h3 : i a.size := by omega
simp [h1, h2, h3]
@[simp] theorem get?_size {a : Array α} : a[a.size]? = none := by
simp only [getElem?, Nat.lt_irrefl, dite_false]
simp only [getElem?_def, Nat.lt_irrefl, dite_false]
@[simp] theorem data_set (a : Array α) (i v) : (a.set i v).data = a.data.set i.1 v := rfl

View File

@@ -23,7 +23,7 @@ theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a ∈ as) : sizeOf a <
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
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)

View File

@@ -47,8 +47,6 @@ def get (s : Subarray α) (i : Fin s.size) : α :=
instance : GetElem (Subarray α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem (Subarray α) Nat α fun xs i => i < xs.size where
@[inline] def getD (s : Subarray α) (i : Nat) (v₀ : α) : α :=
if h : i < s.size then s.get i, h else v₀

View File

@@ -0,0 +1,17 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.TakeDrop
namespace Array
theorem exists_of_uset (self : Array α) (i d h) :
l₁ l₂, self.data = l₁ ++ self[i] :: l₂ List.length l₁ = i.toNat
(self.uset i d h).data = l₁ ++ d :: l₂ := by
simpa [Array.getElem_eq_data_getElem] using List.exists_of_set _
end Array

60
src/Init/Data/BEq.lean Normal file
View File

@@ -0,0 +1,60 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Markus Himmel
-/
prelude
import Init.Data.Bool
set_option linter.missingDocs true
/-- `PartialEquivBEq α` says that the `BEq` implementation is a
partial equivalence relation, that is:
* it is symmetric: `a == b → b == a`
* it is transitive: `a == b → b == c → a == c`.
-/
class PartialEquivBEq (α) [BEq α] : Prop where
/-- Symmetry for `BEq`. If `a == b` then `b == a`. -/
symm : (a : α) == b b == a
/-- Transitivity for `BEq`. If `a == b` and `b == c` then `a == c`. -/
trans : (a : α) == b b == c a == c
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- Reflexivity for `BEq`. -/
refl : (a : α) == a
/-- `EquivBEq` says that the `BEq` implementation is an equivalence relation. -/
class EquivBEq (α) [BEq α] extends PartialEquivBEq α, ReflBEq α : Prop
@[simp]
theorem BEq.refl [BEq α] [ReflBEq α] {a : α} : a == a :=
ReflBEq.refl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.refl
theorem BEq.symm [BEq α] [PartialEquivBEq α] {a b : α} : a == b b == a :=
PartialEquivBEq.symm
theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
Bool.eq_iff_iff.2 BEq.symm, BEq.symm
theorem BEq.symm_false [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = false (b == a) = false :=
BEq.comm (α := α) id
theorem BEq.trans [BEq α] [PartialEquivBEq α] {a b c : α} : a == b b == c a == c :=
PartialEquivBEq.trans
theorem BEq.neq_of_neq_of_beq [BEq α] [PartialEquivBEq α] {a b c : α} :
(a == b) = false b == c (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₁ (BEq.trans h₃ (BEq.symm h₂))
theorem BEq.neq_of_beq_of_neq [BEq α] [PartialEquivBEq α] {a b c : α} :
a == b (b == c) = false (a == c) = false :=
fun h₁ h₂ => Bool.eq_false_iff.2 fun h₃ => Bool.eq_false_iff.1 h₂ (BEq.trans (BEq.symm h₁) h₃)
instance (priority := low) [BEq α] [LawfulBEq α] : EquivBEq α where
refl := LawfulBEq.rfl
symm h := (beq_iff_eq _ _).2 <| Eq.symm <| (beq_iff_eq _ _).1 h
trans hab hbc := (beq_iff_eq _ _).2 <| ((beq_iff_eq _ _).1 hab).trans <| (beq_iff_eq _ _).1 hbc

View File

@@ -614,6 +614,13 @@ theorem ofBool_append (msb : Bool) (lsbs : BitVec w) :
ofBool msb ++ lsbs = (cons msb lsbs).cast (Nat.add_comm ..) :=
rfl
/--
`twoPow w i` is the bitvector `2^i` if `i < w`, and `0` otherwise.
That is, 2 to the power `i`.
For the bitwise point of view, it has the `i`th bit as `1` and all other bits as `0`.
-/
def twoPow (w : Nat) (i : Nat) : BitVec w := 1#w <<< i
end bitwise
section normalization_eqs

View File

@@ -159,6 +159,21 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
theorem allOnes_sub_eq_not (x : BitVec w) : allOnes w - x = ~~~x := by
rw [ add_not_self x, BitVec.add_comm, add_sub_cancel]
/-- Addition of bitvectors is the same as bitwise or, if bitwise and is zero. -/
theorem add_eq_or_of_and_eq_zero {w : Nat} (x y : BitVec w)
(h : x &&& y = 0#w) : x + y = x ||| y := by
rw [add_eq_adc, adc, iunfoldr_replace (fun _ => false) (x ||| y)]
· rfl
· simp only [adcb, atLeastTwo, Bool.and_false, Bool.or_false, bne_false, getLsb_or,
Prod.mk.injEq, and_eq_false_imp]
intros i
replace h : (x &&& y).getLsb i = (0#w).getLsb i := by rw [h]
simp only [getLsb_and, getLsb_zero, and_eq_false_imp] at h
constructor
· intros hx
simp_all [hx]
· by_cases hx : x.getLsb i <;> simp_all [hx]
/-! ### Negation -/
theorem bit_not_testBit (x : BitVec w) (i : Fin w) :
@@ -235,4 +250,80 @@ theorem sle_eq_carry (x y : BitVec w) :
x.sle y = !((x.msb == y.msb).xor (carry w y (~~~x) true)) := by
rw [sle_eq_not_slt, slt_eq_not_carry, beq_comm]
/-! ### mul recurrence for bitblasting -/
/--
A recurrence that describes multiplication as repeated addition.
Is useful for bitblasting multiplication.
-/
def mulRec (l r : BitVec w) (s : Nat) : BitVec w :=
let cur := if r.getLsb s then (l <<< s) else 0
match s with
| 0 => cur
| s + 1 => mulRec l r s + cur
theorem mulRec_zero_eq (l r : BitVec w) :
mulRec l r 0 = if r.getLsb 0 then l else 0 := by
simp [mulRec]
theorem mulRec_succ_eq (l r : BitVec w) (s : Nat) :
mulRec l r (s + 1) = mulRec l r s + if r.getLsb (s + 1) then (l <<< (s + 1)) else 0 := rfl
/--
Recurrence lemma: truncating to `i+1` bits and then zero extending to `w`
equals truncating upto `i` bits `[0..i-1]`, and then adding the `i`th bit of `x`.
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow (x : BitVec w) (i : Nat) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) + (x &&& twoPow w i) := by
rw [add_eq_or_of_and_eq_zero]
· ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp
· simp only [getLsb_twoPow, hik, decide_False, Bool.and_false, Bool.or_false]
by_cases hik' : k < (i + 1)
· have hik'' : k < i := by omega
simp [hik', hik'']
· have hik'' : ¬ (k < i) := by omega
simp [hik', hik'']
· ext k
simp
omega
/--
Recurrence lemma: multiplying `l` with the first `s` bits of `r` is the
same as truncating `r` to `s` bits, then zero extending to the original length,
and performing the multplication. -/
theorem mulRec_eq_mul_signExtend_truncate (l r : BitVec w) (s : Nat) :
mulRec l r s = l * ((r.truncate (s + 1)).zeroExtend w) := by
induction s
case zero =>
simp only [mulRec_zero_eq, ofNat_eq_ofNat, Nat.reduceAdd]
by_cases r.getLsb 0
case pos hr =>
simp only [hr, reduceIte, truncate, zeroExtend_one_eq_ofBool_getLsb_zero,
hr, ofBool_true, ofNat_eq_ofNat]
rw [zeroExtend_ofNat_one_eq_ofNat_one_of_lt (by omega)]
simp
case neg hr =>
simp [hr, zeroExtend_one_eq_ofBool_getLsb_zero]
case succ s' hs =>
rw [mulRec_succ_eq, hs]
have heq :
(if r.getLsb (s' + 1) = true then l <<< (s' + 1) else 0) =
(l * (r &&& (BitVec.twoPow w (s' + 1)))) := by
simp only [ofNat_eq_ofNat, and_twoPow_eq]
by_cases hr : r.getLsb (s' + 1) <;> simp [hr]
rw [heq, BitVec.mul_add, zeroExtend_truncate_succ_eq_zeroExtend_truncate_add_twoPow]
theorem getLsb_mul (x y : BitVec w) (i : Nat) :
(x * y).getLsb i = (mulRec x y w).getLsb i := by
simp only [mulRec_eq_mul_signExtend_truncate]
rw [truncate, truncate_eq_zeroExtend, truncate_eq_zeroExtend,
truncate_truncate_of_le]
· simp
· omega
end BitVec

View File

@@ -110,8 +110,8 @@ theorem eq_of_getMsb_eq {x y : BitVec w}
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
@[simp] theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
@[simp] theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp [of_length_zero]
@[simp] theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp [of_length_zero]
theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false := by simp
theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false := by simp
@[simp] theorem msb_zero_length (x : BitVec 0) : x.msb = false := by simp [BitVec.msb, of_length_zero]
theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y
@@ -163,6 +163,13 @@ theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_of_le_right (by trivial : 0 < 2) le)
@[simp]
theorem getLsb_ofBool (b : Bool) (i : Nat) : (BitVec.ofBool b).getLsb i = ((i = 0) && b) := by
rcases b with rfl | rfl
· simp [ofBool]
· simp only [ofBool, ofNat_eq_ofNat, cond_true, getLsb_ofNat, Bool.and_true]
by_cases hi : i = 0 <;> simp [hi] <;> omega
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsb]
@@ -184,8 +191,7 @@ theorem msb_eq_getLsb_last (x : BitVec w) :
· simp only [h]
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
· decide
· have : BitVec.toNat x < 2^w + 2^w := by simpa [Nat.pow_succ, Nat.mul_two] using x.isLt
omega
· omega
@[bv_toNat] theorem getLsb_succ_last (x : BitVec (w + 1)) :
x.getLsb w = decide (2 ^ w x.toNat) := getLsb_last x
@@ -287,6 +293,9 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
/-! ### zeroExtend and truncate -/
theorem truncate_eq_zeroExtend {v : Nat} {x : BitVec w} :
truncate v x = zeroExtend v x := rfl
@[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m n) (x : BitVec m) :
(zeroExtend' p x).toNat = x.toNat := by
unfold zeroExtend'
@@ -320,7 +329,7 @@ theorem zeroExtend'_eq {x : BitVec w} (h : w ≤ v) : x.zeroExtend' h = x.zeroEx
apply eq_of_toNat_eq
simp [toNat_zeroExtend]
@[simp] theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = truncate m x := by
apply eq_of_toNat_eq
@@ -331,10 +340,7 @@ theorem toNat_eq_nat (x : BitVec w) (y : Nat)
: (x.toNat = y) (y < 2^w (x = BitVec.ofNat w y)) := by
apply Iff.intro
· intro eq
simp at eq
have lt := x.isLt
simp [eq] at lt
simp [eq, lt, x.isLt]
simp [eq, x.isLt]
· intro eq
simp [Nat.mod_eq_of_lt, eq]
@@ -377,7 +383,7 @@ theorem nat_eq_toNat (x : BitVec w) (y : Nat)
all_goals (first | apply getLsb_ge | apply Eq.symm; apply getLsb_ge)
<;> omega
@[simp] theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
getLsb (truncate m x) i = (decide (i < m) && getLsb x i) :=
getLsb_zeroExtend m x i
@@ -396,6 +402,12 @@ theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k := b
(x.truncate l).truncate k = x.truncate k :=
zeroExtend_zeroExtend_of_le x h
/--Truncating by the bitwidth has no effect. -/
@[simp]
theorem truncate_eq_self {x : BitVec w} : x.truncate w = x := by
ext i
simp [getLsb_zeroExtend]
@[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k := by
apply eq_of_getLsb_eq
simp
@@ -408,6 +420,22 @@ theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) &
theorem msb_zeroExtend' (x : BitVec w) (h : w v) : (x.zeroExtend' h).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
rw [zeroExtend'_eq, msb_zeroExtend]
/-- zero extending a bitvector to width 1 equals the boolean of the lsb. -/
theorem zeroExtend_one_eq_ofBool_getLsb_zero (x : BitVec w) :
x.zeroExtend 1 = BitVec.ofBool (x.getLsb 0) := by
ext i
simp [getLsb_zeroExtend, Fin.fin_one_eq_zero i]
/-- Zero extending `1#v` to `1#w` equals `1#w` when `v > 0`. -/
theorem zeroExtend_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
(BitVec.ofNat v 1).zeroExtend w = BitVec.ofNat w 1 := by
ext i, hilt
simp only [getLsb_zeroExtend, hilt, decide_True, getLsb_ofNat, Bool.true_and,
Bool.and_iff_right_iff_imp, decide_eq_true_eq]
intros hi₁
have hv := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi₁
omega
/-! ## extractLsb -/
@[simp]
@@ -580,7 +608,7 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
ext
simp_all [lt_of_getLsb]
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) := by
@[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x ^^^ cast h y = cast h (x ^^^ y) := by
ext
simp_all [lt_of_getLsb]
@@ -593,6 +621,11 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
BitVec.toFin (x <<< n) = Fin.ofNat' (x.toNat <<< n) (Nat.two_pow_pos w) := rfl
@[simp]
theorem shiftLeft_zero_eq (x : BitVec w) : x <<< 0 = x := by
apply eq_of_toNat_eq
simp
@[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) := by
rw [ testBit_toNat, getLsb]
@@ -1047,8 +1080,16 @@ theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toNat) := by rfl
@[simp, bv_toNat] theorem toNat_sub {n} (x y : BitVec n) :
(x - y).toNat = (((2^n - y.toNat) + x.toNat) % 2^n) := rfl
@[simp] theorem toNat_sub {n} (x y : BitVec n) :
(x - y).toNat = (((2^n - y.toNat) + x.toNat) % 2^n) := rfl
-- We prefer this lemma to `toNat_sub` for the `bv_toNat` simp set.
-- For reasons we don't yet understand, unfolding via `toNat_sub` sometimes
-- results in `omega` generating proof terms that are very slow in the kernel.
@[bv_toNat] theorem toNat_sub' {n} (x y : BitVec n) :
(x - y).toNat = ((x.toNat + (2^n - y.toNat)) % 2^n) := by
rw [toNat_sub, Nat.add_comm]
@[simp] theorem toFin_sub (x y : BitVec n) : (x - y).toFin = toFin x - toFin y := rfl
@[simp] theorem ofFin_sub (x : Fin (2^n)) (y : BitVec n) : .ofFin x - y = .ofFin (x - y.toFin) :=
@@ -1140,6 +1181,18 @@ instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_asso
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
right_id := BitVec.mul_one
@[simp]
theorem BitVec.mul_zero {x : BitVec w} : x * 0#w = 0#w := by
apply eq_of_toNat_eq
simp [toNat_mul]
theorem BitVec.mul_add {x y z : BitVec w} :
x * (y + z) = x * y + x * z := by
apply eq_of_toNat_eq
simp only [toNat_mul, toNat_add, Nat.add_mod_mod, Nat.mod_add_mod]
rw [Nat.mul_mod, Nat.mod_mod (y.toNat + z.toNat),
Nat.mul_mod, Nat.mul_add]
@[simp, bv_toNat] theorem toInt_mul (x y : BitVec w) :
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
simp [toInt_eq_toNat_bmod]
@@ -1240,11 +1293,7 @@ x.rotateLeft 2 = (<6 5 | 4 3 2 1 0>).rotateLeft 2 = <3 2 1 0 | 6 5>
theorem getLsb_rotateLeftAux_of_le {x : BitVec w} {r : Nat} {i : Nat} (hi : i < r) :
(x.rotateLeftAux r).getLsb i = x.getLsb (w - r + i) := by
rw [rotateLeftAux, getLsb_or, getLsb_ushiftRight]
suffices (x <<< r).getLsb i = false by
simp; omega
simp only [getLsb_shiftLeft, Bool.and_eq_false_imp, Bool.and_eq_true, decide_eq_true_eq,
Bool.not_eq_true', decide_eq_false_iff_not, Nat.not_lt, and_imp]
omega
simp; omega
/--
Accessing bits in `x.rotateLeft r` the range `[r, w)` is equal to
@@ -1375,4 +1424,84 @@ theorem getLsb_rotateRight {x : BitVec w} {r i : Nat} :
· simp
· rw [ rotateRight_mod_eq_rotateRight, getLsb_rotateRight_of_le (Nat.mod_lt _ (by omega))]
/- ## twoPow -/
@[simp, bv_toNat]
theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
rcases w with rfl | w
· simp [Nat.mod_one]
· simp only [twoPow, toNat_shiftLeft, toNat_ofNat]
have h1 : 1 < 2 ^ (w + 1) := Nat.one_lt_two_pow (by omega)
rw [Nat.mod_eq_of_lt h1, Nat.shiftLeft_eq, Nat.one_mul]
@[simp]
theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j)) := by
rcases w with rfl | w
· simp
· simp only [twoPow, getLsb_shiftLeft, getLsb_ofNat]
by_cases hj : j < i
· simp only [hj, decide_True, Bool.not_true, Bool.and_false, Bool.false_and, Bool.false_eq,
Bool.and_eq_false_imp, decide_eq_true_eq, decide_eq_false_iff_not]
omega
· by_cases hi : Nat.testBit 1 (j - i)
· obtain hi' := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi
have hij : j = i := by omega
simp_all
· have hij : i j := by
intro h; subst h
simp at hi
simp_all
theorem and_twoPow_eq (x : BitVec w) (i : Nat) :
x &&& (twoPow w i) = if x.getLsb i then twoPow w i else 0#w := by
ext j
simp only [getLsb_and, getLsb_twoPow]
by_cases hj : i = j <;> by_cases hx : x.getLsb i <;> simp_all
@[simp]
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
x * (twoPow w i) = x <<< i := by
apply eq_of_toNat_eq
simp only [toNat_mul, toNat_twoPow, toNat_shiftLeft, Nat.shiftLeft_eq]
by_cases hi : i < w
· have hpow : 2^i < 2^w := Nat.pow_lt_pow_of_lt (by omega) (by omega)
rw [Nat.mod_eq_of_lt hpow]
· have hpow : 2 ^ i % 2 ^ w = 0 := by
rw [Nat.mod_eq_zero_of_dvd]
apply Nat.pow_dvd_pow 2 (by omega)
simp [Nat.mul_mod, hpow]
/- ### zeroExtend, truncate, and bitwise operations -/
/--
When the `(i+1)`th bit of `x` is false,
keeping the lower `(i + 1)` bits of `x` equals keeping the lower `i` bits.
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_of_getLsb_false
{x : BitVec w} {i : Nat} (hx : x.getLsb i = false) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) := by
ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp [hx]
· by_cases hik' : k < i + 1 <;> simp [hik'] <;> omega
/--
When the `(i+1)`th bit of `x` is true,
keeping the lower `(i + 1)` bits of `x` equalsk eeping the lower `i` bits
and then performing bitwise-or with `twoPow i = (1 << i)`,
-/
theorem zeroExtend_truncate_succ_eq_zeroExtend_truncate_or_twoPow_of_getLsb_true
{x : BitVec w} {i : Nat} (hx : x.getLsb i = true) :
zeroExtend w (x.truncate (i + 1)) =
zeroExtend w (x.truncate i) ||| (twoPow w i) := by
ext k
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and, getLsb_or, getLsb_and]
by_cases hik : i = k
· subst hik
simp [hx]
· by_cases hik' : k < i + 1 <;> simp [hik, hik'] <;> omega
end BitVec

View File

@@ -52,8 +52,8 @@ theorem eq_iff_iff {a b : Bool} : a = b ↔ (a ↔ b) := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} [Decidable (b = true)] : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} [Decidable (b = false)] : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
theorem decide_true_eq {b : Bool} [Decidable (true = b)] : decide (true = b) = b := by cases b <;> simp
theorem decide_false_eq {b : Bool} [Decidable (false = b)] : decide (false = b) = !b := by cases b <;> simp
/-! ### and -/
@@ -163,7 +163,7 @@ Consider the term: `¬((b && c) = true)`:
-/
@[simp] theorem and_eq_false_imp : (x y : Bool), (x && y) = false (x = true y = false) := by decide
@[simp] theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by decide
theorem or_eq_true_iff : (x y : Bool), (x || y) = true x = true y = true := by simp
@[simp] theorem or_eq_false_iff : (x y : Bool), (x || y) = false x = false y = false := by decide
@@ -187,11 +187,9 @@ in false_eq and true_eq.
@[simp] theorem true_beq : b, (true == b) = b := by decide
@[simp] theorem false_beq : b, (false == b) = !b := by decide
@[simp] theorem beq_true : b, (b == true) = b := by decide
instance : Std.LawfulIdentity (· == ·) true where
left_id := true_beq
right_id := beq_true
@[simp] theorem beq_false : b, (b == false) = !b := by decide
@[simp] theorem true_bne : (b : Bool), (true != b) = !b := by decide
@[simp] theorem false_bne : (b : Bool), (false != b) = b := by decide
@@ -353,7 +351,7 @@ theorem and_or_inj_left_iff :
/-! ## toNat -/
/-- convert a `Bool` to a `Nat`, `false -> 0`, `true -> 1` -/
def toNat (b:Bool) : Nat := cond b 1 0
def toNat (b : Bool) : Nat := cond b 1 0
@[simp] theorem toNat_false : false.toNat = 0 := rfl
@@ -496,6 +494,16 @@ protected theorem cond_false {α : Type u} {a b : α} : cond false a b = b := co
@[simp] theorem cond_true_same : (c b : Bool), cond c c b = (c || b) := by decide
@[simp] theorem cond_false_same : (c b : Bool), cond c b c = (c && b) := by decide
theorem cond_pos {b : Bool} {a a' : α} (h : b = true) : (bif b then a else a') = a := by
rw [h, cond_true]
theorem cond_neg {b : Bool} {a a' : α} (h : b = false) : (bif b then a else a') = a' := by
rw [h, cond_false]
theorem apply_cond (f : α β) {b : Bool} {a a' : α} :
f (bif b then a else a') = bif b then f a else f a' := by
cases b <;> simp
/-# decidability -/
protected theorem decide_coe (b : Bool) [Decidable (b = true)] : decide (b = true) = b := decide_eq_true

View File

@@ -52,13 +52,9 @@ def get : (a : @& ByteArray) → (@& Fin a.size) → UInt8
instance : GetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem ByteArray Nat UInt8 fun xs i => i < xs.size where
instance : GetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
getElem xs i h := xs.uget i h
instance : LawfulGetElem ByteArray USize UInt8 fun xs i => i.val < xs.size where
@[extern "lean_byte_array_set"]
def set! : ByteArray (@& Nat) UInt8 ByteArray
| bs, i, b => bs.set! i b
@@ -96,20 +92,24 @@ protected def append (a : ByteArray) (b : ByteArray) : ByteArray :=
instance : Append ByteArray := ByteArray.append
partial def toList (bs : ByteArray) : List UInt8 :=
def toList (bs : ByteArray) : List UInt8 :=
let rec loop (i : Nat) (r : List UInt8) :=
if i < bs.size then
loop (i+1) (bs.get! i :: r)
else
r.reverse
termination_by bs.size - i
decreasing_by decreasing_trivial_pre_omega
loop 0 []
@[inline] partial def findIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option Nat :=
@[inline] def findIdx? (a : ByteArray) (p : UInt8 Bool) (start := 0) : Option Nat :=
let rec @[specialize] loop (i : Nat) :=
if i < a.size then
if p (a.get! i) then some i else loop (i+1)
else
none
termination_by a.size - i
decreasing_by decreasing_trivial_pre_omega
loop start
/--

View File

@@ -31,11 +31,9 @@ theorem utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2 c.utf8Siz
rw [Char.ofNat, dif_pos]
rfl
@[ext] theorem Char.ext : {a b : Char} a.val = b.val a = b
@[ext] protected theorem ext : {a b : Char} a.val = b.val a = b
| _,_, _,_, rfl => rfl
theorem Char.ext_iff {x y : Char} : x = y x.val = y.val := congrArg _, Char.ext
end Char
@[deprecated Char.utf8Size (since := "2024-06-04")] abbrev String.csize := Char.utf8Size

View File

@@ -0,0 +1,15 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.Nat.Bitwise
import Init.Data.Fin.Basic
namespace Fin
@[simp] theorem and_val (a b : Fin n) : (a &&& b).val = a.val &&& b.val :=
Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt Nat.and_le_left a.isLt)
end Fin

View File

@@ -37,9 +37,7 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem ext_iff {a b : Fin n} : a = b a.1 = b.1 := val_inj.symm
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -47,12 +45,12 @@ theorem forall_iff {p : Fin n → Prop} : (∀ i, p i) ↔ ∀ i h, p ⟨i, h⟩
fun h i hi => h i, hi, fun h i, hi => h i hi
protected theorem mk.inj_iff {n a b : Nat} {ha : a < n} {hb : b < n} :
(a, ha : Fin n) = b, hb a = b := ext_iff
(a, ha : Fin n) = b, hb a = b := Fin.ext_iff
theorem val_mk {m n : Nat} (h : m < n) : (m, h : Fin n).val = m := rfl
theorem eq_mk_iff_val_eq {a : Fin n} {k : Nat} {hk : k < n} :
a = k, hk (a : Nat) = k := ext_iff
a = k, hk (a : Nat) = k := Fin.ext_iff
theorem mk_val (i : Fin n) : (i, i.isLt : Fin n) = i := Fin.eta ..
@@ -145,7 +143,7 @@ theorem eq_succ_of_ne_zero {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) : ∃ j :
@[simp] theorem val_rev (i : Fin n) : rev i = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := ext <| by
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@[simp] theorem rev_le_rev {i j : Fin n} : rev i rev j j i := by
@@ -171,12 +169,12 @@ theorem le_last (i : Fin (n + 1)) : i ≤ last n := Nat.le_of_lt_succ i.is_lt
theorem last_pos : (0 : Fin (n + 2)) < last (n + 1) := Nat.succ_pos _
theorem eq_last_of_not_lt {i : Fin (n + 1)} (h : ¬(i : Nat) < n) : i = last n :=
ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
Fin.ext <| Nat.le_antisymm (le_last i) (Nat.not_lt.1 h)
theorem val_lt_last {i : Fin (n + 1)} : i last n (i : Nat) < n :=
Decidable.not_imp_comm.1 eq_last_of_not_lt
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := ext <| by simp
@[simp] theorem rev_last (n : Nat) : rev (last n) = 0 := Fin.ext <| by simp
@[simp] theorem rev_zero (n : Nat) : rev 0 = last n := by
rw [ rev_rev (last _), rev_last]
@@ -244,11 +242,11 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt one_pos
@[simp] theorem succ_lt_succ_iff {a b : Fin n} : a.succ < b.succ a < b := Nat.succ_lt_succ_iff
@[simp] theorem succ_inj {a b : Fin n} : a.succ = b.succ a = b := by
refine fun h => ext ?_, congrArg _
refine fun h => Fin.ext ?_, congrArg _
apply Nat.le_antisymm <;> exact succ_le_succ_iff.1 (h Nat.le_refl _)
theorem succ_ne_zero {n} : k : Fin n, Fin.succ k 0
| k, _, heq => Nat.succ_ne_zero k <| ext_iff.1 heq
| k, _, heq => Nat.succ_ne_zero k <| congrArg Fin.val heq
@[simp] theorem succ_zero_eq_one : Fin.succ (0 : Fin (n + 1)) = 1 := rfl
@@ -267,7 +265,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
rw [ succ_zero_eq_one, succ_lt_succ_iff]; exact succ_pos a
@[simp] theorem add_one_lt_iff {n : Nat} {k : Fin (n + 2)} : k + 1 < k k = last _ := by
simp only [lt_def, val_add, val_last, ext_iff]
simp only [lt_def, val_add, val_last, Fin.ext_iff]
let k, hk := k
match Nat.eq_or_lt_of_le (Nat.le_of_lt_succ hk) with
| .inl h => cases h; simp [Nat.succ_pos]
@@ -285,7 +283,7 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
split <;> simp [*, (Nat.succ_ne_zero _).symm, Nat.ne_of_gt (Nat.lt_succ_self _)]
@[simp] theorem last_le_iff {n : Nat} {k : Fin (n + 1)} : last n k k = last n := by
rw [ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
rw [Fin.ext_iff, Nat.le_antisymm_iff, le_def, and_iff_right (by apply le_last)]
@[simp] theorem lt_add_one_iff {n : Nat} {k : Fin (n + 1)} : k < k + 1 k < last n := by
rw [ Decidable.not_iff_not]; simp
@@ -306,10 +304,10 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [ext_iff]
@[simp] theorem castLE_zero {n m : Nat} (h : n.succ m.succ) : castLE h 0 = 0 := by simp [Fin.ext_iff]
@[simp] theorem castLE_succ {m n : Nat} (h : m + 1 n + 1) (i : Fin m) :
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [ext_iff]
castLE h i.succ = (castLE (Nat.succ_le_succ_iff.mp h) i).succ := by simp [Fin.ext_iff]
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
@@ -322,7 +320,7 @@ theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) ≠ 1 :=
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (cast h i : Nat) = i := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : cast h (last n) = last n' :=
ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : cast h i, hn = i, h hn := rfl
@@ -348,7 +346,7 @@ theorem castAdd_lt {m : Nat} (n : Nat) (i : Fin m) : (castAdd n i : Nat) < m :=
/-- For rewriting in the reverse direction, see `Fin.cast_castAdd_left`. -/
theorem castAdd_cast {n n' : Nat} (m : Nat) (i : Fin n') (h : n' = n) :
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := ext rfl
castAdd m (Fin.cast h i) = Fin.cast (congrArg (. + m) h) (castAdd m i) := Fin.ext rfl
theorem cast_castAdd_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
cast h (castAdd m i) = castAdd m (cast (Nat.add_right_cancel h) i) := rfl
@@ -397,7 +395,7 @@ theorem castSucc_lt_iff_succ_le {n : Nat} {i : Fin n} {j : Fin (n + 1)} :
@[simp] theorem castSucc_lt_castSucc_iff {a b : Fin n} :
Fin.castSucc a < Fin.castSucc b a < b := .rfl
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [ext_iff]
theorem castSucc_inj {a b : Fin n} : castSucc a = castSucc b a = b := by simp [Fin.ext_iff]
theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
@@ -409,7 +407,7 @@ theorem castSucc_lt_last (a : Fin n) : castSucc a < last n := a.is_lt
theorem castSucc_pos {i : Fin (n + 1)} (h : 0 < i) : 0 < castSucc i := by
simpa [lt_def] using h
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 a = 0 := by simp [ext_iff]
@[simp] theorem castSucc_eq_zero_iff (a : Fin (n + 1)) : castSucc a = 0 a = 0 := by simp [Fin.ext_iff]
theorem castSucc_ne_zero_iff (a : Fin (n + 1)) : castSucc a 0 a 0 :=
not_congr <| castSucc_eq_zero_iff a
@@ -421,7 +419,7 @@ theorem castSucc_fin_succ (n : Nat) (j : Fin n) :
theorem coeSucc_eq_succ {a : Fin n} : castSucc a + 1 = a.succ := by
cases n
· exact a.elim0
· simp [ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
· simp [Fin.ext_iff, add_def, Nat.mod_eq_of_lt (Nat.succ_lt_succ a.is_lt)]
theorem lt_succ {a : Fin n} : castSucc a < a.succ := by
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
@@ -454,7 +452,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
@[simp] theorem cast_addNat_right {n m m' : Nat} (i : Fin n) (h : n + m' = n + m) :
cast h (addNat i m') = addNat i m :=
ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@@ -474,7 +472,7 @@ theorem cast_natAdd_right {n n' m : Nat} (i : Fin n') (h : m + n' = m + n) :
@[simp] theorem cast_natAdd_left {n m m' : Nat} (i : Fin n) (h : m' + n = m + n) :
cast h (natAdd m' i) = natAdd m i :=
ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
Fin.ext <| (congrArg (· + (i : Nat)) (Nat.add_right_cancel h) : _)
theorem castAdd_natAdd (p m : Nat) {n : Nat} (i : Fin n) :
castAdd p (natAdd m i) = cast (Nat.add_assoc ..).symm (natAdd m (castAdd p i)) := rfl
@@ -484,27 +482,27 @@ theorem natAdd_castAdd (p m : Nat) {n : Nat} (i : Fin n) :
theorem natAdd_natAdd (m n : Nat) {p : Nat} (i : Fin p) :
natAdd m (natAdd n i) = cast (Nat.add_assoc ..) (natAdd (m + n) i) :=
ext <| (Nat.add_assoc ..).symm
Fin.ext <| (Nat.add_assoc ..).symm
@[simp]
theorem cast_natAdd_zero {n n' : Nat} (i : Fin n) (h : 0 + n = n') :
cast h (natAdd 0 i) = cast ((Nat.zero_add _).symm.trans h) i :=
ext <| Nat.zero_add _
Fin.ext <| Nat.zero_add _
@[simp]
theorem cast_natAdd (n : Nat) {m : Nat} (i : Fin m) :
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (natAdd n i) = addNat i n := Fin.ext <| Nat.add_comm ..
@[simp]
theorem cast_addNat {n : Nat} (m : Nat) (i : Fin n) :
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := ext <| Nat.add_comm ..
cast (Nat.add_comm ..) (addNat i m) = natAdd m i := Fin.ext <| Nat.add_comm ..
@[simp] theorem natAdd_last {m n : Nat} : natAdd n (last m) = last (n + m) := rfl
theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSucc (natAdd n i) :=
rfl
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := ext <| by
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
@@ -534,7 +532,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} (i : Fin (n + 1)) (hi : i ≠ 0) (j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [ext_iff, coe_pred, Nat.add_sub_cancel]
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -554,14 +552,14 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
{a b : Fin (n + 1)} {ha : a 0} {hb : b 0}, a.pred ha = b.pred hb a = b
| 0, _, _, ha, _ => by simp only [mk_zero, ne_eq, not_true] at ha
| i + 1, _, 0, _, _, hb => by simp only [mk_zero, ne_eq, not_true] at hb
| i + 1, hi, j + 1, hj, ha, hb => by simp [ext_iff, Nat.succ.injEq]
| i + 1, hi, j + 1, hj, ha, hb => by simp [Fin.ext_iff, Nat.succ.injEq]
@[simp] theorem pred_one {n : Nat} :
Fin.pred (1 : Fin (n + 2)) (Ne.symm (Fin.ne_of_lt one_pos)) = 0 := rfl
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@@ -573,10 +571,10 @@ theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (castSucc i.succ) (Fin.ne_of_gt (castSucc_pos i.succ_pos)) = castSucc i := rfl
@[simp] theorem addNat_subNat {i : Fin (n + m)} (h : m i) : addNat (subNat m i h) m = i :=
ext <| Nat.sub_add_cancel h
Fin.ext <| Nat.sub_add_cancel h
@[simp] theorem subNat_addNat (i : Fin n) (m : Nat) (h : m addNat i m := le_coe_addNat m i) :
subNat m (addNat i m) h = i := ext <| Nat.add_sub_cancel i m
subNat m (addNat i m) h = i := Fin.ext <| Nat.add_sub_cancel i m
@[simp] theorem natAdd_subNat_cast {i : Fin (n + m)} (h : n i) :
natAdd n (subNat n (cast (Nat.add_comm ..) i) h) = i := by simp [ cast_addNat]; rfl
@@ -786,6 +784,9 @@ theorem coe_sub_iff_le {a b : Fin n} : (↑(a - b) : Nat) = a - b ↔ b ≤ a :=
rw [Nat.mod_eq_of_lt]
all_goals omega
theorem sub_val_of_le {a b : Fin n} : b a (a - b).val = a.val - b.val :=
coe_sub_iff_le.2
theorem coe_sub_iff_lt {a b : Fin n} : ((a - b) : Nat) = n + a - b a < b := by
rw [sub_def, lt_def]
dsimp only
@@ -807,10 +808,10 @@ theorem coe_mul {n : Nat} : ∀ a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
match n with
| 0 => exact Subsingleton.elim (α := Fin 1) ..
| n+1 => simp [ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
| n+1 => simp [Fin.ext_iff, mul_def, Nat.mod_eq_of_lt (is_lt k)]
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
Fin.ext <| by rw [mul_def, mul_def, Nat.mul_comm]
instance : Std.Commutative (α := Fin n) (· * ·) := Fin.mul_comm
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
@@ -826,9 +827,9 @@ instance : Std.LawfulIdentity (α := Fin (n + 1)) (· * ·) 1 where
left_id := Fin.one_mul
right_id := Fin.mul_one
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [ext_iff, mul_def]
protected theorem mul_zero (k : Fin (n + 1)) : k * 0 = 0 := by simp [Fin.ext_iff, mul_def]
protected theorem zero_mul (k : Fin (n + 1)) : (0 : Fin (n + 1)) * k = 0 := by
simp [ext_iff, mul_def]
simp [Fin.ext_iff, mul_def]
end Fin

View File

@@ -101,13 +101,13 @@ Returns an undefined value if `x` is not finite.
instance : ToString Float where
toString := Float.toString
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
instance : Repr Float where
reprPrec n _ := Float.toString n
reprPrec n prec := if n < UInt64.toFloat 0 then Repr.addAppParen (toString n) prec else toString n
instance : ReprAtom Float :=
@[extern "lean_uint64_to_float"] opaque UInt64.toFloat (n : UInt64) : Float
@[extern "sin"] opaque Float.sin : Float Float
@[extern "cos"] opaque Float.cos : Float Float
@[extern "tan"] opaque Float.tan : Float Float

View File

@@ -58,13 +58,9 @@ def get? (ds : FloatArray) (i : Nat) : Option Float :=
instance : GetElem FloatArray Nat Float fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem FloatArray Nat Float fun xs i => i < xs.size where
instance : GetElem FloatArray USize Float fun xs i => i.val < xs.size where
getElem xs i h := xs.uget i h
instance : LawfulGetElem FloatArray USize Float fun xs i => i.val < xs.size where
@[extern "lean_float_array_uset"]
def uset : (a : FloatArray) (i : USize) Float i.toNat < a.size FloatArray
| ds, i, v, h => ds.uset i v h

View File

@@ -62,3 +62,16 @@ instance (P : Prop) : Hashable P where
/-- An opaque (low-level) hash operation used to implement hashing for pointers. -/
@[always_inline, inline] def hash64 (u : UInt64) : UInt64 :=
mixHash u 11
/-- `LawfulHashable α` says that the `BEq α` and `Hashable α` instances on `α` are compatible, i.e.,
that `a == b` implies `hash a = hash b`. This is automatic if the `BEq` instance is lawful.
-/
class LawfulHashable (α : Type u) [BEq α] [Hashable α] where
/-- If `a == b`, then `hash a = hash b`. -/
hash_eq (a b : α) : a == b hash a = hash b
theorem hash_eq [BEq α] [Hashable α] [LawfulHashable α] {a b : α} : a == b hash a = hash b :=
LawfulHashable.hash_eq a b
instance (priority := low) [BEq α] [Hashable α] [LawfulBEq α] : LawfulHashable α where
hash_eq _ _ h := eq_of_beq h rfl

View File

@@ -636,7 +636,7 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp]
theorem Int.emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
theorem emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
by_cases h : y = 0
· simp [h]
· simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]

View File

@@ -127,9 +127,14 @@ protected theorem lt_iff_le_not_le {a b : Int} : a < b ↔ a ≤ b ∧ ¬b ≤ a
· exact Int.le_antisymm h h'
· subst h'; apply Int.le_refl
protected theorem lt_of_not_ge {a b : Int} (h : ¬a b) : b < a :=
Int.lt_iff_le_not_le.mpr (Int.le_total ..).resolve_right h, h
protected theorem not_le_of_gt {a b : Int} (h : b < a) : ¬a b :=
(Int.lt_iff_le_not_le.mp h).right
protected theorem not_le {a b : Int} : ¬a b b < a :=
fun h => Int.lt_iff_le_not_le.2 (Int.le_total ..).resolve_right h, h,
fun h => (Int.lt_iff_le_not_le.1 h).2
Iff.intro Int.lt_of_not_ge Int.not_le_of_gt
protected theorem not_lt {a b : Int} : ¬a < b b a :=
by rw [ Int.not_le, Decidable.not_not]
@@ -509,9 +514,6 @@ theorem mem_toNat' : ∀ (a : Int) (n : Nat), toNat' a = some n ↔ a = n
/-! ## Order properties of the integers -/
protected theorem lt_of_not_ge {a b : Int} : ¬a b b < a := Int.not_le.mp
protected theorem not_le_of_gt {a b : Int} : b < a ¬a b := Int.not_le.mpr
protected theorem le_of_not_le {a b : Int} : ¬ a b b a := (Int.le_total a b).resolve_left
@[simp] theorem negSucc_not_pos (n : Nat) : 0 < -[n+1] False := by
@@ -586,7 +588,10 @@ theorem add_one_le_iff {a b : Int} : a + 1 ≤ b ↔ a < b := .rfl
theorem lt_add_one_iff {a b : Int} : a < b + 1 a b := Int.add_le_add_iff_right _
@[simp] theorem succ_ofNat_pos (n : Nat) : 0 < (n : Int) + 1 :=
lt_add_one_iff.2 (ofNat_zero_le _)
lt_add_one_iff.mpr (ofNat_zero_le _)
theorem not_ofNat_neg (n : Nat) : ¬((n : Int) < 0) :=
Int.not_lt.mpr (ofNat_zero_le ..)
theorem le_add_one {a b : Int} (h : a b) : a b + 1 :=
Int.le_of_lt (Int.lt_add_one_iff.2 h)
@@ -801,6 +806,12 @@ protected theorem lt_add_of_neg_lt_sub_right {a b c : Int} (h : -b < a - c) : c
protected theorem neg_lt_sub_right_of_lt_add {a b c : Int} (h : c < a + b) : -b < a - c :=
Int.lt_sub_left_of_add_lt (Int.sub_right_lt_of_lt_add h)
protected theorem add_lt_iff (a b c : Int) : a + b < c a < -b + c := by
rw [ Int.add_lt_add_iff_left (-b), Int.add_comm (-b), Int.add_neg_cancel_right]
protected theorem sub_lt_iff (a b c : Int) : a - b < c a < c + b :=
Iff.intro Int.lt_add_of_sub_right_lt Int.sub_right_lt_of_lt_add
protected theorem sub_lt_of_sub_lt {a b c : Int} (h : a - b < c) : a - c < b :=
Int.sub_left_lt_of_lt_add (Int.lt_add_of_sub_right_lt h)

View File

@@ -8,6 +8,7 @@ import Init.Data.List.Basic
import Init.Data.List.BasicAux
import Init.Data.List.Control
import Init.Data.List.Lemmas
import Init.Data.List.Attach
import Init.Data.List.Impl
import Init.Data.List.TakeDrop
import Init.Data.List.Notation

View File

@@ -0,0 +1,46 @@
/-
Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.List.Lemmas
namespace List
/-- `O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
but is defined only when all members of `l` satisfy `P`, using the proof
to apply `f`. -/
@[simp] def pmap {P : α Prop} (f : a, P a β) : l : List α, (H : a l, P a) List β
| [], _ => []
| a :: l, H => f a (forall_mem_cons.1 H).1 :: pmap f l (forall_mem_cons.1 H).2
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`List {x // P x}` is the same as the input `List α`.
(Someday, the compiler might do this optimization automatically, but until then...)
-/
@[inline] private unsafe def attachWithImpl
(l : List α) (P : α Prop) (_ : x l, P x) : List {x // P x} := unsafeCast l
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `l` to produce a new list
with the same elements but in the type `{x // P x}`. -/
@[implemented_by attachWithImpl] def attachWith
(l : List α) (P : α Prop) (H : x l, P x) : List {x // P x} := pmap Subtype.mk l H
/-- `O(1)`. "Attach" the proof that the elements of `l` are in `l` to produce a new list
with the same elements but in the type `{x // x ∈ l}`. -/
@[inline] def attach (l : List α) : List {x // x l} := attachWith l _ fun _ => id
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@[inline] private def pmapImpl {P : α Prop} (f : a, P a β) (l : List α) (H : a l, P a) :
List β := (l.attachWith _ H).map fun x, h' => f x h'
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
funext α β p f L h'
let rec go : L' (hL' : x, x L' p x),
pmap f L' hL' = map (fun x, hx => f x hx) (pmap Subtype.mk L' hL')
| nil, hL' => rfl
| cons _ L', hL' => congrArg _ <| go L' fun _ hx => hL' (.tail _ hx)
exact go L h'

View File

@@ -22,7 +22,7 @@ along with `@[csimp]` lemmas,
In `Init.Data.List.Lemmas` we develop the full API for these functions.
Recall that `length`, `get`, `set`, `fold`, and `concat` have already been defined in `Init.Prelude`.
Recall that `length`, `get`, `set`, `foldl`, and `concat` have already been defined in `Init.Prelude`.
The operations are organized as follow:
* Equality: `beq`, `isEqv`.
@@ -32,8 +32,8 @@ The operations are organized as follow:
* List membership: `isEmpty`, `elem`, `contains`, `mem` (and the `∈` notation),
and decidability for predicates quantifying over membership in a `List`.
* Sublists: `take`, `drop`, `takeWhile`, `dropWhile`, `partition`, `dropLast`,
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
`isPrefixOf`, `isPrefixOf?`, `isSuffixOf`, `isSuffixOf?`, `Subset`, `Sublist`, `rotateLeft` and `rotateRight`.
* Manipulating elements: `replace`, `insert`, `erase`, `eraseP`, `eraseIdx`, `find?`, `findSome?`, and `lookup`.
* Logic: `any`, `all`, `or`, and `and`.
* Zippers: `zipWith`, `zip`, `zipWithAll`, and `unzip`.
* Ranges and enumeration: `range`, `iota`, `enumFrom`, and `enum`.
@@ -67,6 +67,9 @@ namespace List
@[simp 1100] theorem length_singleton (a : α) : length [a] = 1 := rfl
@[simp] theorem length_cons {α} (a : α) (as : List α) : (cons a as).length = as.length + 1 :=
rfl
/-! ### set -/
@[simp] theorem length_set (as : List α) (i : Nat) (a : α) : (as.set i a).length = as.length := by
@@ -85,7 +88,7 @@ namespace List
/-! ### concat -/
@[simp] theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
@[simp high] theorem length_concat (as : List α) (a : α) : (concat as a).length = as.length + 1 := by
induction as with
| nil => rfl
| cons _ xs ih => simp [concat, ih]
@@ -567,10 +570,12 @@ def replicate : (n : Nat) → (a : α) → List α
| n+1, a => a :: replicate n a
@[simp] theorem replicate_zero : replicate 0 a = [] := rfl
@[simp] theorem replicate_succ (a : α) (n) : replicate (n+1) a = a :: replicate n a := rfl
theorem replicate_succ (a : α) (n) : replicate (n+1) a = a :: replicate n a := rfl
@[simp] theorem length_replicate (n : Nat) (a : α) : (replicate n a).length = n := by
induction n <;> simp_all
induction n with
| zero => simp
| succ n ih => simp only [ih, replicate_succ, length_cons, Nat.succ_eq_add_one]
/-! ## List membership
@@ -609,13 +614,13 @@ def isEmpty : List α → Bool
-/
def elem [BEq α] (a : α) : List α Bool
| [] => false
| b::bs => match b == a with
| b::bs => match a == b with
| true => true
| false => elem a bs
@[simp] theorem elem_nil [BEq α] : ([] : List α).elem a = false := rfl
theorem elem_cons [BEq α] {a : α} :
(a::as).elem b = match a == b with | true => true | false => as.elem b := rfl
(b::bs).elem a = match a == b with | true => true | false => bs.elem a := rfl
/-- `notElem a l` is `!(elem a l)`. -/
@[deprecated (since := "2024-06-15")]
@@ -812,6 +817,8 @@ def dropLast {α} : List α → List α
@[simp] theorem dropLast_cons₂ :
(x::y::zs).dropLast = x :: (y::zs).dropLast := rfl
-- Later this can be proved by `simp` via `[List.length_dropLast, List.length_cons, Nat.add_sub_cancel]`,
-- but we need this while bootstrapping `Array`.
@[simp] theorem length_dropLast_cons (a : α) (as : List α) : (a :: as).dropLast.length = as.length := by
match as with
| [] => rfl
@@ -850,12 +857,49 @@ That is, there exists a `t` such that `l₂ == t ++ l₁`. -/
def isSuffixOf [BEq α] (l₁ l₂ : List α) : Bool :=
isPrefixOf l₁.reverse l₂.reverse
@[simp] theorem isSuffixOf_nil_left [BEq α] : isSuffixOf ([] : List α) l = true := by
simp [isSuffixOf]
/-! ### isSuffixOf? -/
/-- `isSuffixOf? l₁ l₂` returns `some t` when `l₂ == t ++ l₁`.-/
def isSuffixOf? [BEq α] (l₁ l₂ : List α) : Option (List α) :=
Option.map List.reverse <| isPrefixOf? l₁.reverse l₂.reverse
/-! ### Subset -/
/--
`l₁ ⊆ l₂` means that every element of `l₁` is also an element of `l₂`, ignoring multiplicity.
-/
protected def Subset (l₁ l₂ : List α) := a : α, a l₁ a l₂
instance : HasSubset (List α) := List.Subset
instance [DecidableEq α] : DecidableRel (Subset : List α List α Prop) :=
fun _ _ => decidableBAll _ _
/-! ### Sublist and isSublist -/
/-- `l₁ <+ l₂`, or `Sublist l₁ l₂`, says that `l₁` is a (non-contiguous) subsequence of `l₂`. -/
inductive Sublist {α} : List α List α Prop
/-- the base case: `[]` is a sublist of `[]` -/
| slnil : Sublist [] []
/-- If `l₁` is a subsequence of `l₂`, then it is also a subsequence of `a :: l₂`. -/
| cons a : Sublist l₁ l₂ Sublist l₁ (a :: l₂)
/-- If `l₁` is a subsequence of `l₂`, then `a :: l₁` is a subsequence of `a :: l₂`. -/
| cons₂ a : Sublist l₁ l₂ Sublist (a :: l₁) (a :: l₂)
@[inherit_doc] scoped infixl:50 " <+ " => Sublist
/-- True if the first list is a potentially non-contiguous sub-sequence of the second list. -/
def isSublist [BEq α] : List α List α Bool
| [], _ => true
| _, [] => false
| l₁@(hd₁::tl₁), hd₂::tl₂ =>
if hd₁ == hd₂
then tl₁.isSublist tl₂
else l₁.isSublist tl₂
/-! ### rotateLeft -/
/--
@@ -875,6 +919,8 @@ def rotateLeft (xs : List α) (n : Nat := 1) : List α :=
let e := xs.drop n
e ++ b
@[simp] theorem rotateLeft_nil : ([] : List α).rotateLeft n = [] := rfl
/-! ### rotateRight -/
/--
@@ -894,6 +940,57 @@ def rotateRight (xs : List α) (n : Nat := 1) : List α :=
let e := xs.drop n
e ++ b
@[simp] theorem rotateRight_nil : ([] : List α).rotateRight n = [] := rfl
/-! ## Pairwise, Nodup -/
section Pairwise
variable (R : α α Prop)
/--
`Pairwise R l` means that all the elements with earlier indexes are
`R`-related to all the elements with later indexes.
```
Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3
```
For example if `R = (·≠·)` then it asserts `l` has no duplicates,
and if `R = (·<·)` then it asserts that `l` is (strictly) sorted.
-/
inductive Pairwise : List α Prop
/-- All elements of the empty list are vacuously pairwise related. -/
| nil : Pairwise []
/-- `a :: l` is `Pairwise R` if `a` `R`-relates to every element of `l`,
and `l` is `Pairwise R`. -/
| cons : {a : α} {l : List α}, ( a', a' l R a a') Pairwise l Pairwise (a :: l)
attribute [simp] Pairwise.nil
variable {R}
@[simp] theorem pairwise_cons : Pairwise R (a::l) ( a', a' l R a a') Pairwise R l :=
fun | .cons h₁ h₂ => h₁, h₂, fun h₁, h₂ => h₂.cons h₁
instance instDecidablePairwise [DecidableRel R] :
(l : List α) Decidable (Pairwise R l)
| [] => isTrue .nil
| hd :: tl =>
match instDecidablePairwise tl with
| isTrue ht =>
match decidableBAll (R hd) tl with
| isFalse hf => isFalse fun hf' => hf (pairwise_cons.1 hf').1
| isTrue ht' => isTrue <| pairwise_cons.mpr (And.intro ht' ht)
| isFalse hf => isFalse fun | .cons _ ih => hf ih
end Pairwise
/-- `Nodup l` means that `l` has no duplicates, that is, any element appears at most
once in the List. It is defined as `Pairwise (≠)`. -/
def Nodup : List α Prop := Pairwise (· ·)
instance nodupDecidable [DecidableEq α] : l : List α, Decidable (Nodup l) :=
instDecidablePairwise
/-! ## Manipulating elements -/
/-! ### replace -/
@@ -906,13 +1003,13 @@ def rotateRight (xs : List α) (n : Nat := 1) : List α :=
-/
def replace [BEq α] : List α α α List α
| [], _, _ => []
| a::as, b, c => match a == b with
| a::as, b, c => match b == a with
| true => c::as
| false => a :: replace as b c
@[simp] theorem replace_nil [BEq α] : ([] : List α).replace a b = [] := rfl
theorem replace_cons [BEq α] {a : α} :
(a::as).replace b c = match a == b with | true => c::as | false => a :: replace as b c :=
(a::as).replace b c = match b == a with | true => c::as | false => a :: replace as b c :=
rfl
/-! ### insert -/
@@ -939,6 +1036,11 @@ theorem erase_cons [BEq α] (a b : α) (l : List α) :
(b :: l).erase a = if b == a then l else b :: l.erase a := by
simp only [List.erase]; split <;> simp_all
/-- `eraseP p l` removes the first element of `l` satisfying the predicate `p`. -/
def eraseP (p : α Bool) : List α List α
| [] => []
| a :: l => bif p a then l else a :: eraseP p l
/-! ### eraseIdx -/
/--

View File

@@ -258,7 +258,7 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
unless `b` is not found in `xs` in which case it returns `l`. -/
@[specialize] go : List α Array α List α
| [], _ => l
| a::as, acc => bif a == b then acc.toListAppend (c::as) else go as (acc.push a)
| a::as, acc => bif b == a then acc.toListAppend (c::as) else go as (acc.push a)
@[csimp] theorem replace_eq_replaceTR : @List.replace = @replaceTR := by
funext α _ l b c; simp [replaceTR]
@@ -295,6 +295,24 @@ theorem replicateTR_loop_eq : ∀ n, replicateTR.loop a n acc = replicate n a ++
· rw [IH] <;> simp_all
· simp
/-- Tail-recursive version of `eraseP`. -/
@[inline] def erasePTR (p : α Bool) (l : List α) : List α := go l #[] where
/-- Auxiliary for `erasePTR`: `erasePTR.go p l xs acc = acc.toList ++ eraseP p xs`,
unless `xs` does not contain any elements satisfying `p`, where it returns `l`. -/
@[specialize] go : List α Array α List α
| [], _ => l
| a :: l, acc => bif p a then acc.toListAppend l else go l (acc.push a)
@[csimp] theorem eraseP_eq_erasePTR : @eraseP = @erasePTR := by
funext α p l; simp [erasePTR]
let rec go (acc) : xs, l = acc.data ++ xs
erasePTR.go p l xs acc = acc.data ++ xs.eraseP p
| [] => fun h => by simp [erasePTR.go, eraseP, h]
| x::xs => by
simp [erasePTR.go, eraseP]; cases p x <;> simp
· intro h; rw [go _ xs]; {simp}; simp [h]
exact (go #[] _ rfl).symm
/-! ### eraseIdx -/
/-- Tail recursive version of `List.eraseIdx`. -/

File diff suppressed because it is too large Load Diff

View File

@@ -39,10 +39,15 @@ theorem take_take : ∀ (n m) (l : List α), take n (take m l) = take (min n m)
| succ n, succ m, a :: l => by
simp only [take, succ_min_succ, take_take n m l]
theorem take_replicate (a : α) : n m : Nat, take n (replicate m a) = replicate (min n m) a
@[simp] theorem take_replicate (a : α) : n m : Nat, take n (replicate m a) = replicate (min n m) a
| n, 0 => by simp [Nat.min_zero]
| 0, m => by simp [Nat.zero_min]
| succ n, succ m => by simp [succ_min_succ, take_replicate]
| succ n, succ m => by simp [replicate_succ, succ_min_succ, take_replicate]
@[simp] theorem drop_replicate (a : α) : n m : Nat, drop n (replicate m a) = replicate (m - n) a
| n, 0 => by simp
| 0, m => by simp
| succ n, succ m => by simp [replicate_succ, succ_sub_succ, drop_replicate]
/-- Taking the first `n` elements in `l₁ ++ l₂` is the same as appending the first `n` elements
of `l₁` to the first `n - l₁.length` elements of `l₂`. -/
@@ -97,7 +102,7 @@ theorem get_take' (L : List α) {j i} :
theorem getElem?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
(l.take n)[m]? = none :=
getElem?_eq_none.mpr <| Nat.le_trans (length_take_le _ _) h
getElem?_eq_none <| Nat.le_trans (length_take_le _ _) h
@[deprecated getElem?_take_eq_none (since := "2024-06-12")]
theorem get?_take_eq_none {l : List α} {n m : Nat} (h : n m) :
@@ -115,6 +120,43 @@ theorem get?_take_eq_if {l : List α} {n m : Nat} :
(l.take n).get? m = if m < n then l.get? m else none := by
simp [getElem?_take_eq_if]
theorem head?_take {l : List α} {n : Nat} :
(l.take n).head? = if n = 0 then none else l.head? := by
simp [head?_eq_getElem?, getElem?_take_eq_if]
split
· rw [if_neg (by omega)]
· rw [if_pos (by omega)]
theorem head_take {l : List α} {n : Nat} (h : l.take n []) :
(l.take n).head h = l.head (by simp_all) := by
apply Option.some_inj.1
rw [ head?_eq_head, head?_eq_head, head?_take, if_neg]
simp_all
theorem getLast?_take {l : List α} : (l.take n).getLast? = if n = 0 then none else l[n - 1]?.or l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_take_eq_if, length_take]
split
· rw [if_neg (by omega)]
rw [Nat.min_def]
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [ getLast?_eq_getElem?, getElem?_eq_none (by omega)]
simp
· rw [if_pos]
omega
theorem getLast_take {l : List α} (h : l.take n []) :
(l.take n).getLast h = l[n - 1]?.getD (l.getLast (by simp_all)) := by
rw [getLast_eq_getElem, getElem_take']
simp [length_take, Nat.min_def]
simp at h
split
· rw [getElem?_eq_getElem (by omega)]
simp
· rw [getElem?_eq_none (by omega), getLast_eq_getElem]
simp
@[simp]
theorem take_eq_take :
{l : List α} {m n : Nat}, l.take m = l.take n min m l.length = min n l.length
@@ -240,6 +282,31 @@ theorem getElem?_drop (L : List α) (i j : Nat) : (L.drop i)[j]? = L[i + j]? :=
theorem get?_drop (L : List α) (i j : Nat) : get? (L.drop i) j = get? L (i + j) := by
simp
theorem head?_drop (l : List α) (n : Nat) :
(l.drop n).head? = l[n]? := by
rw [head?_eq_getElem?, getElem?_drop, Nat.add_zero]
theorem head_drop {l : List α} {n : Nat} (h : l.drop n []) :
(l.drop n).head h = l[n]'(by simp_all) := by
have w : n < l.length := length_lt_of_drop_ne_nil h
simpa [head?_eq_head, getElem?_eq_getElem, h, w] using head?_drop l n
theorem getLast?_drop {l : List α} : (l.drop n).getLast? = if l.length n then none else l.getLast? := by
rw [getLast?_eq_getElem?, getElem?_drop]
rw [length_drop]
split
· rw [getElem?_eq_none (by omega)]
· rw [getLast?_eq_getElem?]
congr
omega
theorem getLast_drop {l : List α} (h : l.drop n []) :
(l.drop n).getLast h = l.getLast (ne_nil_of_length_pos (by simp at h; omega)) := by
simp only [ne_eq, drop_eq_nil_iff_le] at h
apply Option.some_inj.1
simp only [ getLast?_eq_getLast, getLast?_drop, ite_eq_right_iff]
omega
theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
l.set n a = if n < l.length then l.take n ++ a :: l.drop (n + 1) else l := by
split <;> rename_i h
@@ -267,6 +334,15 @@ theorem set_eq_take_append_cons_drop {l : List α} {n : Nat} {a : α} :
· rw [set_eq_of_length_le]
omega
theorem exists_of_set {n : Nat} {a' : α} {l : List α} (h : n < l.length) :
l₁ l₂, l = l₁ ++ l[n] :: l₂ l₁.length = n l.set n a' = l₁ ++ a' :: l₂ := by
refine l.take n, l.drop (n + 1), by simp, length_take_of_le (Nat.le_of_lt h), ?_
simp [set_eq_take_append_cons_drop, h]
theorem drop_set_of_lt (a : α) {n m : Nat} (l : List α)
(hnm : n < m) : drop m (l.set n a) = l.drop m :=
ext_getElem? fun k => by simpa only [getElem?_drop] using getElem?_set_ne (by omega)
theorem drop_take : (m n : Nat) (l : List α), drop n (take m l) = take (m - n) (drop n l)
| 0, _, _ => by simp
| _, 0, _ => by simp
@@ -298,6 +374,32 @@ theorem take_reverse {α} {xs : List α} (n : Nat) (h : n ≤ xs.length) :
@[deprecated (since := "2024-06-15")] abbrev reverse_take := @take_reverse
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_replicate (n) (a : α) : rotateLeft (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (n + 1) % m + min ((n + 1) % m) m = m by
simpa [rotateLeft]
intro h
rw [Nat.min_eq_left (Nat.le_of_lt (Nat.mod_lt _ (by omega)))]
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
omega
/-! ### rotateRight -/
@[simp] theorem rotateRight_replicate (n) (a : α) : rotateRight (replicate m a) n = replicate m a := by
cases n with
| zero => simp
| succ n =>
suffices 1 < m m - (m - (n + 1) % m) + min (m - (n + 1) % m) m = m by
simpa [rotateRight]
intro h
have : (n + 1) % m < m := Nat.mod_lt _ (by omega)
rw [Nat.min_eq_left (by omega)]
omega
/-! ### zipWith -/
@[simp] theorem length_zipWith (f : α β γ) (l₁ l₂) :
@@ -305,10 +407,98 @@ theorem take_reverse {α} {xs : List α} (n : Nat) (h : n ≤ xs.length) :
induction l₁ generalizing l₂ <;> cases l₂ <;>
simp_all [succ_min_succ, Nat.zero_min, Nat.min_zero]
theorem zipWith_eq_zipWith_take_min : (l₁ : List α) (l₂ : List β),
zipWith f l₁ l₂ = zipWith f (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zipWith_eq_zipWith_take_min l₁ l₂]
@[simp] theorem zipWith_replicate {a : α} {b : β} {m n : Nat} :
zipWith f (replicate m a) (replicate n b) = replicate (min m n) (f a b) := by
rw [zipWith_eq_zipWith_take_min]
simp
/-! ### zip -/
@[simp] theorem length_zip (l₁ : List α) (l₂ : List β) :
length (zip l₁ l₂) = min (length l₁) (length l₂) := by
simp [zip]
theorem zip_eq_zip_take_min : (l₁ : List α) (l₂ : List β),
zip l₁ l₂ = zip (l₁.take (min l₁.length l₂.length)) (l₂.take (min l₁.length l₂.length))
| [], _ => by simp
| _, [] => by simp
| a :: l₁, b :: l₂ => by simp [succ_min_succ, zip_eq_zip_take_min l₁ l₂]
@[simp] theorem zip_replicate {a : α} {b : β} {m n : Nat} :
zip (replicate m a) (replicate n b) = replicate (min m n) (a, b) := by
rw [zip_eq_zip_take_min]
simp
/-! ### minimum? -/
-- A specialization of `minimum?_eq_some_iff` to Nat.
theorem minimum?_eq_some_iff' {xs : List Nat} :
xs.minimum? = some a (a xs b xs, a b) :=
minimum?_eq_some_iff
(le_refl := Nat.le_refl)
(min_eq_or := fun _ _ => by omega)
(le_min_iff := fun _ _ _ => by omega)
-- This could be generalized,
-- but will first require further work on order typeclasses in the core repository.
theorem minimum?_cons' {a : Nat} {l : List Nat} :
(a :: l).minimum? = some (match l.minimum? with
| none => a
| some m => min a m) := by
rw [minimum?_eq_some_iff']
split <;> rename_i h m
· simp_all
· rw [minimum?_eq_some_iff'] at m
obtain m, le := m
rw [Nat.min_def]
constructor
· split
· exact mem_cons_self a l
· exact mem_cons_of_mem a m
· intro b m
cases List.mem_cons.1 m with
| inl => split <;> omega
| inr h =>
specialize le b h
split <;> omega
/-! ### maximum? -/
-- A specialization of `maximum?_eq_some_iff` to Nat.
theorem maximum?_eq_some_iff' {xs : List Nat} :
xs.maximum? = some a (a xs b xs, b a) :=
maximum?_eq_some_iff
(le_refl := Nat.le_refl)
(max_eq_or := fun _ _ => by omega)
(max_le_iff := fun _ _ _ => by omega)
-- This could be generalized,
-- but will first require further work on order typeclasses in the core repository.
theorem maximum?_cons' {a : Nat} {l : List Nat} :
(a :: l).maximum? = some (match l.maximum? with
| none => a
| some m => max a m) := by
rw [maximum?_eq_some_iff']
split <;> rename_i h m
· simp_all
· rw [maximum?_eq_some_iff'] at m
obtain m, le := m
rw [Nat.max_def]
constructor
· split
· exact mem_cons_of_mem a m
· exact mem_cons_self a l
· intro b m
cases List.mem_cons.1 m with
| inl => split <;> omega
| inr h =>
specialize le b h
split <;> omega
end List

View File

@@ -100,6 +100,7 @@ def blt (a b : Nat) : Bool :=
ble a.succ b
attribute [simp] Nat.zero_le
attribute [simp] Nat.not_lt_zero
/-! # Helper "packing" theorems -/
@@ -124,13 +125,8 @@ instance : LawfulBEq Nat where
eq_of_beq h := Nat.eq_of_beq_eq_true h
rfl := by simp [BEq.beq]
@[simp] theorem beq_eq_true_eq (a b : Nat) : ((a == b) = true) = (a = b) := propext <| Iff.intro eq_of_beq (fun h => by subst h; apply LawfulBEq.rfl)
@[simp] theorem not_beq_eq_true_eq (a b : Nat) : ((!(a == b)) = true) = ¬(a = b) :=
propext <| Iff.intro
(fun h₁ h₂ => by subst h₂; rw [LawfulBEq.rfl] at h₁; contradiction)
(fun h =>
have : ¬ ((a == b) = true) := fun h' => absurd (eq_of_beq h') h
by simp [this])
theorem beq_eq_true_eq (a b : Nat) : ((a == b) = true) = (a = b) := by simp
theorem not_beq_eq_true_eq (a b : Nat) : ((!(a == b)) = true) = ¬(a = b) := by simp
/-! # Nat.add theorems -/
@@ -278,7 +274,7 @@ theorem succ_sub_succ_eq_sub (n m : Nat) : succ n - succ m = n - m := by
| zero => exact rfl
| succ m ih => apply congrArg pred ih
@[simp] theorem pred_le : (n : Nat), pred n n
theorem pred_le : (n : Nat), pred n n
| zero => Nat.le.refl
| succ _ => le_succ _
@@ -288,7 +284,7 @@ theorem pred_lt : ∀ {n : Nat}, n ≠ 0 → pred n < n
theorem sub_one_lt : {n : Nat}, n 0 n - 1 < n := pred_lt
theorem sub_le (n m : Nat) : n - m n := by
@[simp] theorem sub_le (n m : Nat) : n - m n := by
induction m with
| zero => exact Nat.le_refl (n - 0)
| succ m ih => apply Nat.le_trans (pred_le (n - m)) ih
@@ -355,7 +351,7 @@ protected theorem pos_of_ne_zero {n : Nat} : n ≠ 0 → 0 < n := (eq_zero_or_po
theorem lt.base (n : Nat) : n < succ n := Nat.le_refl (succ n)
@[simp] theorem lt_succ_self (n : Nat) : n < succ n := lt.base n
theorem lt_succ_self (n : Nat) : n < succ n := lt.base n
@[simp] protected theorem lt_add_one (n : Nat) : n < n + 1 := lt.base n
@@ -638,6 +634,10 @@ theorem succ_lt_succ_iff : succ a < succ b ↔ a < b := ⟨lt_of_succ_lt_succ, s
theorem add_one_inj : a + 1 = b + 1 a = b := succ_inj'
theorem ne_add_one (n : Nat) : n n + 1 := fun h => by cases h
theorem add_one_ne (n : Nat) : n + 1 n := fun h => by cases h
theorem add_one_le_add_one_iff : a + 1 b + 1 a b := succ_le_succ_iff
theorem add_one_lt_add_one_iff : a + 1 < b + 1 a < b := succ_lt_succ_iff
@@ -705,8 +705,7 @@ protected theorem one_ne_zero : 1 ≠ (0 : Nat) :=
protected theorem zero_ne_one : 0 (1 : Nat) :=
fun h => Nat.noConfusion h
@[simp] theorem succ_ne_zero (n : Nat) : succ n 0 :=
fun h => Nat.noConfusion h
theorem succ_ne_zero (n : Nat) : succ n 0 := by simp
/-! # mul + order -/
@@ -814,8 +813,14 @@ theorem sub_one_lt_of_lt {n m : Nat} (h : m < n) : n - 1 < n :=
/-! # pred theorems -/
@[simp] protected theorem pred_zero : pred 0 = 0 := rfl
@[simp] protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
protected theorem pred_zero : pred 0 = 0 := rfl
protected theorem pred_succ (n : Nat) : pred n.succ = n := rfl
@[simp] protected theorem zero_sub_one : 0 - 1 = 0 := rfl
@[simp] protected theorem add_one_sub_one (n : Nat) : n + 1 - 1 = n := rfl
theorem sub_one_eq_self (n : Nat) : n - 1 = n n = 0 := by cases n <;> simp [ne_add_one]
theorem eq_self_sub_one (n : Nat) : n = n - 1 n = 0 := by cases n <;> simp [add_one_ne]
theorem succ_pred {a : Nat} (h : a 0) : a.pred.succ = a := by
induction a with

View File

@@ -86,7 +86,7 @@ noncomputable def div2Induction {motive : Nat → Sort u}
@[simp] theorem testBit_zero (x : Nat) : testBit x 0 = decide (x % 2 = 1) := by
cases mod_two_eq_zero_or_one x with | _ p => simp [testBit, p]
@[simp] theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
theorem testBit_succ (x i : Nat) : testBit x (succ i) = testBit (x/2) i := by
unfold testBit
simp [shiftRight_succ_inside]
@@ -310,6 +310,11 @@ theorem testBit_bool_to_nat (b : Bool) (i : Nat) :
Nat.div_div_eq_div_mul _ 2, one_div_two,
Nat.mod_eq_of_lt]
/-- `testBit 1 i` is true iff the index `i` equals 0. -/
theorem testBit_one_eq_true_iff_self_eq_zero {i : Nat} :
Nat.testBit 1 i = true i = 0 := by
cases i <;> simp
/-! ### bitwise -/
theorem testBit_bitwise
@@ -499,3 +504,27 @@ theorem mul_add_lt_is_or {b : Nat} (b_lt : b < 2^i) (a : Nat) : 2^i * a + b = 2^
@[simp] theorem testBit_shiftRight (x : Nat) : testBit (x >>> i) j = testBit x (i+j) := by
simp [testBit, shiftRight_add]
/-! ### le -/
theorem le_of_testBit {n m : Nat} (h : i, n.testBit i = true m.testBit i = true) : n m := by
induction n using div2Induction generalizing m
next n ih =>
have : n / 2 m / 2 := by
rcases n with (_|n)
· simp
· exact ih (Nat.succ_pos _) fun i => by simpa using h (i + 1)
rw [ div_add_mod n 2, div_add_mod m 2]
cases hn : n.testBit 0
· have hn2 : n % 2 = 0 := by simp at hn; omega
rw [hn2]
omega
· have hn2 : n % 2 = 1 := by simpa using hn
have hm2 : m % 2 = 1 := by simpa using h _ hn
omega
theorem and_le_left {n m : Nat} : n &&& m n :=
le_of_testBit (by simpa using fun i x _ => x)
theorem and_le_right {n m : Nat} : n &&& m m :=
le_of_testBit (by simp)

View File

@@ -251,10 +251,10 @@ theorem div_mul_le_self : ∀ (m n : Nat), m / n * n ≤ m
theorem div_lt_iff_lt_mul (Hk : 0 < k) : x / k < y x < y * k := by
rw [ Nat.not_le, Nat.not_le]; exact not_congr (le_div_iff_mul_le Hk)
@[simp] theorem add_div_right (x : Nat) {z : Nat} (H : 0 < z) : (x + z) / z = succ (x / z) := by
@[simp] theorem add_div_right (x : Nat) {z : Nat} (H : 0 < z) : (x + z) / z = (x / z) + 1 := by
rw [div_eq_sub_div H (Nat.le_add_left _ _), Nat.add_sub_cancel]
@[simp] theorem add_div_left (x : Nat) {z : Nat} (H : 0 < z) : (z + x) / z = succ (x / z) := by
@[simp] theorem add_div_left (x : Nat) {z : Nat} (H : 0 < z) : (z + x) / z = (x / z) + 1 := by
rw [Nat.add_comm, add_div_right x H]
theorem add_mul_div_left (x z : Nat) {y : Nat} (H : 0 < y) : (x + y * z) / y = x / y + z := by
@@ -285,7 +285,7 @@ theorem add_mul_div_right (x y : Nat) {z : Nat} (H : 0 < z) : (x + y * z) / z =
@[simp] theorem mul_mod_left (m n : Nat) : (m * n) % n = 0 := by
rw [Nat.mul_comm, mul_mod_right]
protected theorem div_eq_of_lt_le (lo : k * n m) (hi : m < succ k * n) : m / n = k :=
protected theorem div_eq_of_lt_le (lo : k * n m) (hi : m < (k + 1) * n) : m / n = k :=
have npos : 0 < n := (eq_zero_or_pos _).resolve_left fun hn => by
rw [hn, Nat.mul_zero] at hi lo; exact absurd lo (Nat.not_le_of_gt hi)
Nat.le_antisymm
@@ -307,7 +307,7 @@ theorem sub_mul_div (x n p : Nat) (h₁ : n*p ≤ x) : (x - n*p) / n = x / n - p
rw [sub_succ, IH h₂, div_eq_sub_div h₀ h₃]
simp [Nat.pred_succ, mul_succ, Nat.sub_sub]
theorem mul_sub_div (x n p : Nat) (h₁ : x < n*p) : (n * p - succ x) / n = p - succ (x / n) := by
theorem mul_sub_div (x n p : Nat) (h₁ : x < n*p) : (n * p - (x + 1)) / n = p - ((x / n) + 1) := by
have npos : 0 < n := (eq_zero_or_pos _).resolve_left fun n0 => by
rw [n0, Nat.zero_mul] at h₁; exact not_lt_zero _ h₁
apply Nat.div_eq_of_lt_le

View File

@@ -115,8 +115,6 @@ protected theorem add_sub_cancel_right (n m : Nat) : (n + m) - m = n := Nat.add_
theorem succ_sub_one (n) : succ n - 1 = n := rfl
protected theorem add_one_sub_one (n : Nat) : (n + 1) - 1 = n := rfl
protected theorem one_add_sub_one (n : Nat) : (1 + n) - 1 = n := Nat.add_sub_cancel_left 1 _
protected theorem sub_sub_self {n m : Nat} (h : m n) : n - (n - m) = m :=
@@ -212,13 +210,19 @@ instance : Std.IdempotentOp (α := Nat) min := ⟨Nat.min_self⟩
@[simp] protected theorem min_zero (a) : min a 0 = 0 := Nat.min_eq_right (Nat.zero_le _)
protected theorem min_assoc : (a b c : Nat), min (min a b) c = min a (min b c)
@[simp] protected theorem min_assoc : (a b c : Nat), min (min a b) c = min a (min b c)
| 0, _, _ => by rw [Nat.zero_min, Nat.zero_min, Nat.zero_min]
| _, 0, _ => by rw [Nat.zero_min, Nat.min_zero, Nat.zero_min]
| _, _, 0 => by rw [Nat.min_zero, Nat.min_zero, Nat.min_zero]
| _+1, _+1, _+1 => by simp only [Nat.succ_min_succ]; exact congrArg succ <| Nat.min_assoc ..
instance : Std.Associative (α := Nat) min := Nat.min_assoc
@[simp] protected theorem min_self_assoc {m n : Nat} : min m (min m n) = min m n := by
rw [ Nat.min_assoc, Nat.min_self]
@[simp] protected theorem min_self_assoc' {m n : Nat} : min n (min m n) = min n m := by
rw [Nat.min_comm m n, Nat.min_assoc, Nat.min_self]
protected theorem sub_sub_eq_min : (a b : Nat), a - (a - b) = min a b
| 0, _ => by rw [Nat.zero_sub, Nat.zero_min]
| _, 0 => by rw [Nat.sub_zero, Nat.sub_self, Nat.min_zero]

View File

@@ -583,8 +583,6 @@ theorem PolyCnstr.denote_mul (ctx : Context) (k : Nat) (c : PolyCnstr) : (c.mul
have : k 0 k + 1 1 := by intro h; match k with | 0 => contradiction | k+1 => simp [Nat.succ.injEq]
have : ¬ (k == 0) (k + 1 == 1) = false := fun h => beq_false_of_ne (this (ne_of_beq_false (Bool.of_not_eq_true h)))
have : ¬ ((k + 1 == 0) = true) := fun h => absurd (eq_of_beq h) (Nat.succ_ne_zero k)
have : (1 == (0 : Nat)) = false := rfl
have : (1 == (1 : Nat)) = true := rfl
by_cases he : eq = true <;> simp [he, PolyCnstr.mul, PolyCnstr.denote, Poly.denote_le, Poly.denote_eq]
<;> by_cases hk : k == 0 <;> (try simp [eq_of_beq hk]) <;> simp [*] <;> apply Iff.intro <;> intro h
· exact Nat.eq_of_mul_eq_mul_left (Nat.zero_lt_succ _) h

View File

@@ -19,6 +19,7 @@ def getM [Alternative m] : Option α → m α
| some a => pure a
@[deprecated getM (since := "2024-04-17")]
-- `[Monad m]` is not needed here.
def toMonad [Monad m] [Alternative m] : Option α m α := getM
/-- Returns `true` on `some x` and `false` on `none`. -/
@@ -26,7 +27,7 @@ def toMonad [Monad m] [Alternative m] : Option α → m α := getM
| some _ => true
| none => false
@[deprecated isSome, inline] def toBool : Option α Bool := isSome
@[deprecated isSome (since := "2024-04-17"), inline] def toBool : Option α Bool := isSome
/-- Returns `true` on `none` and `false` on `some x`. -/
@[inline] def isNone : Option α Bool
@@ -80,7 +81,9 @@ theorem map_id : (Option.map id : Option α → Option α) = id :=
| none => false
/--
Implementation of `OrElse`'s `<|>` syntax for `Option`.
Implementation of `OrElse`'s `<|>` syntax for `Option`. If the first argument is `some a`, returns
`some a`, otherwise evaluates and returns the second argument. See also `or` for a version that is
strict in the second argument.
-/
@[always_inline, macro_inline] protected def orElse : Option α (Unit Option α) Option α
| some a, _ => some a
@@ -89,6 +92,12 @@ Implementation of `OrElse`'s `<|>` syntax for `Option`.
instance : OrElse (Option α) where
orElse := Option.orElse
/-- If the first argument is `some a`, returns `some a`, otherwise returns the second argument.
This is similar to `<|>`/`orElse`, but it is strict in the second argument. -/
@[always_inline, macro_inline] def or : Option α Option α Option α
| some a, _ => some a
| none, b => b
@[inline] protected def lt (r : α α Prop) : Option α Option α Prop
| none, some _ => True
| some x, some y => r x y
@@ -119,7 +128,7 @@ def merge (fn : ααα) : Option α → Option α → Option α
/-- An elimination principle for `Option`. It is a nondependent version of `Option.recOn`. -/
@[simp, inline] protected def elim : Option α β (α β) β
@[inline] protected def elim : Option α β (α β) β
| some x, _, f => f x
| none, y, _ => y

View File

@@ -26,7 +26,7 @@ instance : Membership α (Option α) := ⟨fun a b => b = some a⟩
instance [DecidableEq α] (j : α) (o : Option α) : Decidable (j o) :=
inferInstanceAs <| Decidable (o = some j)
theorem isNone_iff_eq_none {o : Option α} : o.isNone o = none :=
@[simp] theorem isNone_iff_eq_none {o : Option α} : o.isNone o = none :=
Option.eq_none_of_isNone, fun e => e.symm rfl
theorem some_inj {a b : α} : some a = some b a = b := by simp; rfl

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.Option.BasicAux
import Init.Data.Option.Instances
import Init.Classical
import Init.Ext
@@ -41,6 +42,21 @@ theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.get
theorem getD_eq_iff {o : Option α} {a b} : o.getD a = b (o = some b o = none a = b) := by
cases o <;> simp
@[simp] theorem get!_none [Inhabited α] : (none : Option α).get! = default := rfl
@[simp] theorem get!_some [Inhabited α] {a : α} : (some a).get! = a := rfl
theorem get_eq_get! [Inhabited α] : (o : Option α) {h : o.isSome} o.get h = o.get!
| some _, _ => rfl
theorem get_eq_getD {fallback : α} : (o : Option α) {h : o.isSome} o.get h = o.getD fallback
| some _, _ => rfl
theorem some_get! [Inhabited α] : (o : Option α) o.isSome some (o.get!) = o
| some _, _ => rfl
theorem get!_eq_getD_default [Inhabited α] (o : Option α) : o.get! = o.getD default := rfl
theorem mem_unique {o : Option α} {a b : α} (ha : a o) (hb : b o) : a = b :=
some.inj <| ha hb
@@ -66,7 +82,7 @@ theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by cases x <;> sim
cases a <;> simp
theorem eq_some_iff_get_eq : o = some a h : o.isSome, o.get h = a := by
cases o <;> simp; nofun
cases o <;> simp
theorem eq_some_of_isSome : {o : Option α} (h : o.isSome), o = some (o.get h)
| some _, _ => rfl
@@ -145,6 +161,12 @@ theorem map_eq_some : f <$> x = some b ↔ ∃ a, x = some a ∧ f a = b := map_
@[simp] theorem map_eq_none' : x.map f = none x = none := by
cases x <;> simp only [map_none', map_some', eq_self_iff_true]
theorem isSome_map {x : Option α} : (f <$> x).isSome = x.isSome := by
cases x <;> simp
@[simp] theorem isSome_map' {x : Option α} : (x.map f).isSome = x.isSome := by
cases x <;> simp
theorem map_eq_none : f <$> x = none x = none := map_eq_none'
theorem map_eq_bind {x : Option α} : x.map f = x.bind (some f) := by
@@ -168,6 +190,9 @@ theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘
theorem mem_map_of_mem (g : α β) (h : a x) : g a Option.map g x := h.symm map_some' ..
@[simp] theorem filter_none (p : α Bool) : none.filter p = none := rfl
theorem filter_some : Option.filter p (some a) = if p a then some a else none := rfl
theorem bind_map_comm {α β} {x : Option (Option α)} {f : α β} :
x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp
@@ -208,9 +233,9 @@ theorem liftOrGet_eq_or_eq {f : ααα} (h : ∀ a b, f a b = a f
@[simp] theorem liftOrGet_some_some {f} {a b : α} :
liftOrGet f (some a) (some b) = f a b := rfl
theorem elim_none (x : β) (f : α β) : none.elim x f = x := rfl
@[simp] theorem elim_none (x : β) (f : α β) : none.elim x f = x := rfl
theorem elim_some (x : β) (f : α β) (a : α) : (some a).elim x f = f a := rfl
@[simp] theorem elim_some (x : β) (f : α β) (a : α) : (some a).elim x f = f a := rfl
@[simp] theorem getD_map (f : α β) (x : α) (o : Option α) :
(o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl
@@ -236,3 +261,46 @@ end
@[simp] theorem toList_some (a : α) : (a : Option α).toList = [a] := rfl
@[simp] theorem toList_none (α : Type _) : (none : Option α).toList = [] := rfl
@[simp] theorem or_some : (some a).or o = some a := rfl
@[simp] theorem none_or : none.or o = o := rfl
theorem or_eq_bif : or o o' = bif o.isSome then o else o' := by
cases o <;> rfl
@[simp] theorem isSome_or : (or o o').isSome = (o.isSome || o'.isSome) := by
cases o <;> rfl
@[simp] theorem isNone_or : (or o o').isNone = (o.isNone && o'.isNone) := by
cases o <;> rfl
@[simp] theorem or_eq_none : or o o' = none o = none o' = none := by
cases o <;> simp
theorem or_eq_some : or o o' = some a o = some a (o = none o' = some a) := by
cases o <;> simp
theorem or_assoc : or (or o₁ o₂) o₃ = or o₁ (or o₂ o₃) := by
cases o₁ <;> cases o₂ <;> rfl
instance : Std.Associative (or (α := α)) := @or_assoc _
@[simp]
theorem or_none : or o none = o := by
cases o <;> rfl
instance : Std.LawfulIdentity (or (α := α)) none where
left_id := @none_or _
right_id := @or_none _
@[simp]
theorem or_self : or o o = o := by
cases o <;> rfl
instance : Std.IdempotentOp (or (α := α)) := @or_self _
theorem or_eq_orElse : or o o' = o.orElse (fun _ => o') := by
cases o <;> rfl
theorem map_or : f <$> or o o' = (f <$> o).or (f <$> o') := by
cases o <;> rfl
theorem map_or' : (or o o').map f = (o.map f).or (o'.map f) := by
cases o <;> rfl

View File

@@ -230,7 +230,7 @@ protected def Int.repr : Int → String
| negSucc m => "-" ++ Nat.repr (succ m)
instance : Repr Int where
reprPrec i _ := i.repr
reprPrec i prec := if i < 0 then Repr.addAppParen i.repr prec else i.repr
def hexDigitRepr (n : Nat) : String :=
String.singleton <| Nat.digitChar n

View File

@@ -69,7 +69,7 @@ where
decreasing_by exact Nat.sub_lt_sub_left _ (Nat.lt_add_of_pos_right c.utf8Size_pos)
/-- Converts a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`. -/
@[extern "lean_string_from_utf8"]
@[extern "lean_string_from_utf8_unchecked"]
def fromUTF8 (a : @& ByteArray) (h : validateUTF8 a) : String :=
loop 0 ""
where

View File

@@ -7,3 +7,4 @@ prelude
import Init.Data.UInt.Basic
import Init.Data.UInt.Log2
import Init.Data.UInt.Lemmas
import Init.Data.UInt.Bitwise

View File

@@ -0,0 +1,24 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
prelude
import Init.Data.UInt.Basic
import Init.Data.Fin.Bitwise
set_option hygiene false in
macro "declare_bitwise_uint_theorems" typeName:ident : command =>
`(
namespace $typeName
@[simp] protected theorem and_toNat (a b : $typeName) : (a &&& b).toNat = a.toNat &&& b.toNat := Fin.and_val ..
end $typeName
)
declare_bitwise_uint_theorems UInt8
declare_bitwise_uint_theorems UInt16
declare_bitwise_uint_theorems UInt32
declare_bitwise_uint_theorems UInt64
declare_bitwise_uint_theorems USize

View File

@@ -26,6 +26,8 @@ theorem add_def (a b : $typeName) : a + b = ⟨a.val + b.val⟩ := rfl
| _, _ => rfl
theorem val_eq_of_lt {a : Nat} : a < size ((ofNat a).val : Nat) = a :=
Nat.mod_eq_of_lt
theorem toNat_ofNat_of_lt {n : Nat} (h : n < size) : (ofNat n).toNat = n := by
rw [toNat, val_eq_of_lt h]
theorem le_def {a b : $typeName} : a b a.1 b.1 := .rfl
theorem lt_def {a b : $typeName} : a < b a.1 < b.1 := .rfl
@@ -48,6 +50,7 @@ protected theorem ne_of_lt {a b : $typeName} (h : a < b) : a ≠ b := ne_of_val_
@[simp] protected theorem zero_toNat : (0 : $typeName).toNat = 0 := Nat.zero_mod _
@[simp] protected theorem mod_toNat (a b : $typeName) : (a % b).toNat = a.toNat % b.toNat := Fin.mod_val ..
@[simp] protected theorem div_toNat (a b : $typeName) : (a / b).toNat = a.toNat / b.toNat := Fin.div_val ..
@[simp] protected theorem sub_toNat_of_le (a b : $typeName) : b a (a - b).toNat = a.toNat - b.toNat := Fin.sub_val_of_le
@[simp] protected theorem modn_toNat (a : $typeName) (b : Nat) : (a.modn b).toNat = a.toNat % b := Fin.modn_val ..
protected theorem modn_lt {m : Nat} : (u : $typeName), m > 0 toNat (u % m) < m
| u, h => Fin.modn_lt u h
@@ -55,6 +58,8 @@ open $typeName (modn_lt) in
protected theorem mod_lt (a b : $typeName) (h : 0 < b) : a % b < b := modn_lt _ (by simp [lt_def] at h; exact h)
protected theorem toNat.inj : {a b : $typeName}, a.toNat = b.toNat a = b
| _, _, _, _, rfl => rfl
protected theorem toNat_lt_size (a : $typeName) : a.toNat < size := a.1.2
@[simp] protected theorem ofNat_one : ofNat 1 = 1 := rfl
end $typeName
)

View File

@@ -10,58 +10,38 @@ import Init.RCases
namespace Lean
namespace Parser.Attr
/-- Registers an extensionality theorem.
/--
The flag `(iff := false)` prevents `ext` from generating an `ext_iff` lemma.
-/
syntax extIff := atomic("(" &"iff" " := " &"false" ")")
/--
The flag `(flat := false)` causes `ext` to not flatten parents' fields when generating an `ext` lemma.
-/
syntax extFlat := atomic("(" &"flat" " := " &"false" ")")
/--
Registers an extensionality theorem.
* When `@[ext]` is applied to a structure, it generates `.ext` and `.ext_iff` theorems and registers
them for the `ext` tactic.
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic.
* When `@[ext]` is applied to a theorem, the theorem is registered for the `ext` tactic, and it generates an `ext_iff` theorem.
The name of the theorem is from adding the suffix `_iff` to the theorem name.
* An optional natural number argument, e.g. `@[ext 9000]`, specifies a priority for the lemma. Higher-priority lemmas are chosen first, and the default is `1000`.
* The flag `@[ext (iff := false)]` prevents it from generating an `ext_iff` theorem.
* The flag `@[ext (flat := false)]` causes generated structure extensionality theorems to show inherited fields based on their representation,
rather than flattening the parents' fields into the lemma's equality hypotheses.
structures in the generated extensionality theorems. -/
syntax (name := ext) "ext" (" (" &"flat" " := " term ")")? (ppSpace prio)? : attr
-/
syntax (name := ext) "ext" (ppSpace extIff)? (ppSpace extFlat)? (ppSpace prio)? : attr
end Parser.Attr
-- TODO: rename this namespace?
-- Remark: `ext` has scoped syntax, Mathlib may depend on the actual namespace name.
namespace Elab.Tactic.Ext
/--
Creates the type of the extensionality theorem for the given structure,
elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
-/
scoped syntax (name := extType) "ext_type% " term:max ppSpace ident : term
/--
Creates the type of the iff-variant of the extensionality theorem for the given structure,
elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
-/
scoped syntax (name := extIffType) "ext_iff_type% " term:max ppSpace ident : term
/--
`declare_ext_theorems_for A` declares the extensionality theorems for the structure `A`.
These theorems state that two expressions with the structure type are equal if their fields are equal.
-/
syntax (name := declareExtTheoremFor) "declare_ext_theorems_for " ("(" &"flat" " := " term ") ")? ident (ppSpace prio)? : command
macro_rules | `(declare_ext_theorems_for $[(flat := $f)]? $struct:ident $(prio)?) => do
let flat := f.getD (mkIdent `true)
let names Macro.resolveGlobalName struct.getId.eraseMacroScopes
let name match names.filter (·.2.isEmpty) with
| [] => Macro.throwError s!"unknown constant {struct.getId}"
| [(name, _)] => pure name
| _ => Macro.throwError s!"ambiguous name {struct.getId}"
let extName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext"
let extIffName := mkIdentFrom struct (canonical := true) <| name.mkStr "ext_iff"
`(@[ext $(prio)?] protected theorem $extName:ident : ext_type% $flat $struct:ident :=
fun {..} {..} => by intros; subst_eqs; rfl
protected theorem $extIffName:ident : ext_iff_type% $flat $struct:ident :=
fun {..} {..} =>
fun h => by cases h; and_intros <;> rfl,
fun _ => by (repeat cases _ _); subst_eqs; rfl)
/--
Applies extensionality lemmas that are registered with the `@[ext]` attribute.
@@ -96,19 +76,8 @@ macro "ext1" xs:(colGt ppSpace rintroPat)* : tactic =>
end Elab.Tactic.Ext
end Lean
attribute [ext] Prod PProd Sigma PSigma
attribute [ext] funext propext Subtype.eq
@[ext] theorem Prod.ext : {x y : Prod α β} x.fst = y.fst x.snd = y.snd x = y
| _,_, _,_, rfl, rfl => rfl
@[ext] theorem PProd.ext : {x y : PProd α β} x.fst = y.fst x.snd = y.snd x = y
| _,_, _,_, rfl, rfl => rfl
@[ext] theorem Sigma.ext : {x y : Sigma β} x.fst = y.fst HEq x.snd y.snd x = y
| _,_, _,_, rfl, .rfl => rfl
@[ext] theorem PSigma.ext : {x y : PSigma β} x.fst = y.fst HEq x.snd y.snd x = y
| _,_, _,_, rfl, .rfl => rfl
@[ext] protected theorem PUnit.ext (x y : PUnit) : x = y := rfl
protected theorem Unit.ext (x y : Unit) : x = y := rfl

View File

@@ -7,22 +7,57 @@ prelude
import Init.Util
@[never_extract]
private def outOfBounds [Inhabited α] : α :=
def outOfBounds [Inhabited α] : α :=
panic! "index out of bounds"
/--
The class `GetElem coll idx elem valid` implements the `xs[i]` notation.
Given `xs[i]` with `xs : coll` and `i : idx`, Lean looks for an instance of
`GetElem coll idx elem valid` and uses this to infer the type of return
value `elem` and side conditions `valid` required to ensure `xs[i]` yields
a valid value of type `elem`.
theorem outOfBounds_eq_default [Inhabited α] : (outOfBounds : α) = default := rfl
/--
The classes `GetElem` and `GetElem?` implement lookup notation,
specifically `xs[i]`, `xs[i]?`, `xs[i]!`, and `xs[i]'p`.
Both classes are indexed by types `coll`, `idx`, and `elem` which are
the collection, the index, and the element types.
A single collection may support lookups with multiple index
types. The relation `valid` determines when the index is guaranteed to be
valid; lookups of valid indices are guaranteed not to fail.
For example, an instance for arrays looks like
`GetElem (Array α) Nat α (fun xs i => i < xs.size)`. In other words, given an
array `xs` and a natural number `i`, `xs[i]` will return an `α` when `valid xs i`
holds, which is true when `i` is less than the size of the array. `Array`
additionally supports indexing with `USize` instead of `Nat`.
In either case, because the bounds are checked at compile time,
no runtime check is required.
Given `xs[i]` with `xs : coll` and `i : idx`, Lean looks for an instance of
`GetElem coll idx elem valid` and uses this to infer the type of the return
value `elem` and side condition `valid` required to ensure `xs[i]` yields
a valid value of type `elem`. The tactic `get_elem_tactic` is
invoked to prove validity automatically. The `xs[i]'p` notation uses the
proof `p` to satisfy the validity condition.
If the proof `p` is long, it is often easier to place the
proof in the context using `have`, because `get_elem_tactic` tries
`assumption`.
For example, the instance for arrays looks like
`GetElem (Array α) Nat α (fun xs i => i < xs.size)`.
The proof side-condition `valid xs i` is automatically dispatched by the
`get_elem_tactic` tactic, which can be extended by adding more clauses to
`get_elem_tactic_trivial`.
`get_elem_tactic` tactic; this tactic can be extended by adding more clauses to
`get_elem_tactic_trivial` using `macro_rules`.
`xs[i]?` and `xs[i]!` do not impose a proof obligation; the former returns
an `Option elem`, with `none` signalling that the value isn't present, and
the latter returns `elem` but panics if the value isn't there, returning
`default : elem` based on the `Inhabited elem` instance.
These are provided by the `GetElem?` class, for which there is a default instance
generated from a `GetElem` class as long as `valid xs i` is always decidable.
Important instances include:
* `arr[i] : α` where `arr : Array α` and `i : Nat` or `i : USize`: does array
indexing with no runtime bounds check and a proof side goal `i < arr.size`.
* `l[i] : α` where `l : List α` and `i : Nat`: index into a list, with proof
side goal `i < l.length`.
-/
class GetElem (coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll idx Prop)) where
@@ -30,33 +65,10 @@ class GetElem (coll : Type u) (idx : Type v) (elem : outParam (Type w))
The syntax `arr[i]` gets the `i`'th element of the collection `arr`. If there
are proof side conditions to the application, they will be automatically
inferred by the `get_elem_tactic` tactic.
The actual behavior of this class is type-dependent, but here are some
important implementations:
* `arr[i] : α` where `arr : Array α` and `i : Nat` or `i : USize`: does array
indexing with no bounds check and a proof side goal `i < arr.size`.
* `l[i] : α` where `l : List α` and `i : Nat`: index into a list, with proof
side goal `i < l.length`.
* `stx[i] : Syntax` where `stx : Syntax` and `i : Nat`: get a syntax argument,
no side goal (returns `.missing` out of range)
There are other variations on this syntax:
* `arr[i]!` is syntax for `getElem! arr i` which should panic and return
`default : α` if the index is not valid.
* `arr[i]?` is syntax for `getElem?` which should return `none` if the index
is not valid.
* `arr[i]'h` is syntax for `getElem arr i h` with `h` an explicit proof the
index is valid.
-/
getElem (xs : coll) (i : idx) (h : valid xs i) : elem
getElem? (xs : coll) (i : idx) [Decidable (valid xs i)] : Option elem :=
if h : _ then some (getElem xs i h) else none
getElem! [Inhabited elem] (xs : coll) (i : idx) [Decidable (valid xs i)] : elem :=
match getElem? xs i with | some e => e | none => outOfBounds
export GetElem (getElem getElem! getElem?)
export GetElem (getElem)
@[inherit_doc getElem]
syntax:max term noWs "[" withoutPosition(term) "]" : term
@@ -66,6 +78,30 @@ macro_rules | `($x[$i]) => `(getElem $x $i (by get_elem_tactic))
syntax term noWs "[" withoutPosition(term) "]'" term:max : term
macro_rules | `($x[$i]'$h) => `(getElem $x $i $h)
/-- Helper function for implementation of `GetElem?.getElem?`. -/
abbrev decidableGetElem? [GetElem coll idx elem valid] (xs : coll) (i : idx) [Decidable (valid xs i)] :
Option elem :=
if h : valid xs i then some xs[i] else none
@[inherit_doc GetElem]
class GetElem? (coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll idx Prop)) extends GetElem coll idx elem valid where
/--
The syntax `arr[i]?` gets the `i`'th element of the collection `arr`,
if it is present (and wraps it in `some`), and otherwise returns `none`.
-/
getElem? : coll idx Option elem
/--
The syntax `arr[i]!` gets the `i`'th element of the collection `arr`,
if it is present, and otherwise panics at runtime and returns the `default` term
from `Inhabited elem`.
-/
getElem! [Inhabited elem] (xs : coll) (i : idx) : elem :=
match getElem? xs i with | some e => e | none => outOfBounds
export GetElem? (getElem? getElem!)
/--
The syntax `arr[i]?` gets the `i`'th element of the collection `arr` or
returns `none` if `i` is out of bounds.
@@ -78,32 +114,51 @@ panics `i` is out of bounds.
-/
macro:max x:term noWs "[" i:term "]" noWs "!" : term => `(getElem! $x $i)
instance (priority := low) [GetElem coll idx elem valid] [ xs i, Decidable (valid xs i)] :
GetElem? coll idx elem valid where
getElem? xs i := decidableGetElem? xs i
theorem getElem_congr_coll [GetElem coll idx elem valid] {c d : coll} {i : idx} {h : valid c i}
(h' : c = d) : c[i] = d[i]'(h' h) := by
cases h'; rfl
theorem getElem_congr [GetElem coll idx elem valid] {c : coll} {i j : idx} {h : valid c i}
(h' : i = j) : c[i] = c[j]'(h' h) := by
cases h'; rfl
class LawfulGetElem (cont : Type u) (idx : Type v) (elem : outParam (Type w))
(dom : outParam (cont idx Prop)) [ge : GetElem cont idx elem dom] : Prop where
(dom : outParam (cont idx Prop)) [ge : GetElem? cont idx elem dom] : Prop where
getElem?_def (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]? = if h : dom c i then some (c[i]'h) else none := by intros; eq_refl
getElem!_def [Inhabited elem] (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]! = match c[i]? with | some e => e | none => default := by intros; eq_refl
c[i]? = if h : dom c i then some (c[i]'h) else none := by
intros
try simp only [getElem?] <;> congr
getElem!_def [Inhabited elem] (c : cont) (i : idx) :
c[i]! = match c[i]? with | some e => e | none => default := by
intros
simp only [getElem!, getElem?, outOfBounds_eq_default]
export LawfulGetElem (getElem?_def getElem!_def)
theorem getElem?_pos [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
instance (priority := low) [GetElem coll idx elem valid] [ xs i, Decidable (valid xs i)] :
LawfulGetElem coll idx elem valid where
theorem getElem?_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) (h : dom c i) [Decidable (dom c i)] : c[i]? = some (c[i]'h) := by
rw [getElem?_def]
exact dif_pos h
theorem getElem?_neg [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
theorem getElem?_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) (h : ¬dom c i) [Decidable (dom c i)] : c[i]? = none := by
rw [getElem?_def]
exact dif_neg h
theorem getElem!_pos [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
theorem getElem!_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
[Inhabited elem] (c : cont) (i : idx) (h : dom c i) [Decidable (dom c i)] :
c[i]! = c[i]'h := by
simp only [getElem!_def, getElem?_def, h]
theorem getElem!_neg [GetElem cont idx elem dom] [LawfulGetElem cont idx elem dom]
theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
[Inhabited elem] (c : cont) (i : idx) (h : ¬dom c i) [Decidable (dom c i)] : c[i]! = default := by
simp only [getElem!_def, getElem?_def, h]
@@ -111,23 +166,22 @@ namespace Fin
instance instGetElemFinVal [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
getElem xs i h := getElem xs i.1 h
instance instGetElem?FinVal [GetElem? cont Nat elem dom] : GetElem? cont (Fin n) elem fun xs i => dom xs i where
getElem? xs i := getElem? xs i.val
getElem! xs i := getElem! xs i.val
instance [GetElem cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
instance [GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
LawfulGetElem cont (Fin n) elem fun xs i => dom xs i where
getElem?_def _c _i _d := h.getElem?_def ..
getElem!_def _c _i _d := h.getElem!_def ..
getElem!_def _c _i := h.getElem!_def ..
@[simp] theorem getElem_fin [GetElem Cont Nat Elem Dom] (a : Cont) (i : Fin n) (h : Dom a i) :
@[simp] theorem getElem_fin [GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) (h : Dom a i) :
a[i] = a[i.1] := rfl
@[simp] theorem getElem?_fin [h : GetElem Cont Nat Elem Dom] (a : Cont) (i : Fin n)
[Decidable (Dom a i)] : a[i]? = a[i.1]? := by rfl
@[simp] theorem getElem?_fin [h : GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) : a[i]? = a[i.1]? := by rfl
@[simp] theorem getElem!_fin [GetElem Cont Nat Elem Dom] (a : Cont) (i : Fin n)
[Decidable (Dom a i)] [Inhabited Elem] : a[i]! = a[i.1]! := rfl
@[simp] theorem getElem!_fin [GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) [Inhabited Elem] : a[i]! = a[i.1]! := rfl
macro_rules
| `(tactic| get_elem_tactic_trivial) => `(tactic| apply Fin.val_lt_of_le; get_elem_tactic_trivial; done)
@@ -139,17 +193,15 @@ namespace List
instance : GetElem (List α) Nat α fun as i => i < as.length where
getElem as i h := as.get i, h
instance : LawfulGetElem (List α) Nat α fun as i => i < as.length where
@[simp] theorem getElem_cons_zero (a : α) (as : List α) (h : 0 < (a :: as).length) : getElem (a :: as) 0 h = a := by
rfl
@[deprecated (since := "2024-6-12")] abbrev cons_getElem_zero := @getElem_cons_zero
@[deprecated (since := "2024-06-12")] abbrev cons_getElem_zero := @getElem_cons_zero
@[simp] theorem getElem_cons_succ (a : α) (as : List α) (i : Nat) (h : i + 1 < (a :: as).length) : getElem (a :: as) (i+1) h = getElem as i (Nat.lt_of_succ_lt_succ h) := by
rfl
@[deprecated (since := "2024-6-12")] abbrev cons_getElem_succ := @getElem_cons_succ
@[deprecated (since := "2024-06-12")] abbrev cons_getElem_succ := @getElem_cons_succ
theorem get_drop_eq_drop (as : List α) (i : Nat) (h : i < as.length) : as[i] :: as.drop (i+1) = as.drop i :=
match as, i with
@@ -163,8 +215,6 @@ namespace Array
instance : GetElem (Array α) Nat α fun xs i => i < xs.size where
getElem xs i h := xs.get i, h
instance : LawfulGetElem (Array α) Nat α fun xs i => i < xs.size where
end Array
namespace Lean.Syntax
@@ -172,6 +222,4 @@ namespace Lean.Syntax
instance : GetElem Syntax Nat Syntax fun _ _ => True where
getElem stx i _ := stx.getArg i
instance : LawfulGetElem Syntax Nat Syntax fun _ _ => True where
end Lean.Syntax

View File

@@ -1278,12 +1278,46 @@ def Occurrences.isAll : Occurrences → Bool
| all => true
| _ => false
/--
Controls which new mvars are turned in to goals by the `apply` tactic.
- `nonDependentFirst` mvars that don't depend on other goals appear first in the goal list.
- `nonDependentOnly` only mvars that don't depend on other goals are added to goal list.
- `all` all unassigned mvars are added to the goal list.
-/
-- TODO: Consider renaming to `Apply.NewGoals`
inductive ApplyNewGoals where
| nonDependentFirst | nonDependentOnly | all
/-- Configures the behaviour of the `apply` tactic. -/
-- TODO: Consider renaming to `Apply.Config`
structure ApplyConfig where
newGoals := ApplyNewGoals.nonDependentFirst
/--
If `synthAssignedInstances` is `true`, then `apply` will synthesize instance implicit arguments
even if they have assigned by `isDefEq`, and then check whether the synthesized value matches the
one inferred. The `congr` tactic sets this flag to false.
-/
synthAssignedInstances := true
/--
If `allowSynthFailures` is `true`, then `apply` will return instance implicit arguments
for which typeclass search failed as new goals.
-/
allowSynthFailures := false
/--
If `approx := true`, then we turn on `isDefEq` approximations. That is, we use
the `approxDefEq` combinator.
-/
approx : Bool := true
namespace Rewrite
abbrev NewGoals := ApplyNewGoals
structure Config where
transparency : TransparencyMode := TransparencyMode.reducible
transparency : TransparencyMode := .reducible
offsetCnstrs : Bool := true
occs : Occurrences := Occurrences.all
occs : Occurrences := .all
newGoals : NewGoals := .nonDependentFirst
end Rewrite

View File

@@ -42,23 +42,67 @@ inductive EtaStructMode where
namespace DSimp
/--
The configuration for `dsimp`.
Passed to `dsimp` using, for example, the `dsimp (config := {zeta := false})` syntax.
Implementation note: this structure is only used for processing the `(config := ...)` syntax, and it is not used internally.
It is immediately converted to `Lean.Meta.Simp.Config` by `Lean.Elab.Tactic.elabSimpConfig`.
-/
structure Config where
/-- `let x := v; e[x]` reduces to `e[v]`. -/
/--
When `true` (default: `true`), performs zeta reduction of let expressions.
That is, `let x := v; e[x]` reduces to `e[v]`.
See also `zetaDelta`.
-/
zeta : Bool := true
/--
When `true` (default: `true`), performs beta reduction of applications of `fun` expressions.
That is, `(fun x => e[x]) v` reduces to `e[v]`.
-/
beta : Bool := true
/--
TODO (currently unimplemented). When `true` (default: `true`), performs eta reduction for `fun` expressions.
That is, `(fun x => f x)` reduces to `f`.
-/
eta : Bool := true
/--
Configures how to determine definitional equality between two structure instances.
See documentation for `Lean.Meta.EtaStructMode`.
-/
etaStruct : EtaStructMode := .all
/--
When `true` (default: `true`), reduces `match` expressions applied to constructors.
-/
iota : Bool := true
/--
When `true` (default: `true`), reduces projections of structure constructors.
-/
proj : Bool := true
/--
When `true` (default: `false`), rewrites a proposition `p` to `True` or `False` by inferring
a `Decidable p` instance and reducing it.
-/
decide : Bool := false
/--
When `true` (default: `false`), unfolds definitions.
This can be enabled using the `simp!` syntax.
-/
autoUnfold : Bool := false
/-- If `failIfUnchanged := true`, then calls to `simp`, `dsimp`, or `simp_all`
will fail if they do not make progress. -/
/--
If `failIfUnchanged` is `true` (default: `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. -/
/--
If `unfoldPartialApp` is `true` (default: `false`), then calls to `simp`, `dsimp`, or `simp_all`
will unfold even partial applications of `f` when we request `f` to be unfolded.
-/
unfoldPartialApp : Bool := false
/-- Given a local context containing entry `x : t := e`, free variable `x` reduces to `e`. -/
/--
When `true` (default: `false`), local definitions are unfolded.
That is, given a local context containing entry `x : t := e`, the free variable `x` reduces to `e`.
-/
zetaDelta : Bool := false
deriving Inhabited, BEq
@@ -72,7 +116,7 @@ def defaultMaxSteps := 100000
The configuration for `simp`.
Passed to `simp` using, for example, the `simp (config := {contextual := true})` syntax.
See also `Lean.Meta.Simp.neutralConfig`.
See also `Lean.Meta.Simp.neutralConfig` and `Lean.Meta.DSimp.Config`.
-/
structure Config where
/--
@@ -174,6 +218,14 @@ structure Config where
to find candidate `simp` theorems. It approximates Lean 3 `simp` behavior.
-/
index : Bool := true
/--
When `true` (default: `false`), `simp` will **not** create a proof for a rewriting rule associated
with an `rfl`-theorem.
Rewriting rules are provided by users by annotating theorems with the attribute `@[simp]`.
If the proof of the theorem is just `rfl` (reflexivity), and `implicitDefEqProofs := true`, `simp`
will **not** create a proof term which is an application of the annotated theorem.
-/
implicitDefEqProofs : Bool := false
deriving Inhabited, BEq
-- Configuration object for `simp_all`

View File

@@ -267,6 +267,7 @@ syntax (name := rawNatLit) "nat_lit " num : term
@[inherit_doc] infixr:90 "" => Function.comp
@[inherit_doc] infixr:35 " × " => Prod
@[inherit_doc] infixr:35 " ×' " => PProd
@[inherit_doc] infix:50 " " => Dvd.dvd
@[inherit_doc] infixl:55 " ||| " => HOr.hOr
@@ -703,6 +704,28 @@ syntax (name := checkSimp) "#check_simp " term "~>" term : command
-/
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
/--
`#discr_tree_key t` prints the discrimination tree keys for a term `t` (or, if it is a single identifier, the type of that constant).
It uses the default configuration for generating keys.
For example,
```
#discr_tree_key (∀ {a n : Nat}, bar a (OfNat.ofNat n))
-- bar _ (@OfNat.ofNat Nat _ _)
#discr_tree_simp_key Nat.add_assoc
-- @HAdd.hAdd Nat Nat Nat _ (@HAdd.hAdd Nat Nat Nat _ _ _) _
```
`#discr_tree_simp_key` is similar to `#discr_tree_key`, but treats the underlying type
as one of a simp lemma, i.e. transforms it into an equality and produces the key of the
left-hand side.
-/
syntax (name := discrTreeKeyCmd) "#discr_tree_key " term : command
@[inherit_doc discrTreeKeyCmd]
syntax (name := discrTreeSimpKeyCmd) "#discr_tree_simp_key" term : command
/--
The `seal foo` command ensures that the definition of `foo` is sealed, meaning it is marked as `[irreducible]`.
This command is particularly useful in contexts where you want to prevent the reduction of `foo` in proofs.

View File

@@ -173,7 +173,7 @@ theorem mul_neg_left (xs ys : IntList) : (-xs) * ys = -(xs * ys) := by
attribute [local simp] add_def neg_def sub_def in
theorem sub_eq_add_neg (xs ys : IntList) : xs - ys = xs + (-ys) := by
induction xs generalizing ys with
| nil => simp; rfl
| nil => simp
| cons x xs ih =>
cases ys with
| nil => simp

View File

@@ -38,6 +38,10 @@ theorem ext {a b : LinearCombo} (w₁ : a.const = b.const) (w₂ : a.coeffs = b.
subst w₁; subst w₂
congr
/-- Check if a linear combination is an atom, i.e. the constant term is zero and there is exactly one nonzero coefficient, which is one. -/
def isAtom (a : LinearCombo) : Bool :=
a.const == 0 && (a.coeffs.filter (· == 1)).length == 1 && a.coeffs.all fun c => c == 0 || c == 1
/--
Evaluate a linear combination `⟨r, [c_1, …, c_k]⟩` at values `[v_1, …, v_k]` to obtain
`r + (c_1 * x_1 + (c_2 * x_2 + ... (c_k * x_k + 0))))`.

View File

@@ -488,9 +488,9 @@ attribute [unbox] Prod
/--
Similar to `Prod`, but `α` and `β` can be propositions.
You can use `α ×' β` as notation for `PProd α β`.
We use this type internally to automatically generate the `brecOn` recursor.
-/
@[pp_using_anonymous_constructor]
structure PProd (α : Sort u) (β : Sort v) where
/-- The first projection out of a pair. if `p : PProd α β` then `p.1 : α`. -/
fst : α
@@ -740,7 +740,7 @@ prove `p` given any element `x : α`, then `p` holds. Note that it is essential
that `p` is a `Prop` here; the version with `p` being a `Sort u` is equivalent
to `Classical.choice`.
-/
protected def Nonempty.elim {α : Sort u} {p : Prop} (h₁ : Nonempty α) (h₂ : α p) : p :=
protected theorem Nonempty.elim {α : Sort u} {p : Prop} (h₁ : Nonempty α) (h₂ : α p) : p :=
match h₁ with
| intro a => h₂ a
@@ -2329,9 +2329,6 @@ without running out of stack space.
def List.lengthTR (as : List α) : Nat :=
lengthTRAux as 0
@[simp] theorem List.length_cons {α} (a : α) (as : List α) : Eq (cons a as).length as.length.succ :=
rfl
/--
`as.get i` returns the `i`'th element of the list `as`.
This version of the function uses `i : Fin as.length` to ensure that it will
@@ -2976,7 +2973,7 @@ def MonadExcept.ofExcept [Monad m] [MonadExcept ε m] : Except ε α → m α
export MonadExcept (throw tryCatch ofExcept)
instance (ε : outParam (Type u)) (m : Type v Type w) [MonadExceptOf ε m] : MonadExcept ε m where
instance (ε : Type u) (m : Type v Type w) [MonadExceptOf ε m] : MonadExcept ε m where
throw := throwThe ε
tryCatch := tryCatchThe ε
@@ -3150,7 +3147,7 @@ instance (ρ : Type u) (m : Type u → Type v) [MonadWithReaderOf ρ m] : MonadW
instance {ρ : Type u} {m : Type u Type v} {n : Type u Type v} [MonadFunctor m n] [MonadWithReaderOf ρ m] : MonadWithReaderOf ρ n where
withReader f := monadMap (m := m) (withTheReader ρ f)
instance {ρ : Type u} {m : Type u Type v} [Monad m] : MonadWithReaderOf ρ (ReaderT ρ m) where
instance {ρ : Type u} {m : Type u Type v} : MonadWithReaderOf ρ (ReaderT ρ m) where
withReader f x := fun ctx => x (f ctx)
/--
@@ -3175,8 +3172,8 @@ class MonadStateOf (σ : semiOutParam (Type u)) (m : Type u → Type v) where
export MonadStateOf (set)
/--
Like `withReader`, but with `ρ` explicit. This is useful if a monad supports
`MonadWithReaderOf` for multiple different types `ρ`.
Like `get`, but with `σ` explicit. This is useful if a monad supports
`MonadStateOf` for multiple different types `σ`.
-/
abbrev getThe (σ : Type u) {m : Type u Type v} [MonadStateOf σ m] : m σ :=
MonadStateOf.get
@@ -3233,7 +3230,7 @@ def modify {σ : Type u} {m : Type u → Type v} [MonadState σ m] (f : σ
of the state. It is equivalent to `get <* modify f` but may be more efficient.
-/
@[always_inline, inline]
def getModify {σ : Type u} {m : Type u Type v} [MonadState σ m] [Monad m] (f : σ σ) : m σ :=
def getModify {σ : Type u} {m : Type u Type v} [MonadState σ m] (f : σ σ) : m σ :=
modifyGet fun s => (s, f s)
-- NOTE: The Ordering of the following two instances determines that the top-most `StateT` Monad layer

View File

@@ -253,6 +253,9 @@ end forall_congr
@[simp] theorem not_exists : (¬ x, p x) x, ¬p x := exists_imp
theorem forall_not_of_not_exists (h : ¬ x, p x) : x, ¬p x := not_exists.mp h
theorem not_exists_of_forall_not (h : x, ¬p x) : ¬ x, p x := not_exists.mpr h
theorem forall_and : ( x, p x q x) ( x, p x) ( x, q x) :=
fun h => fun x => (h x).1, fun x => (h x).2, fun h₁, h₂ x => h₁ x, h₂ x
@@ -292,6 +295,8 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_eq_left' : ( a, a' = a p a) p a' := by simp [@eq_comm _ a']
@[simp] theorem exists_eq_right' : ( a, p a a' = a) p a' := by simp [@eq_comm _ a']
@[simp] theorem forall_eq_or_imp : ( a, a = a' q a p a) p a' a, q a p a := by
simp only [or_imp, forall_and, forall_eq]
@@ -368,9 +373,6 @@ else isTrue fun h2 => absurd h2 h
theorem decide_eq_true_iff (p : Prop) [Decidable p] : (decide p = true) p := by simp
@[simp] theorem decide_eq_false_iff_not (p : Prop) {_ : Decidable p} : (decide p = false) ¬p :=
of_decide_eq_false, decide_eq_false
@[simp] theorem decide_eq_decide {p q : Prop} {_ : Decidable p} {_ : Decidable q} :
decide p = decide q (p q) :=
fun h => by rw [ decide_eq_true_iff p, h, decide_eq_true_iff], fun h => by simp [h]

View File

@@ -102,3 +102,11 @@ instance ShareCommonT.monadShareCommon [Monad m] : MonadShareCommon (ShareCommon
@[inline] def ShareCommonT.run [Monad m] (x : ShareCommonT σ m α) : m α := x.run' default
@[inline] def ShareCommonM.run (x : ShareCommonM σ α) : α := ShareCommonT.run x
/--
A more restrictive but efficient max sharing primitive.
Remark: it optimizes the number of RC operations, and the strategy for caching results.
-/
@[extern "lean_sharecommon_quick"]
def ShareCommon.shareCommon' (a : α) : α := a

View File

@@ -129,6 +129,7 @@ instance : Std.LawfulIdentity Or False where
@[simp] theorem iff_false (p : Prop) : (p False) = ¬p := propext (·.1), (·, False.elim)
@[simp] theorem false_iff (p : Prop) : (False p) = ¬p := propext (·.2), (False.elim, ·)
@[simp] theorem false_implies (p : Prop) : (False p) = True := eq_true False.elim
@[simp] theorem forall_false (p : False Prop) : ( h : False, p h) = True := eq_true (False.elim ·)
@[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
@@ -228,25 +229,22 @@ instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
@[simp] theorem Bool.not_true : (!true) = false := by decide
@[simp] theorem Bool.not_false : (!false) = true := by decide
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
@[simp] theorem Bool.not_beq_false (b : Bool) : (!(b == false)) = (b == true) := by cases b <;> rfl
@[simp] theorem beq_true (b : Bool) : (b == true) = b := by cases b <;> rfl
@[simp] theorem beq_false (b : Bool) : (b == false) = !b := by cases b <;> rfl
@[simp] theorem Bool.not_eq_true' (b : Bool) : ((!b) = true) = (b = false) := by cases b <;> simp
@[simp] theorem Bool.not_eq_false' (b : Bool) : ((!b) = false) = (b = true) := by cases b <;> simp
@[simp] theorem Bool.beq_to_eq (a b : Bool) :
(a == b) = (a = b) := by cases a <;> cases b <;> decide
@[simp] theorem Bool.not_beq_to_not_eq (a b : Bool) :
(!(a == b)) = ¬(a = b) := by cases a <;> cases b <;> decide
@[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_eq_false_iff_not {_ : Decidable p} : (decide p = false) ¬p :=
of_decide_eq_false, decide_eq_false
@[simp] theorem decide_not [g : Decidable p] [h : Decidable (Not p)] : decide (Not p) = !(decide p) := by
cases g <;> (rename_i gp; simp [gp]; rfl)
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by
cases h <;> (rename_i hp; simp [decide, hp])
@[simp] theorem not_decide_eq_true [h : Decidable p] : ((!decide p) = true) = ¬ p := by simp
@[simp] theorem heq_eq_eq (a b : α) : HEq a b = (a = b) := propext <| Iff.intro eq_of_heq heq_of_eq
@@ -254,10 +252,10 @@ instance : Std.Associative (· || ·) := ⟨Bool.or_assoc⟩
@[simp] theorem cond_false (a b : α) : cond false a b = b := rfl
@[simp] theorem beq_self_eq_true [BEq α] [LawfulBEq α] (a : α) : (a == a) = true := LawfulBEq.rfl
@[simp] theorem beq_self_eq_true' [DecidableEq α] (a : α) : (a == a) = true := by simp [BEq.beq]
theorem beq_self_eq_true' [DecidableEq α] (a : α) : (a == a) = true := by simp
@[simp] theorem bne_self_eq_false [BEq α] [LawfulBEq α] (a : α) : (a != a) = false := by simp [bne]
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp
@[simp] theorem decide_False : decide False = false := rfl
@[simp] theorem decide_True : decide True = true := rfl
@@ -283,7 +281,10 @@ These will both normalize to `a = b` with the first via `bne_eq_false_iff_eq`.
rw [bne, beq_iff_eq a b]
cases a == b <;> decide
/-# Nat -/
theorem Bool.beq_to_eq (a b : Bool) : (a == b) = (a = b) := by simp
theorem Bool.not_beq_to_not_eq (a b : Bool) : (!(a == b)) = ¬(a = b) := by simp
/- # Nat -/
@[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 rw [h]; decide

View File

@@ -712,8 +712,17 @@ structure Child (cfg : StdioConfig) where
@[extern "lean_io_process_spawn"] opaque spawn (args : SpawnArgs) : IO (Child args.toStdioConfig)
/--
Block until the child process has exited and return its exit code.
-/
@[extern "lean_io_process_child_wait"] opaque Child.wait {cfg : @& StdioConfig} : @& Child cfg IO UInt32
/--
Check whether the child has exited yet. If it hasn't return none, otherwise its exit code.
-/
@[extern "lean_io_process_child_try_wait"] opaque Child.tryWait {cfg : @& StdioConfig} : @& Child cfg
IO (Option UInt32)
/-- Terminates the child process using the SIGTERM signal or a platform analogue.
If the process was started using `SpawnArgs.setsid`, terminates the entire process group instead. -/
@[extern "lean_io_process_child_kill"] opaque Child.kill {cfg : @& StdioConfig} : @& Child cfg IO Unit
@@ -814,6 +823,10 @@ def set (tk : CancelToken) : BaseIO Unit :=
def isSet (tk : CancelToken) : BaseIO Bool :=
tk.ref.get
-- separate definition as otherwise no unboxed version is generated
@[export lean_io_cancel_token_is_set]
private def isSetExport := @isSet
end CancelToken
namespace FS

View File

@@ -267,7 +267,9 @@ syntax (name := case') "case' " sepBy1(caseArg, " | ") " => " tacticSeq : tactic
`next x₁ ... xₙ => tac` additionally renames the `n` most recent hypotheses with
inaccessible names to the given names.
-/
macro "next " args:binderIdent* " => " tac:tacticSeq : tactic => `(tactic| case _ $args* => $tac)
macro "next " args:binderIdent* arrowTk:" => " tac:tacticSeq : tactic =>
-- Limit ref variability for incrementality; see Note [Incremental Macros]
withRef arrowTk `(tactic| case _ $args* =>%$arrowTk $tac)
/-- `all_goals tac` runs `tac` on each goal, concatenating the resulting goals, if any. -/
syntax (name := allGoals) "all_goals " tacticSeq : tactic
@@ -371,7 +373,8 @@ reflexivity theorems (e.g., `Iff.rfl`).
macro "rfl" : tactic => `(tactic| case' _ => fail "The rfl tactic failed. Possible reasons:
- The goal is not a reflexive relation (neither `=` nor a relation with a @[refl] lemma).
- The arguments of the relation are not equal.
Try using the reflexivitiy lemma for your relation explicitly, e.g. `exact Eq.rfl`.")
Try using the reflexivity lemma for your relation explicitly, e.g. `exact Eq.refl _` or
`exact HEq.rfl` etc.")
macro_rules | `(tactic| rfl) => `(tactic| eq_refl)
macro_rules | `(tactic| rfl) => `(tactic| exact HEq.rfl)

View File

@@ -31,15 +31,19 @@ private def expandIfThenElse
pure (hole, #[case])
let (posHole, posCase) mkCase thenTk pos `(?pos)
let (negHole, negCase) mkCase elseTk neg `(?neg)
`(tactic| (open Classical in refine%$ifTk $( mkIf posHole negHole); $[$(posCase ++ negCase)]*))
`(tactic| ((open Classical in refine%$ifTk $( mkIf posHole negHole)); $[$(posCase ++ negCase)]*))
macro_rules
| `(tactic| if%$tk $h : $c then%$ttk $pos else%$etk $neg) =>
expandIfThenElse tk ttk etk pos neg fun pos neg => `(if $h : $c then $pos else $neg)
-- Limit ref variability for incrementality; see Note [Incremental Macros]
withRef tk do
expandIfThenElse tk ttk etk pos neg fun pos neg => `(if $h : $c then $pos else $neg)
macro_rules
| `(tactic| if%$tk $c then%$ttk $pos else%$etk $neg) =>
expandIfThenElse tk ttk etk pos neg fun pos neg => `(if h : $c then $pos else $neg)
-- Limit ref variability for incrementality; see Note [Incremental Macros]
withRef tk do
expandIfThenElse tk ttk etk pos neg fun pos neg => `(if h : $c then $pos else $neg)
/--
`iterate n tac` runs `tac` exactly `n` times.

View File

@@ -37,7 +37,7 @@ noncomputable abbrev Acc.ndrecOn.{u1, u2} {α : Sort u2} {r : αα → Prop
namespace Acc
variable {α : Sort u} {r : α α Prop}
def inv {x y : α} (h₁ : Acc r x) (h₂ : r y x) : Acc r y :=
theorem inv {x y : α} (h₁ : Acc r x) (h₂ : r y x) : Acc r y :=
h₁.recOn (fun _ ac₁ _ h₂ => ac₁ y h₂) h₂
end Acc
@@ -58,7 +58,7 @@ class WellFoundedRelation (α : Sort u) where
wf : WellFounded rel
namespace WellFounded
def apply {α : Sort u} {r : α α Prop} (wf : WellFounded r) (a : α) : Acc r a :=
theorem apply {α : Sort u} {r : α α Prop} (wf : WellFounded r) (a : α) : Acc r a :=
wf.rec (fun p => p) a
section
@@ -78,7 +78,7 @@ noncomputable def fixF (x : α) (a : Acc r x) : C x := by
induction a with
| intro x₁ _ ih => exact F x₁ ih
def fixFEq (x : α) (acx : Acc r x) : fixF F x acx = F x (fun (y : α) (p : r y x) => fixF F y (Acc.inv acx p)) := by
theorem fixFEq (x : α) (acx : Acc r x) : fixF F x acx = F x (fun (y : α) (p : r y x) => fixF F y (Acc.inv acx p)) := by
induction acx with
| intro x r _ => exact rfl
@@ -112,14 +112,14 @@ def emptyWf {α : Sort u} : WellFoundedRelation α where
namespace Subrelation
variable {α : Sort u} {r q : α α Prop}
def accessible {a : α} (h₁ : Subrelation q r) (ac : Acc r a) : Acc q a := by
theorem accessible {a : α} (h₁ : Subrelation q r) (ac : Acc r a) : Acc q a := by
induction ac with
| intro x _ ih =>
apply Acc.intro
intro y h
exact ih y (h₁ h)
def wf (h₁ : Subrelation q r) (h₂ : WellFounded r) : WellFounded q :=
theorem wf (h₁ : Subrelation q r) (h₂ : WellFounded r) : WellFounded q :=
fun a => accessible @h₁ (apply h₂ a)
end Subrelation
@@ -136,10 +136,10 @@ private def accAux (f : α → β) {b : β} (ac : Acc r b) : (x : α) → f x =
subst x
apply ih (f y) lt y rfl
def accessible {a : α} (f : α β) (ac : Acc r (f a)) : Acc (InvImage r f) a :=
theorem accessible {a : α} (f : α β) (ac : Acc r (f a)) : Acc (InvImage r f) a :=
accAux f ac a rfl
def wf (f : α β) (h : WellFounded r) : WellFounded (InvImage r f) :=
theorem wf (f : α β) (h : WellFounded r) : WellFounded (InvImage r f) :=
fun a => accessible f (apply h (f a))
end InvImage
@@ -148,22 +148,26 @@ end InvImage
wf := InvImage.wf f h.wf
-- The transitive closure of a well-founded relation is well-founded
namespace TC
variable {α : Sort u} {r : α α Prop}
open Relation
def accessible {z : α} (ac : Acc r z) : Acc (TC r) z := by
induction ac with
| intro x acx ih =>
apply Acc.intro x
intro y rel
induction rel with
| base a b rab => exact ih a rab
| trans a b c rab _ _ ih₂ => apply Acc.inv (ih₂ acx ih) rab
theorem Acc.transGen (h : Acc r a) : Acc (TransGen r) a := by
induction h with
| intro x _ H =>
refine Acc.intro x fun y hy ?_
cases hy with
| single hyx =>
exact H y hyx
| tail hyz hzx =>
exact (H _ hzx).inv hyz
def wf (h : WellFounded r) : WellFounded (TC r) :=
fun a => accessible (apply h a)
end TC
theorem acc_transGen_iff : Acc (TransGen r) a Acc r a :=
Subrelation.accessible TransGen.single, Acc.transGen
theorem WellFounded.transGen (h : WellFounded r) : WellFounded (TransGen r) :=
fun a (h.apply a).transGen
@[deprecated Acc.transGen (since := "2024-07-16")] abbrev TC.accessible := @Acc.transGen
@[deprecated WellFounded.transGen (since := "2024-07-16")] abbrev TC.wf := @WellFounded.transGen
namespace Nat
-- less-than is well-founded
@@ -251,7 +255,7 @@ instance [αeqDec : DecidableEq α] {r : αα → Prop} [rDec : DecidableRe
apply isFalse; intro contra; cases contra <;> contradiction
-- TODO: generalize
def right' {a₁ : Nat} {b₁ : β} (h₁ : a₁ a₂) (h₂ : rb b₁ b₂) : Prod.Lex Nat.lt rb (a₁, b₁) (a₂, b₂) :=
theorem right' {a₁ : Nat} {b₁ : β} (h₁ : a₁ a₂) (h₂ : rb b₁ b₂) : Prod.Lex Nat.lt rb (a₁, b₁) (a₂, b₂) :=
match Nat.eq_or_lt_of_le h₁ with
| Or.inl h => h Prod.Lex.right a₁ h₂
| Or.inr h => Prod.Lex.left b₁ _ h
@@ -268,7 +272,7 @@ section
variable {α : Type u} {β : Type v}
variable {ra : α α Prop} {rb : β β Prop}
def lexAccessible {a : α} (aca : Acc ra a) (acb : (b : β) Acc rb b) (b : β) : Acc (Prod.Lex ra rb) (a, b) := by
theorem lexAccessible {a : α} (aca : Acc ra a) (acb : (b : β) Acc rb b) (b : β) : Acc (Prod.Lex ra rb) (a, b) := by
induction aca generalizing b with
| intro xa _ iha =>
induction (acb b) with
@@ -288,7 +292,7 @@ instance [ha : WellFoundedRelation α] [hb : WellFoundedRelation β] : WellFound
lex ha hb
-- relational product is a Subrelation of the Lex
def RProdSubLex (a : α × β) (b : α × β) (h : RProd ra rb a b) : Prod.Lex ra rb a b := by
theorem RProdSubLex (a : α × β) (b : α × β) (h : RProd ra rb a b) : Prod.Lex ra rb a b := by
cases h with
| intro h₁ h₂ => exact Prod.Lex.left _ _ h₁
@@ -320,7 +324,7 @@ section
variable {α : Sort u} {β : α Sort v}
variable {r : α α Prop} {s : (a : α), β a β a Prop}
def lexAccessible {a} (aca : Acc r a) (acb : (a : α) WellFounded (s a)) (b : β a) : Acc (Lex r s) a, b := by
theorem lexAccessible {a} (aca : Acc r a) (acb : (a : α) WellFounded (s a)) (b : β a) : Acc (Lex r s) a, b := by
induction aca with
| intro xa _ iha =>
induction (WellFounded.apply (acb xa) b) with
@@ -347,7 +351,7 @@ variable {α : Sort u} {β : Sort v}
def lexNdep (r : α α Prop) (s : β β Prop) :=
Lex r (fun _ => s)
def lexNdepWf {r : α α Prop} {s : β β Prop} (ha : WellFounded r) (hb : WellFounded s) : WellFounded (lexNdep r s) :=
theorem lexNdepWf {r : α α Prop} {s : β β Prop} (ha : WellFounded r) (hb : WellFounded s) : WellFounded (lexNdep r s) :=
WellFounded.intro fun a, b => lexAccessible (WellFounded.apply ha a) (fun _ => hb) b
end
@@ -365,7 +369,7 @@ open WellFounded
variable {α : Sort u} {β : Sort v}
variable {r : α α Prop} {s : β β Prop}
def revLexAccessible {b} (acb : Acc s b) (aca : (a : α) Acc r a): (a : α) Acc (RevLex r s) a, b := by
theorem revLexAccessible {b} (acb : Acc s b) (aca : (a : α) Acc r a): (a : α) Acc (RevLex r s) a, b := by
induction acb with
| intro xb _ ihb =>
intro a
@@ -377,7 +381,7 @@ def revLexAccessible {b} (acb : Acc s b) (aca : (a : α) → Acc r a): (a : α)
| left => apply iha; assumption
| right => apply ihb; assumption
def revLex (ha : WellFounded r) (hb : WellFounded s) : WellFounded (RevLex r s) :=
theorem revLex (ha : WellFounded r) (hb : WellFounded s) : WellFounded (RevLex r s) :=
WellFounded.intro fun a, b => revLexAccessible (apply hb b) (WellFounded.apply ha) a
end
@@ -389,7 +393,7 @@ def skipLeft (α : Type u) {β : Type v} (hb : WellFoundedRelation β) : WellFou
rel := SkipLeft α hb.rel
wf := revLex emptyWf.wf hb.wf
def mkSkipLeft {α : Type u} {β : Type v} {b₁ b₂ : β} {s : β β Prop} (a₁ a₂ : α) (h : s b₁ b₂) : SkipLeft α s a₁, b₁ a₂, b₂ :=
theorem mkSkipLeft {α : Type u} {β : Type v} {b₁ b₂ : β} {s : β β Prop} (a₁ a₂ : α) (h : s b₁ b₂) : SkipLeft α s a₁, b₁ a₂, b₂ :=
RevLex.right _ _ h
end

View File

@@ -8,11 +8,22 @@ import Lean.CoreM
namespace Lean
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration) : Except KernelException Environment :=
addDeclCore env (Core.getMaxHeartbeats opts).toUSize decl
register_builtin_option debug.skipKernelTC : Bool := {
defValue := false
group := "debug"
descr := "skip kernel type checker. WARNING: setting this option to true may compromise soundness because your proofs will not be checked by the Lean kernel"
}
def Environment.addAndCompile (env : Environment) (opts : Options) (decl : Declaration) : Except KernelException Environment := do
let env addDecl env opts decl
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
(cancelTk? : Option IO.CancelToken := none) : Except KernelException Environment :=
if debug.skipKernelTC.get opts then
addDeclWithoutChecking env decl
else
addDeclCore env (Core.getMaxHeartbeats opts).toUSize decl cancelTk?
def Environment.addAndCompile (env : Environment) (opts : Options) (decl : Declaration)
(cancelTk? : Option IO.CancelToken := none) : Except KernelException Environment := do
let env addDecl env opts decl cancelTk?
compileDecl env opts decl
def addDecl (decl : Declaration) : CoreM Unit := do
@@ -20,7 +31,7 @@ def addDecl (decl : Declaration) : CoreM Unit := do
withTraceNode `Kernel (fun _ => return m!"typechecking declaration") do
if !( MonadLog.hasErrors) && decl.hasSorry then
logWarning "declaration uses 'sorry'"
match ( getEnv).addDecl ( getOptions) decl with
match ( getEnv).addDecl ( getOptions) decl ( read).cancelTk? with
| .ok env => setEnv env
| .error ex => throwKernelException ex

View File

@@ -53,7 +53,7 @@ structure AttributeImpl extends AttributeImplCore where
erase (decl : Name) : AttrM Unit := throwError "attribute cannot be erased"
deriving Inhabited
builtin_initialize attributeMapRef : IO.Ref (PersistentHashMap Name AttributeImpl) IO.mkRef {}
builtin_initialize attributeMapRef : IO.Ref (HashMap Name AttributeImpl) IO.mkRef {}
/-- Low level attribute registration function. -/
def registerBuiltinAttribute (attr : AttributeImpl) : IO Unit := do
@@ -185,7 +185,7 @@ structure ParametricAttributeImpl (α : Type) extends AttributeImplCore where
afterSet : Name α AttrM Unit := fun _ _ _ => pure ()
afterImport : Array (Array (Name × α)) ImportM Unit := fun _ => pure ()
def registerParametricAttribute [Inhabited α] (impl : ParametricAttributeImpl α) : IO (ParametricAttribute α) := do
def registerParametricAttribute (impl : ParametricAttributeImpl α) : IO (ParametricAttribute α) := do
let ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α) registerPersistentEnvExtension {
name := impl.ref
mkInitial := pure {}
@@ -239,7 +239,7 @@ structure EnumAttributes (α : Type) where
ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α)
deriving Inhabited
def registerEnumAttributes [Inhabited α] (attrDescrs : List (Name × String × α))
def registerEnumAttributes (attrDescrs : List (Name × String × α))
(validate : Name α AttrM Unit := fun _ _ => pure ())
(applicationTime := AttributeApplicationTime.afterTypeChecking)
(ref : Name := by exact decl_name%) : IO (EnumAttributes α) := do
@@ -317,7 +317,7 @@ inductive AttributeExtensionOLeanEntry where
structure AttributeExtensionState where
newEntries : List AttributeExtensionOLeanEntry := []
map : PersistentHashMap Name AttributeImpl
map : HashMap Name AttributeImpl
deriving Inhabited
abbrev AttributeExtension := PersistentEnvExtension AttributeExtensionOLeanEntry (AttributeExtensionOLeanEntry × AttributeImpl) AttributeExtensionState
@@ -348,7 +348,7 @@ private def AttributeExtension.addImported (es : Array (Array AttributeExtension
let map es.foldlM
(fun map entries =>
entries.foldlM
(fun (map : PersistentHashMap Name AttributeImpl) entry => do
(fun (map : HashMap Name AttributeImpl) entry => do
let attrImpl mkAttributeImplOfEntry ctx.env ctx.opts entry
return map.insert attrImpl.name attrImpl)
map)
@@ -374,7 +374,7 @@ def isBuiltinAttribute (n : Name) : IO Bool := do
/-- Return the name of all registered attributes. -/
def getBuiltinAttributeNames : IO (List Name) :=
return ( attributeMapRef.get).foldl (init := []) fun r n _ => n::r
return ( attributeMapRef.get).fold (init := []) fun r n _ => n::r
def getBuiltinAttributeImpl (attrName : Name) : IO AttributeImpl := do
let m attributeMapRef.get
@@ -392,7 +392,7 @@ def isAttribute (env : Environment) (attrName : Name) : Bool :=
def getAttributeNames (env : Environment) : List Name :=
let m := (attributeExtension.getState env).map
m.foldl (fun r n _ => n::r) []
m.fold (fun r n _ => n::r) []
def getAttributeImpl (env : Environment) (attrName : Name) : Except String AttributeImpl :=
let m := (attributeExtension.getState env).map
@@ -427,7 +427,7 @@ def Attribute.erase (declName : Name) (attrName : Name) : AttrM Unit := do
def updateEnvAttributesImpl (env : Environment) : IO Environment := do
let map attributeMapRef.get
let s := attributeExtension.getState env
let s := map.foldl (init := s) fun s attrName attrImpl =>
let s := map.fold (init := s) fun s attrName attrImpl =>
if s.map.contains attrName then
s
else

View File

@@ -13,16 +13,17 @@ def recOnSuffix := "recOn"
def brecOnSuffix := "brecOn"
def binductionOnSuffix := "binductionOn"
def belowSuffix := "below"
def ibelowSuffix := "ibelow"
def mkCasesOnName (indDeclName : Name) : Name := Name.mkStr indDeclName casesOnSuffix
def mkRecOnName (indDeclName : Name) : Name := Name.mkStr indDeclName recOnSuffix
def mkBRecOnName (indDeclName : Name) : Name := Name.mkStr indDeclName brecOnSuffix
def mkBInductionOnName (indDeclName : Name) : Name := Name.mkStr indDeclName binductionOnSuffix
def mkBelowName (indDeclName : Name) : Name := Name.mkStr indDeclName belowSuffix
def mkIBelowName (indDeclName : Name) : Name := Name.mkStr indDeclName ibelowSuffix
builtin_initialize auxRecExt : TagDeclarationExtension mkTagDeclarationExtension
@[export lean_mark_aux_recursor]
def markAuxRecursor (env : Environment) (declName : Name) : Environment :=
auxRecExt.tag env declName
@@ -50,7 +51,6 @@ def isBRecOnRecursor (env : Environment) (declName : Name) : Bool :=
builtin_initialize noConfusionExt : TagDeclarationExtension mkTagDeclarationExtension
@[export lean_mark_no_confusion]
def markNoConfusion (env : Environment) (n : Name) : Environment :=
noConfusionExt.tag env n

View File

@@ -5,12 +5,12 @@ Authors: Mario Carneiro
-/
prelude
import Lean.Compiler.InitAttr
import Lean.DocString
import Lean.DocString.Extension
namespace Lean
def declareBuiltinDocStringAndRanges (declName : Name) : AttrM Unit := do
if let some doc findDocString? ( getEnv) declName (includeBuiltin := false) then
if let some doc findSimpleDocString? ( getEnv) declName (includeBuiltin := false) then
declareBuiltin (declName ++ `docString) (mkAppN (mkConst ``addBuiltinDocString) #[toExpr declName, toExpr doc])
if let some declRanges findDeclarationRanges? declName then
declareBuiltin (declName ++ `declRange) (mkAppN (mkConst ``addBuiltinDeclarationRanges) #[toExpr declName, toExpr declRanges])

View File

@@ -18,6 +18,13 @@ private opaque getLeancExtraFlags : Unit → String
def getCFlags (leanSysroot : FilePath) : Array String :=
#["-I", (leanSysroot / "include").toString] ++ (getLeancExtraFlags ()).trim.splitOn
@[extern "lean_get_leanc_internal_flags"]
private opaque getLeancInternalFlags : Unit String
/-- Return C compiler flags needed to use the C compiler bundled with the Lean toolchain. -/
def getInternalCFlags (leanSysroot : FilePath) : Array String :=
(getLeancInternalFlags ()).trim.splitOn.toArray.map (·.replace "ROOT" leanSysroot.toString)
@[extern "lean_get_linker_flags"]
private opaque getBuiltinLinkerFlags (linkStatic : Bool) : String
@@ -25,4 +32,11 @@ private opaque getBuiltinLinkerFlags (linkStatic : Bool) : String
def getLinkerFlags (leanSysroot : FilePath) (linkStatic := true) : Array String :=
#["-L", (leanSysroot / "lib" / "lean").toString] ++ (getBuiltinLinkerFlags linkStatic).trim.splitOn
@[extern "lean_get_internal_linker_flags"]
private opaque getBuiltinInternalLinkerFlags : Unit String
/-- Return linker flags needed to use the linker bundled with the Lean toolchain. -/
def getInternalLinkerFlags (leanSysroot : FilePath) : Array String :=
(getBuiltinInternalLinkerFlags ()).trim.splitOn.toArray.map (·.replace "ROOT" leanSysroot.toString)
end Lean.Compiler.FFI

View File

@@ -94,7 +94,7 @@ def emitCInitName (n : Name) : M Unit :=
def shouldExport (n : Name) : Bool :=
-- HACK: exclude symbols very unlikely to be used by the interpreter or other consumers of
-- libleanshared to avoid Windows symbol limit
!(`Lean.Compiler.LCNF).isPrefixOf n
!(`Lean.Compiler.LCNF).isPrefixOf n && !(`Lean.IR).isPrefixOf n && !(`Lean.Server).isPrefixOf n
def emitFnDeclAux (decl : Decl) (cppBaseName : String) (isExternal : Bool) : M Unit := do
let ps := decl.params
@@ -499,7 +499,11 @@ def emitLit (z : VarId) (t : IRType) (v : LitVal) : M Unit := do
emitLhs z;
match v with
| LitVal.num v => emitNumLit t v; emitLn ";"
| LitVal.str v => emit "lean_mk_string_from_bytes("; emit (quoteString v); emit ", "; emit v.utf8ByteSize; emitLn ");"
| LitVal.str v =>
emit "lean_mk_string_unchecked(";
emit (quoteString v); emit ", ";
emit v.utf8ByteSize; emit ", ";
emit v.length; emitLn ");"
def emitVDecl (z : VarId) (t : IRType) (v : Expr) : M Unit :=
match v with

View File

@@ -178,14 +178,14 @@ def callLeanUnsignedToNatFn (builder : LLVM.Builder llvmctx)
let nv constIntUnsigned n
LLVM.buildCall2 builder fnty f #[nv] name
def callLeanMkStringFromBytesFn (builder : LLVM.Builder llvmctx)
(strPtr nBytes : LLVM.Value llvmctx) (name : String) : M llvmctx (LLVM.Value llvmctx) := do
let fnName := "lean_mk_string_from_bytes"
def callLeanMkStringUncheckedFn (builder : LLVM.Builder llvmctx)
(strPtr nBytes nChars : LLVM.Value llvmctx) (name : String) : M llvmctx (LLVM.Value llvmctx) := do
let fnName := "lean_mk_string_unchecked"
let retty LLVM.voidPtrType llvmctx
let argtys := #[ LLVM.voidPtrType llvmctx, LLVM.size_tType llvmctx]
let argtys := #[ LLVM.voidPtrType llvmctx, LLVM.size_tType llvmctx, LLVM.size_tType llvmctx]
let fn getOrCreateFunctionPrototype ( getLLVMModule) retty fnName argtys
let fnty LLVM.functionType retty argtys
LLVM.buildCall2 builder fnty fn #[strPtr, nBytes] name
LLVM.buildCall2 builder fnty fn #[strPtr, nBytes, nChars] name
def callLeanMkString (builder : LLVM.Builder llvmctx)
(strPtr : LLVM.Value llvmctx) (name : String) : M llvmctx (LLVM.Value llvmctx) := do
@@ -772,7 +772,8 @@ def emitLit (builder : LLVM.Builder llvmctx)
( LLVM.opaquePointerTypeInContext llvmctx)
str_global #[zero] ""
let nbytes constIntSizeT v.utf8ByteSize
callLeanMkStringFromBytesFn builder strPtr nbytes ""
let nchars constIntSizeT v.length
callLeanMkStringUncheckedFn builder strPtr nbytes nchars ""
LLVM.buildStore builder zv zslot
return zslot

View File

@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Compiler.Options
import Lean.Compiler.ExternAttr
import Lean.Compiler.LCNF.PassManager
import Lean.Compiler.LCNF.Passes
import Lean.Compiler.LCNF.PrettyPrinter

View File

@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.PrettyPrinter
import Lean.PrettyPrinter.Delaborator.Options
import Lean.Compiler.LCNF.CompilerM
import Lean.Compiler.LCNF.Internalize

View File

@@ -211,12 +211,12 @@ instance : MonadTrace CoreM where
structure SavedState extends State where
/-- Number of heartbeats passed inside `withRestoreOrSaveFull`, not used otherwise. -/
passedHearbeats : Nat
passedHeartbeats : Nat
deriving Nonempty
def saveState : CoreM SavedState := do
let s get
return { toState := s, passedHearbeats := 0 }
return { toState := s, passedHeartbeats := 0 }
/--
Incremental reuse primitive: if `reusableResult?` is `none`, runs `act` and returns its result
@@ -236,14 +236,14 @@ itself after calling `act` as well as by reuse-handling code such as the one sup
(act : CoreM α) : CoreM (α × SavedState) := do
if let some (val, state) := reusableResult? then
set state.toState
IO.addHeartbeats state.passedHearbeats.toUInt64
IO.addHeartbeats state.passedHeartbeats.toUInt64
return (val, state)
let startHeartbeats IO.getNumHeartbeats
let a act
let s get
let stopHeartbeats IO.getNumHeartbeats
return (a, { toState := s, passedHearbeats := stopHeartbeats - startHeartbeats })
return (a, { toState := s, passedHeartbeats := stopHeartbeats - startHeartbeats })
/-- Restore backtrackable parts of the state. -/
def SavedState.restore (b : SavedState) : CoreM Unit :=
@@ -472,23 +472,30 @@ def Exception.isInterrupt : Exception → Bool
/--
Custom `try-catch` for all monads based on `CoreM`. We usually don't want to catch "runtime
exceptions" these monads, but on `CommandElabM`. See issues #2775 and #2744 as well as
`MonadAlwaysExcept`. Also, we never want to catch interrupt exceptions inside the elaborator.
exceptions" these monads, but on `CommandElabM` or, in specific cases, using `tryCatchRuntimeEx`.
See issues #2775 and #2744 as well as `MonadAlwaysExcept`. Also, we never want to catch interrupt
exceptions inside the elaborator.
-/
@[inline] protected def Core.tryCatch (x : CoreM α) (h : Exception CoreM α) : CoreM α := do
try
x
catch ex =>
if ex.isInterrupt || ex.isRuntime then
throw ex -- We should use `tryCatchRuntimeEx` for catching runtime exceptions
throw ex
else
h ex
/--
A variant of `tryCatch` that also catches runtime exception (see also `tryCatch` documentation).
Like `tryCatch`, this function does not catch interrupt exceptions, which are not considered runtime
exceptions.
-/
@[inline] protected def Core.tryCatchRuntimeEx (x : CoreM α) (h : Exception CoreM α) : CoreM α := do
try
x
catch ex =>
if ex.isInterrupt then
throw ex
h ex
instance : MonadExceptOf Exception CoreM where
@@ -512,4 +519,16 @@ instance : MonadRuntimeException CoreM where
@[inline] def mapCoreM [MonadControlT CoreM m] [Monad m] (f : forall {α}, CoreM α CoreM α) {α} (x : m α) : m α :=
controlAt CoreM fun runInBase => f <| runInBase x
/--
Returns `true` if the given message kind has not been reported in the message log,
and then mark it as reported. Otherwise, returns `false`.
We use this API to ensure we don't report the same kind of warning multiple times.
-/
def reportMessageKind (kind : Name) : CoreM Bool := do
if ( get).messages.reportedKinds.contains kind then
return false
else
modify fun s => { s with messages.reportedKinds := s.messages.reportedKinds.insert kind }
return true
end Lean

View File

@@ -223,8 +223,6 @@ def insertIfNew (m : HashMap α β) (a : α) (b : β) : HashMap α β × Option
instance : GetElem (HashMap α β) α (Option β) fun _ _ => True where
getElem m k _ := m.find? k
instance : LawfulGetElem (HashMap α β) α (Option β) fun _ _ => True where
@[inline] def contains (m : HashMap α β) (a : α) : Bool :=
match m with
| m, _ => m.contains a

View File

@@ -72,8 +72,6 @@ def get! [Inhabited α] (t : PersistentArray α) (i : Nat) : α :=
instance [Inhabited α] : GetElem (PersistentArray α) Nat α fun as i => i < as.size where
getElem xs i _ := xs.get! i
instance [Inhabited α] : LawfulGetElem (PersistentArray α) Nat α fun as i => i < as.size where
partial def setAux : PersistentArrayNode α USize USize α PersistentArrayNode α
| node cs, i, shift, a =>
let j := div2Shift i shift

View File

@@ -23,6 +23,13 @@ inductive Node (α : Type u) (β : Type v) : Type (max u v) where
| entries (es : Array (Entry α β (Node α β))) : Node α β
| collision (ks : Array α) (vs : Array β) (h : ks.size = vs.size) : Node α β
partial def Node.isEmpty : Node α β Bool
| .collision .. => false
| .entries es => es.all fun
| .entry .. => false
| .ref n => n.isEmpty
| .null => true
instance {α β} : Inhabited (Node α β) := Node.entries #[]
abbrev shift : USize := 5
@@ -36,8 +43,7 @@ def mkEmptyEntriesArray {α β} : Array (Entry α β (Node α β)) :=
end PersistentHashMap
structure PersistentHashMap (α : Type u) (β : Type v) [BEq α] [Hashable α] where
root : PersistentHashMap.Node α β := PersistentHashMap.Node.entries PersistentHashMap.mkEmptyEntriesArray
size : Nat := 0
root : PersistentHashMap.Node α β := PersistentHashMap.Node.entries PersistentHashMap.mkEmptyEntriesArray
abbrev PHashMap (α : Type u) (β : Type v) [BEq α] [Hashable α] := PersistentHashMap α β
@@ -45,8 +51,8 @@ namespace PersistentHashMap
def empty [BEq α] [Hashable α] : PersistentHashMap α β := {}
def isEmpty [BEq α] [Hashable α] (m : PersistentHashMap α β) : Bool :=
m.size == 0
def isEmpty {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β Bool
| { root } => root.isEmpty
instance [BEq α] [Hashable α] : Inhabited (PersistentHashMap α β) := {}
@@ -130,7 +136,7 @@ partial def insertAux [BEq α] [Hashable α] : Node α β → USize → USize
else Entry.ref $ mkCollisionNode k' v' k v
def insert {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β α β PersistentHashMap α β
| { root := n, size := sz }, k, v => { root := insertAux n (hash k |>.toUSize) 1 k v, size := sz + 1 }
| { root }, k, v => { root := insertAux root (hash k |>.toUSize) 1 k v }
partial def findAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : keys.size = vals.size) (i : Nat) (k : α) : Option β :=
if h : i < keys.size then
@@ -150,13 +156,11 @@ partial def findAux [BEq α] : Node α β → USize → α → Option β
| Node.collision keys vals heq, _, k => findAtAux keys vals heq 0 k
def find? {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β α Option β
| { root := n, .. }, k => findAux n (hash k |>.toUSize) k
| { root }, k => findAux root (hash k |>.toUSize) k
instance {_ : BEq α} {_ : Hashable α} : GetElem (PersistentHashMap α β) α (Option β) fun _ _ => True where
getElem m i _ := m.find? i
instance {_ : BEq α} {_ : Hashable α} : LawfulGetElem (PersistentHashMap α β) α (Option β) fun _ _ => True where
@[inline] def findD {_ : BEq α} {_ : Hashable α} (m : PersistentHashMap α β) (a : α) (b₀ : β) : β :=
(m.find? a).getD b₀
@@ -183,7 +187,7 @@ partial def findEntryAux [BEq α] : Node α β → USize → α → Option (α
| Node.collision keys vals heq, _, k => findEntryAtAux keys vals heq 0 k
def findEntry? {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β α Option (α × β)
| { root := n, .. }, k => findEntryAux n (hash k |>.toUSize) k
| { root }, k => findEntryAux root (hash k |>.toUSize) k
partial def containsAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : keys.size = vals.size) (i : Nat) (k : α) : Bool :=
if h : i < keys.size then
@@ -202,7 +206,7 @@ partial def containsAux [BEq α] : Node α β → USize → α → Bool
| Node.collision keys vals heq, _, k => containsAtAux keys vals heq 0 k
def contains [BEq α] [Hashable α] : PersistentHashMap α β α Bool
| { root := n, .. }, k => containsAux n (hash k |>.toUSize) k
| { root }, k => containsAux root (hash k |>.toUSize) k
partial def isUnaryEntries (a : Array (Entry α β (Node α β))) (i : Nat) (acc : Option (α × β)) : Option (α × β) :=
if h : i < a.size then
@@ -225,7 +229,7 @@ def isUnaryNode : Node α β → Option (α × β)
else
none
partial def eraseAux [BEq α] : Node α β USize α Node α β × Bool
partial def eraseAux [BEq α] : Node α β USize α Node α β
| n@(Node.collision keys vals heq), _, k =>
match keys.indexOf? k with
| some idx =>
@@ -234,28 +238,26 @@ partial def eraseAux [BEq α] : Node α β → USize → α → Node α β × Bo
let vals' := vals.feraseIdx (Eq.ndrec idx heq)
have veq := vals.size_feraseIdx (Eq.ndrec idx heq)
have : keys.size - 1 = vals.size - 1 := by rw [heq]
(Node.collision keys' vals' (keq.trans (this.trans veq.symm)), true)
| none => (n, false)
Node.collision keys' vals' (keq.trans (this.trans veq.symm))
| none => n
| n@(Node.entries entries), h, k =>
let j := (mod2Shift h shift).toNat
let entry := entries.get! j
match entry with
| Entry.null => (n, false)
| Entry.null => n
| Entry.entry k' _ =>
if k == k' then (Node.entries (entries.set! j Entry.null), true) else (n, false)
if k == k' then Node.entries (entries.set! j Entry.null) else n
| Entry.ref node =>
let entries := entries.set! j Entry.null
let (newNode, deleted) := eraseAux node (div2Shift h shift) k
if !deleted then (n, false)
else match isUnaryNode newNode with
| none => (Node.entries (entries.set! j (Entry.ref newNode)), true)
| some (k, v) => (Node.entries (entries.set! j (Entry.entry k v)), true)
let newNode := eraseAux node (div2Shift h shift) k
match isUnaryNode newNode with
| none => Node.entries (entries.set! j (Entry.ref newNode))
| some (k, v) => Node.entries (entries.set! j (Entry.entry k v))
def erase {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β α PersistentHashMap α β
| { root := n, size := sz }, k =>
| { root }, k =>
let h := hash k |>.toUSize
let (n, del) := eraseAux n h k
{ root := n, size := if del then sz - 1 else sz }
{ root := eraseAux root h k }
section
variable {m : Type w Type w'} [Monad m]
@@ -317,7 +319,7 @@ partial def mapMAux {α : Type u} {β : Type v} {σ : Type u} {m : Type u → Ty
def mapM {α : Type u} {β : Type v} {σ : Type u} {m : Type u Type w} [Monad m] {_ : BEq α} {_ : Hashable α} (pm : PersistentHashMap α β) (f : β m σ) : m (PersistentHashMap α σ) := do
let root mapMAux f pm.root
return { pm with root }
return { root }
def map {α : Type u} {β : Type v} {σ : Type u} {_ : BEq α} {_ : Hashable α} (pm : PersistentHashMap α β) (f : β σ) : PersistentHashMap α σ :=
Id.run <| pm.mapM f

View File

@@ -44,9 +44,6 @@ variable {_ : BEq α} {_ : Hashable α}
@[inline] def contains (s : PersistentHashSet α) (a : α) : Bool :=
s.set.contains a
@[inline] def size (s : PersistentHashSet α) : Nat :=
s.set.size
@[inline] def foldM {β : Type v} {m : Type v Type v} [Monad m] (f : β α m β) (init : β) (s : PersistentHashSet α) : m β :=
s.set.foldlM (init := init) fun d a _ => f d a

View File

@@ -80,6 +80,10 @@ protected def max : RBNode α β → Option (Sigma (fun k => β k))
def singleton (k : α) (v : β k) : RBNode α β :=
node red leaf k v leaf
def isSingleton : RBNode α β Bool
| node _ leaf _ _ leaf => true
| _ => false
-- the first half of Okasaki's `balance`, concerning red-red sequences in the left child
@[inline] def balance1 : RBNode α β (a : α) β a RBNode α β RBNode α β
| node red (node red a kx vx b) ky vy c, kz, vz, d
@@ -269,6 +273,9 @@ variable {α : Type u} {β : Type v} {σ : Type w} {cmp : αα → Ordering
def depth (f : Nat Nat Nat) (t : RBMap α β cmp) : Nat :=
t.val.depth f
def isSingleton (t : RBMap α β cmp) : Bool :=
t.val.isSingleton
@[inline] def fold (f : σ α β σ) : (init : σ) RBMap α β cmp σ
| b, t, _ => t.fold f b

View File

@@ -87,15 +87,14 @@ def switch (m : SMap α β) : SMap α β :=
@[inline] def foldStage2 {σ : Type w} (f : σ α β σ) (s : σ) (m : SMap α β) : σ :=
m.map₂.foldl f s
/-- Monadic fold over a staged map. -/
def foldM {m : Type w Type w} [Monad m]
(f : σ α β m σ) (init : σ) (map : SMap α β) : m σ := do
map.map₂.foldlM f ( map.map₁.foldM f init)
def fold {σ : Type w} (f : σ α β σ) (init : σ) (m : SMap α β) : σ :=
m.map₂.foldl f $ m.map₁.fold f init
def size (m : SMap α β) : Nat :=
m.map₁.size + m.map₂.size
def stageSizes (m : SMap α β) : Nat × Nat :=
(m.map₁.size, m.map₂.size)
def numBuckets (m : SMap α β) : Nat :=
m.map₁.numBuckets

View File

@@ -34,9 +34,6 @@ abbrev switch (s : SSet α) : SSet α :=
abbrev fold (f : σ α σ) (init : σ) (s : SSet α) : σ :=
SMap.fold (fun d a _ => f d a) init s
abbrev size (s : SSet α) : Nat :=
SMap.size s
def toList (m : SSet α) : List α :=
m.fold (init := []) fun es a => a::es

View File

@@ -35,7 +35,7 @@ inductive ReducibilityHints where
| opaque : ReducibilityHints
| abbrev : ReducibilityHints
| regular : UInt32 ReducibilityHints
deriving Inhabited
deriving Inhabited, BEq
@[export lean_mk_reducibility_hints_regular]
def mkReducibilityHintsRegularEx (h : UInt32) : ReducibilityHints :=
@@ -117,7 +117,7 @@ structure DefinitionVal extends ConstantVal where
are compiled using recursors and `WellFounded.fix`.
-/
all : List Name := [name]
deriving Inhabited
deriving Inhabited, BEq
@[export lean_mk_definition_val]
def mkDefinitionValEx (name : Name) (levelParams : List Name) (type : Expr) (value : Expr) (hints : ReducibilityHints) (safety : DefinitionSafety) (all : List Name) : DefinitionVal := {
@@ -161,13 +161,13 @@ def mkOpaqueValEx (name : Name) (levelParams : List Name) (type : Expr) (value :
structure Constructor where
name : Name
type : Expr
deriving Inhabited
deriving Inhabited, BEq
structure InductiveType where
name : Name
type : Expr
ctors : List Constructor
deriving Inhabited
deriving Inhabited, BEq
/-- Declaration object that can be sent to the kernel. -/
inductive Declaration where
@@ -178,7 +178,7 @@ inductive Declaration where
| quotDecl
| mutualDefnDecl (defns : List DefinitionVal) -- All definitions must be marked as `unsafe` or `partial`
| inductDecl (lparams : List Name) (nparams : Nat) (types : List InductiveType) (isUnsafe : Bool)
deriving Inhabited
deriving Inhabited, BEq
@[export lean_mk_inductive_decl]
def mkInductiveDeclEs (lparams : List Name) (nparams : Nat) (types : List InductiveType) (isUnsafe : Bool) : Declaration :=
@@ -189,6 +189,10 @@ def Declaration.isUnsafeInductiveDeclEx : Declaration → Bool
| Declaration.inductDecl _ _ _ isUnsafe => isUnsafe
| _ => false
def Declaration.definitionVal! : Declaration DefinitionVal
| .defnDecl val => val
| _ => panic! "Expected a `Declaration.defnDecl`."
@[specialize] def Declaration.foldExprM {α} {m : Type Type} [Monad m] (d : Declaration) (f : α Expr m α) (a : α) : m α :=
match d with
| Declaration.quotDecl => pure a
@@ -235,6 +239,10 @@ structure InductiveVal extends ConstantVal where
all : List Name
/-- List of the names of the constructors for this inductive datatype. -/
ctors : List Name
/-- Number of auxillary data types produced from nested occurrences.
An inductive definition `T` is nested when there is a constructor with an argument `x : F T`,
where `F : Type → Type` is some suitably behaved (ie strictly positive) function (Eg `Array T`, `List T`, `T × T`, ...). -/
numNested : Nat
/-- `true` when recursive (that is, the inductive type appears as an argument in a constructor). -/
isRec : Bool
/-- Whether the definition is flagged as unsafe. -/
@@ -253,14 +261,12 @@ structure InductiveVal extends ConstantVal where
Section 2.2, Definition 3
-/
isReflexive : Bool
/-- An inductive definition `T` is nested when there is a constructor with an argument `x : F T`,
where `F : Type → Type` is some suitably behaved (ie strictly positive) function (Eg `Array T`, `List T`, `T × T`, ...). -/
isNested : Bool
deriving Inhabited
@[export lean_mk_inductive_val]
def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numParams numIndices : Nat)
(all ctors : List Name) (isRec isUnsafe isReflexive isNested : Bool) : InductiveVal := {
(all ctors : List Name) (numNested : Nat) (isRec isUnsafe isReflexive : Bool) : InductiveVal := {
name := name
levelParams := levelParams
type := type
@@ -268,18 +274,19 @@ def mkInductiveValEx (name : Name) (levelParams : List Name) (type : Expr) (numP
numIndices := numIndices
all := all
ctors := ctors
numNested := numNested
isRec := isRec
isUnsafe := isUnsafe
isReflexive := isReflexive
isNested := isNested
}
@[export lean_inductive_val_is_rec] def InductiveVal.isRecEx (v : InductiveVal) : Bool := v.isRec
@[export lean_inductive_val_is_unsafe] def InductiveVal.isUnsafeEx (v : InductiveVal) : Bool := v.isUnsafe
@[export lean_inductive_val_is_reflexive] def InductiveVal.isReflexiveEx (v : InductiveVal) : Bool := v.isReflexive
@[export lean_inductive_val_is_nested] def InductiveVal.isNestedEx (v : InductiveVal) : Bool := v.isNested
def InductiveVal.numCtors (v : InductiveVal) : Nat := v.ctors.length
def InductiveVal.isNested (v : InductiveVal) : Bool := v.numNested > 0
def InductiveVal.numTypeFormers (v : InductiveVal) : Nat := v.all.length + v.numNested
structure ConstructorVal extends ConstantVal where
/-- Inductive type this constructor is a member of -/

View File

@@ -4,58 +4,26 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.DeclarationRange
import Lean.MonadEnv
import Init.Data.String.Extra
import Lean.DocString.Extension
import Lean.Parser.Tactic.Doc
set_option linter.missingDocs true
-- This module contains the main query interface for docstrings, which assembles user-visible
-- docstrings.
-- The module `Lean.DocString.Extension` contains the underlying data.
namespace Lean
open Lean.Parser.Tactic.Doc
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) IO.mkRef {}
private builtin_initialize docStringExt : MapDeclarationExtension String mkMapDeclarationExtension
/--
Finds the docstring for a name, taking tactic alternate forms and documentation extensions into
account.
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit :=
builtinDocStrings.modify (·.insert declName docString.removeLeadingSpaces)
def addDocString [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString : String) : m Unit := do
unless ( getEnv).getModuleIdxFor? declName |>.isNone do
throwError s!"invalid doc string, declaration '{declName}' is in an imported module"
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
def addDocString' [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString? : Option String) : m Unit :=
match docString? with
| some docString => addDocString declName docString
| none => return ()
def findDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) :=
if let some docStr := docStringExt.find? env declName then
return some docStr
else if includeBuiltin then
return ( builtinDocStrings.get).find? declName
else
return none
structure ModuleDoc where
doc : String
declarationRange : DeclarationRange
private builtin_initialize moduleDocExt : SimplePersistentEnvExtension ModuleDoc (PersistentArray ModuleDoc) registerSimplePersistentEnvExtension {
addImportedFn := fun _ => {}
addEntryFn := fun s e => s.push e
toArrayFn := fun es => es.toArray
}
def addMainModuleDoc (env : Environment) (doc : ModuleDoc) : Environment :=
moduleDocExt.addEntry env doc
def getMainModuleDoc (env : Environment) : PersistentArray ModuleDoc :=
moduleDocExt.getState env
def getModuleDoc? (env : Environment) (moduleName : Name) : Option (Array ModuleDoc) :=
env.getModuleIdx? moduleName |>.map fun modIdx => moduleDocExt.getModuleEntries env modIdx
def getDocStringText [Monad m] [MonadError m] [MonadRef m] (stx : TSyntax `Lean.Parser.Command.docComment) : m String :=
match stx.raw[1] with
| Syntax.atom _ val => return val.extract 0 (val.endPos - 2)
| _ => throwErrorAt stx "unexpected doc string{indentD stx.raw[1]}"
end Lean
Use `Lean.findSimpleDocString?` to look up the raw docstring without resolving alternate forms or
including extensions.
-/
def findDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) := do
let declName := alternativeOfTactic env declName |>.getD declName
let exts := getTacticExtensionString env declName
return ( findSimpleDocString? env declName (includeBuiltin := includeBuiltin)).map (· ++ exts)

View File

@@ -0,0 +1,69 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.DeclarationRange
import Lean.MonadEnv
import Init.Data.String.Extra
-- This module contains the underlying data for docstrings, with as few imports as possible, so that
-- docstrings can be saved in as much of the compiler as possible.
-- The module `Lean.DocString` contains the query interface, which needs to look at additional data
-- to construct user-visible docstrings.
namespace Lean
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) IO.mkRef {}
private builtin_initialize docStringExt : MapDeclarationExtension String mkMapDeclarationExtension
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit :=
builtinDocStrings.modify (·.insert declName docString.removeLeadingSpaces)
def addDocString [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString : String) : m Unit := do
unless ( getEnv).getModuleIdxFor? declName |>.isNone do
throwError s!"invalid doc string, declaration '{declName}' is in an imported module"
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
def addDocString' [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString? : Option String) : m Unit :=
match docString? with
| some docString => addDocString declName docString
| none => return ()
/--
Finds a docstring without performing any alias resolution or enrichment with extra metadata.
Docstrings to be shown to a user should be looked up with `Lean.findDocString?` instead.
-/
def findSimpleDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) :=
if let some docStr := docStringExt.find? env declName then
return some docStr
else if includeBuiltin then
return ( builtinDocStrings.get).find? declName
else
return none
structure ModuleDoc where
doc : String
declarationRange : DeclarationRange
private builtin_initialize moduleDocExt : SimplePersistentEnvExtension ModuleDoc (PersistentArray ModuleDoc) registerSimplePersistentEnvExtension {
addImportedFn := fun _ => {}
addEntryFn := fun s e => s.push e
toArrayFn := fun es => es.toArray
}
def addMainModuleDoc (env : Environment) (doc : ModuleDoc) : Environment :=
moduleDocExt.addEntry env doc
def getMainModuleDoc (env : Environment) : PersistentArray ModuleDoc :=
moduleDocExt.getState env
def getModuleDoc? (env : Environment) (moduleName : Name) : Option (Array ModuleDoc) :=
env.getModuleIdx? moduleName |>.map fun modIdx => moduleDocExt.getModuleEntries env modIdx
def getDocStringText [Monad m] [MonadError m] (stx : TSyntax `Lean.Parser.Command.docComment) : m String :=
match stx.raw[1] with
| Syntax.atom _ val => return val.extract 0 (val.endPos - 2)
| _ => throwErrorAt stx "unexpected doc string{indentD stx.raw[1]}"

View File

@@ -50,3 +50,4 @@ import Lean.Elab.ParseImportsFast
import Lean.Elab.GuardMsgs
import Lean.Elab.CheckTactic
import Lean.Elab.MatchExpr
import Lean.Elab.Tactic.Doc

View File

@@ -1292,6 +1292,7 @@ private partial def elabAppFnId (fIdent : Syntax) (fExplicitUnivs : List Level)
funLVals.foldlM (init := acc) fun acc (f, fIdent, fields) => do
let lvals' := toLVals fields (first := true)
let s observing do
checkDeprecated fIdent f
let f addTermInfo fIdent f expectedType?
let e elabAppLVals f (lvals' ++ lvals) namedArgs args expectedType? explicit ellipsis
if overloaded then ensureHasType expectedType? e else return e
@@ -1424,8 +1425,27 @@ private def getSuccesses (candidates : Array (TermElabResult Expr)) : TermElabM
return false
return true
| _ => return false
if r₂.size == 0 then return r₁ else return r₂
if r₂.size == 0 then
return r₁
if r₂.size == 1 then
return r₂
/-
If there are still more than one solution, discard solutions that have pending metavariables.
We added this extra filter to address regressions introduced after fixing
`isDefEqStuckEx` behavior at `ExprDefEq.lean`.
-/
let r₂ candidates.filterM fun
| .ok _ s => do
try
s.restore
synthesizeSyntheticMVars (postpone := .no)
return true
catch _ =>
return false
| _ => return false
if r₂.size == 0 then
return r₁
return r₂
/--
Throw an error message that describes why each possible interpretation for the overloaded notation and symbols did not work.
We use a nested error message to aggregate the exceptions produced by each failure.

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