Compare commits

...

10 Commits

Author SHA1 Message Date
Kim Morrison
fd7377eb54 chore: upstream Subarray.empty 2024-09-29 15:33:54 +10:00
Kyle Miller
96adf04a62 fix: reduce parents in structure command (#5511)
Makes it possible to `extend` another structure through an abbreviation.
Also inserts a `withSynthesize` checkpoint for parents.

Closes #5417
2024-09-29 02:15:07 +00:00
Kyle Miller
0db6daa8f1 feat: actual implementation for #5283 (#5512)
I did a bad git rebase before merging #5283, which reverted it to an
earlier version. This PR has the actual implementation of RFC #5397.
2024-09-29 01:22:12 +00:00
Kyle Miller
130b465aaf feat: generalize elab_as_elim to allow arbitrary motive applications (#5510)
Now the elab-as-elim procedure allows eliminators whose result is an
arbitrary application of the motive. For example, the following is now
accepted. It will generalize `Int.natAbs _` from the expected type.
```lean
@[elab_as_elim]
theorem natAbs_elim {motive : Nat → Prop} (i : Int)
  (hpos : ∀ (n : Nat), i = n → motive n)
  (hneg : ∀ (n : Nat), i = -↑n → motive n) :
  motive (Int.natAbs i) := by sorry
```

This change simplifies the elaborator, since it no longer needs to keep
track of discriminants (which can easily be read off from the return
type of the eliminator) or the difference between "targets" and "extra
arguments" (which are now both "major arguments" that should be eagerly
elaborated).

Closes #4086
2024-09-28 22:30:14 +00:00
Lean stage0 autoupdater
ccdf07b6a1 chore: update stage0 2024-09-28 14:05:01 +00:00
Tobias Grosser
5605e0198a chore: BitVec.Lemmas - drop non-terminal simps (#5499)
`BitVec.Lemmas` contained a couple of non-terminal simps. We turn
non-terminal `simp$`, `simp [`, and `simp at` expressions into `simp
only` to improve code maintainability.
2024-09-28 10:23:28 +00:00
Henrik Böving
5f22ba7789 feat: bv_normalize handle -> False (#5507) 2024-09-28 10:05:16 +00:00
Henrik Böving
16a16898d5 feat: improve bv_normalize rules for Prop and == (#5506) 2024-09-28 09:21:48 +00:00
Mac Malone
4ea76aadd1 refactor: lake: switch new/init default to TOML (#5504)
Changes the default configuration for new Lake packages to TOML.

Closes #4106.
2024-09-28 06:28:50 +00:00
Kim Morrison
ef71f0beab chore: restore @[simp] to upstreamed Nat.lt_off_iff (#5503)
This was upstreamed from Mathlib in #5478, but leaving off the `@[simp]`
attribute, thereby breaking Mathlib. (We could of course add the simp
attribute back in Mathlib, but wherever it lives it should have been in
place at the time we merged -- this way I have to add it temporarily in
Mathlib and then remove it again once it is redundant.)
2024-09-28 04:55:15 +00:00
567 changed files with 400 additions and 255 deletions

View File

@@ -59,6 +59,22 @@ def popFront (s : Subarray α) : Subarray α :=
else
s
/--
The empty subarray.
-/
protected def empty : Subarray α where
array := #[]
start := 0
stop := 0
start_le_stop := Nat.le_refl 0
stop_le_array_size := Nat.le_refl 0
instance : EmptyCollection (Subarray α) :=
Subarray.empty
instance : Inhabited (Subarray α) :=
{}
@[inline] unsafe def forInUnsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (s : Subarray α) (b : β) (f : α β m (ForInStep β)) : m β :=
let sz := USize.ofNat s.stop
let rec @[specialize] loop (i : USize) (b : β) : m β := do

View File

@@ -165,7 +165,8 @@ theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
· simp [getMsb?, h]
· rw [getLsbD_eq_getElem?_getD, getMsb?_eq_getLsb?]
split <;>
· simp
· simp only [getLsb?_eq_getElem?, Bool.and_iff_right_iff_imp, decide_eq_true_eq,
Option.getD_none, Bool.and_eq_false_imp]
intros
omega
@@ -303,7 +304,7 @@ theorem getElem?_zero_ofNat_one : (BitVec.ofNat (w+1) 1)[0]? = some true := by
-- This does not need to be a `@[simp]` theorem as it is already handled by `getElem?_eq_getElem`.
theorem getElem?_zero_ofBool (b : Bool) : (ofBool b)[0]? = some b := by
simp [ofBool, cond_eq_if]
simp only [ofBool, ofNat_eq_ofNat, cond_eq_if]
split <;> simp_all
@[simp] theorem getElem_zero_ofBool (b : Bool) : (ofBool b)[0] = b := by
@@ -332,7 +333,7 @@ theorem getElem_ofBool {b : Bool} {i : Nat} : (ofBool b)[0] = b := by
theorem msb_eq_getLsbD_last (x : BitVec w) :
x.msb = x.getLsbD (w - 1) := by
simp [BitVec.msb, getMsbD, getLsbD]
simp only [BitVec.msb, getMsbD]
rcases w with rfl | w
· simp [BitVec.eq_nil x]
· simp
@@ -360,7 +361,7 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
| 0 =>
simp [BitVec.msb, BitVec.getMsbD] at p
| n + 1 =>
simp [BitVec.msb_eq_decide] at p
simp only [msb_eq_decide, Nat.add_one_sub_one, decide_eq_true_eq] at p
simp only [Nat.add_sub_cancel]
exact p
@@ -420,7 +421,7 @@ theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) :=
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt x = y := by
intro eq
simp [toInt_eq_toNat_cond] at eq
simp only [toInt_eq_toNat_cond] at eq
apply eq_of_toNat_eq
revert eq
have _xlt := x.isLt
@@ -900,8 +901,8 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
rw [ h]
rw [Nat.testBit_two_pow_sub_succ (isLt _)]
· cases w : decide (i < v)
· simp at w
simp [w]
· simp only [decide_eq_false_iff_not, Nat.not_lt] at w
simp only [Bool.false_bne, Bool.false_and]
rw [Nat.testBit_lt_two_pow]
calc BitVec.toNat x < 2 ^ v := isLt _
_ 2 ^ i := Nat.pow_le_pow_of_le_right Nat.zero_lt_two w
@@ -1041,7 +1042,8 @@ theorem shiftLeft_or_distrib (x y : BitVec w) (n : Nat) :
simp only [t]
simp only [decide_True, Nat.sub_sub, Bool.true_and, Nat.add_assoc]
by_cases h₁ : k < w <;> by_cases h₂ : w - (1 + k) < i <;> by_cases h₃ : k + i < w
<;> simp [h₁, h₂, h₃]
<;> simp only [h₁, h₂, h₃, decide_False, h₂, decide_True, Bool.not_true, Bool.false_and, Bool.and_self,
Bool.true_and, Bool.false_eq, Bool.false_and, Bool.not_false]
<;> (first | apply getLsbD_ge | apply Eq.symm; apply getLsbD_ge)
<;> omega
@@ -1496,18 +1498,18 @@ theorem getLsbD_append {x : BitVec n} {y : BitVec m} :
simp only [append_def, getLsbD_or, getLsbD_shiftLeftZeroExtend, getLsbD_setWidth']
by_cases h : i < m
· simp [h]
· simp [h]; simp_all
· simp_all [h]
theorem getElem_append {x : BitVec n} {y : BitVec m} (h : i < n + m) :
(x ++ y)[i] = bif i < m then getLsbD y i else getLsbD x (i - m) := by
simp only [append_def, getElem_or, getElem_shiftLeftZeroExtend, getElem_setWidth']
by_cases h' : i < m
· simp [h']
· simp [h']; simp_all
· simp_all [h']
@[simp] theorem getMsbD_append {x : BitVec n} {y : BitVec m} :
getMsbD (x ++ y) i = bif n i then getMsbD y (i - n) else getMsbD x i := by
simp [append_def]
simp only [append_def]
by_cases h : n i
· simp [h]
· simp [h]
@@ -1515,7 +1517,7 @@ theorem getElem_append {x : BitVec n} {y : BitVec m} (h : i < n + m) :
theorem msb_append {x : BitVec w} {y : BitVec v} :
(x ++ y).msb = bif (w == 0) then (y.msb) else (x.msb) := by
rw [ append_eq, append]
simp [msb_setWidth']
simp only [msb_or, msb_shiftLeftZeroExtend, msb_setWidth']
by_cases h : w = 0
· subst h
simp [BitVec.msb, getMsbD]
@@ -1636,13 +1638,13 @@ theorem shiftRight_shiftRight {w : Nat} (x : BitVec w) (n m : Nat) :
theorem getLsbD_rev (x : BitVec w) (i : Fin w) :
x.getLsbD i.rev = x.getMsbD i := by
simp [getLsbD, getMsbD]
simp only [getLsbD, Fin.val_rev, getMsbD, Fin.is_lt, decide_True, Bool.true_and]
congr 1
omega
theorem getElem_rev {x : BitVec w} {i : Fin w}:
x[i.rev] = x.getMsbD i := by
simp [getMsbD]
simp only [Fin.getElem_fin, Fin.val_rev, getMsbD, Fin.is_lt, decide_True, Bool.true_and]
congr 1
omega
@@ -1667,13 +1669,13 @@ theorem toNat_cons' {x : BitVec w} :
theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
getLsbD (cons b x) i = if i = n then b else getLsbD x i := by
simp only [getLsbD, toNat_cons, Nat.testBit_or]
rw [Nat.testBit_shiftLeft]
simp only [getLsbD, toNat_cons, Nat.testBit_or, Nat.testBit_shiftLeft, ge_iff_le]
rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
· have p1 : ¬(n i) := by omega
have p2 : i n := by omega
simp [p1, p2]
· simp [i_eq_n, testBit_toNat]
· simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_True, Nat.sub_self, Nat.testBit_zero,
Bool.true_and, testBit_toNat, getLsbD_ge, Bool.or_false, reduceIte]
cases b <;> trivial
· have p1 : i n := by omega
have p2 : i - n 0 := by omega
@@ -1687,7 +1689,8 @@ theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
· have p1 : ¬(n i) := by omega
have p2 : i n := by omega
simp [p1, p2]
· simp [i_eq_n, testBit_toNat]
· simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_True, Nat.sub_self, Nat.testBit_zero,
Bool.true_and, testBit_toNat, getLsbD_ge, Bool.or_false, reduceIte]
cases b <;> trivial
· have p1 : i n := by omega
have p2 : i - n 0 := by omega
@@ -1952,7 +1955,7 @@ theorem toInt_neg {x : BitVec w} :
Int.bmod_add_cancel]
by_cases h : x.toNat < ((2 ^ w) + 1) / 2
· rw [Int.bmod_pos (x := x.toNat)]
all_goals simp [toNat_mod_cancel', h]
all_goals simp only [toNat_mod_cancel']
norm_cast
· rw [Int.bmod_neg (x := x.toNat)]
· simp only [toNat_mod_cancel']
@@ -1966,7 +1969,7 @@ theorem toInt_neg {x : BitVec w} :
theorem sub_toAdd {n} (x y : BitVec n) : x - y = x + - y := by
apply eq_of_toNat_eq
simp
simp only [toNat_sub, toNat_add, toNat_neg, Nat.add_mod_mod]
rw [Nat.add_comm]
@[simp] theorem neg_zero (n:Nat) : -BitVec.ofNat n 0 = BitVec.ofNat n 0 := by apply eq_of_toNat_eq ; simp

View File

@@ -634,7 +634,7 @@ theorem lt_succ_of_lt (h : a < b) : a < succ b := le_succ_of_le h
theorem lt_add_one_of_lt (h : a < b) : a < b + 1 := le_succ_of_le h
theorem lt_one_iff : n < 1 n = 0 := Nat.lt_succ_iff.trans <| by rw [le_zero_eq]
@[simp] theorem lt_one_iff : n < 1 n = 0 := Nat.lt_succ_iff.trans <| by rw [le_zero_eq]
theorem succ_pred_eq_of_ne_zero : {n}, n 0 succ (pred n) = n
| _+1, _ => rfl

View File

@@ -35,4 +35,4 @@ theorem neZero_iff {n : R} : NeZero n ↔ n ≠ 0 :=
fun h h.out, NeZero.mk
@[simp] theorem neZero_zero_iff_false {α : Type _} [Zero α] : NeZero (0 : α) False :=
fun h h.ne rfl, fun h h.elim
fun _ NeZero.ne (0 : α) rfl, fun h h.elim

View File

@@ -411,21 +411,23 @@ private def finalize : M Expr := do
synthesizeAppInstMVars
return e
/-- Return `true` if there is a named argument that depends on the next argument. -/
private def anyNamedArgDependsOnCurrent : M Bool := do
/--
Returns a named argument that depends on the next argument, otherwise `none`.
-/
private def findNamedArgDependsOnCurrent? : M (Option NamedArg) := do
let s get
if s.namedArgs.isEmpty then
return false
return none
else
forallTelescopeReducing s.fType fun xs _ => do
let curr := xs[0]!
for i in [1:xs.size] do
let xDecl xs[i]!.fvarId!.getDecl
if s.namedArgs.any fun arg => arg.name == xDecl.userName then
if let some arg := s.namedArgs.find? fun arg => arg.name == xDecl.userName then
/- Remark: a default value at `optParam` does not count as a dependency -/
if ( exprDependsOn xDecl.type.cleanupAnnotations curr.fvarId!) then
return true
return false
return arg
return none
/-- Return `true` if there are regular or named arguments to be processed. -/
@@ -514,8 +516,9 @@ where
mutual
/--
Create a fresh local variable with the current binder name and argument type, add it to `etaArgs` and `f`,
and then execute the main loop.-/
Create a fresh local variable with the current binder name and argument type, add it to `etaArgs` and `f`,
and then execute the main loop.
-/
private partial def addEtaArg (argName : Name) : M Expr := do
let n getBindingName
let type getArgExpectedType
@@ -524,6 +527,9 @@ mutual
addNewArg argName x
main
/--
Create a fresh metavariable for the implicit argument, add it to `f`, and thn execute the main loop.
-/
private partial def addImplicitArg (argName : Name) : M Expr := do
let argType getArgExpectedType
let arg if ( isNextOutParamOfLocalInstanceAndResult) then
@@ -541,35 +547,47 @@ mutual
main
/--
Process a `fType` of the form `(x : A) → B x`.
This method assume `fType` is a function type -/
Process a `fType` of the form `(x : A) → B x`.
This method assume `fType` is a function type.
-/
private partial def processExplicitArg (argName : Name) : M Expr := do
match ( get).args with
| arg::args =>
if ( anyNamedArgDependsOnCurrent) then
-- Note: currently the following test never succeeds in explicit mode since `@x.f` notation does not exist.
if let some true := NamedArg.suppressDeps <$> ( findNamedArgDependsOnCurrent?) then
/-
We treat the explicit argument `argName` as implicit if we have named arguments that depend on it.
The idea is that this explicit argument can be inferred using the type of the named argument one.
Note that we also use this approach in the branch where there are no explicit arguments left.
This is important to make sure the system behaves in a uniform way.
Moreover, users rely on this behavior. For example, consider the example on issue #1851
We treat the explicit argument `argName` as implicit
if we have a named arguments that depends on it whose `suppressDeps` flag set to `true`.
The motivation for this is class projections (issue #1851).
In some cases, class projections can have explicit parameters. For example, in
```
class Approx {α : Type} (a : α) (X : Type) : Type where
val : X
variable {α β X Y : Type} {f' : α → β} {x' : α} [f : Approx f' (X → Y)] [x : Approx x' X]
#check f.val
#check f.val x.val
```
The type of `Approx.val` is `{α : Type} → (a : α) → {X : Type} → [self : Approx a X] → X`
Note that the argument `a` is explicit since there is no way to infer it from the expected
type or the type of other explicit arguments.
Recall that `f.val` is sugar for `Approx.val (self := f)`. In both `#check` commands above
the user assumed that `a` does not need to be provided since it can be inferred from the type
of `self`.
We used to that only in the branch where `(← get).args` was empty, but it created an asymmetry
because `#check f.val` worked as expected, but one would have to write `#check f.val _ x.val`
the type of `Approx.val` is `{α : Type} → (a : α) → {X : Type} → [self : Approx a X] → X`.
Note that the parameter `a` is explicit since there is no way to infer it from the expected
type or from the types of other explicit parameters.
Being a parameter of the class, `a` is determined by the type of `self`.
Consider
```
variable {α β X Y : Type} {f' : α → β} {x' : α} [f : Approx f' (X → Y)]
```
Recall that `f.val` is, to first approximation, sugar for `Approx.val (self := f)`.
Without further refinement, this would expand to `fun f'' : α → β => Approx.val f'' f`,
which is a type error, since `f''` must be defeq to `f'`.
Furthermore, with projection notation, users expect all structure parameters
to be uniformly implicit; after all, they are determined by `self`.
To handle this, the `(self := f)` named argument is annotated with the `suppressDeps` flag.
This causes the `a` parameter to become implicit, and `f.val` instead expands to `Approx.val f' f`.
This feature previously was enabled for *all* explicit arguments, which confused users
and was frequently reported as a bug (issue #1867).
Now it is only enabled for the `self` argument in structure projections.
We used to do this only when `(← get).args` was empty,
but it created an asymmetry because `f.val` worked as expected,
yet one would have to write `f.val _ x` when there are further arguments.
-/
return ( addImplicitArg argName)
propagateExpectedType arg
@@ -609,17 +627,20 @@ mutual
| false, _, some _ =>
throwError "invalid autoParam, argument must be a constant"
| _, _, _ =>
if !( get).namedArgs.isEmpty then
if ( anyNamedArgDependsOnCurrent) then
addImplicitArg argName
else if ( read).ellipsis then
if ( read).ellipsis then
addImplicitArg argName
else if !( get).namedArgs.isEmpty then
if let some _ findNamedArgDependsOnCurrent? then
/-
Dependencies of named arguments cannot be turned into eta arguments
since they are determined by the named arguments.
Instead we can turn them into implicit arguments.
-/
addImplicitArg argName
else
addEtaArg argName
else if !( read).explicit then
if ( read).ellipsis then
addImplicitArg argName
else if ( fTypeHasOptAutoParams) then
if ( fTypeHasOptAutoParams) then
addEtaArg argName
else
finalize
@@ -647,37 +668,17 @@ mutual
finalize
/--
Process a `fType` of the form `[x : A] → B x`.
This method assume `fType` is a function type -/
Process a `fType` of the form `[x : A] → B x`.
This method assume `fType` is a function type.
-/
private partial def processInstImplicitArg (argName : Name) : M Expr := do
if ( read).explicit then
if ( anyNamedArgDependsOnCurrent) then
/-
See the note in processExplicitArg about `anyNamedArgDependsOnCurrent`.
For consistency, instance implicit arguments should always become implicit if a named argument depends on them.
If we do not do this check here, then the `nextArgHole?` branch being before `processExplicitArg`
would result in counterintuitive behavior.
For example, without this block of code, in the following the `_` is optional.
When it is omitted, `processExplicitArg` sees that `h` depends on the `Decidable` instance
so makes the `Decidable` instance argument become implicit.
When it is `_`, the `nextArgHole?` branch allows it to be present.
The third example counterintuitively yields a "function expected" error.
```lean
theorem foo {p : Prop} [Decidable p] (h : ite p x y = x) : p := sorry
variable {p : Prop} [Decidable p] {α : Type} (x y : α) (h : ite p x y = x)
example : p := @foo (h := h)
example : p := @foo (h := h) _
example : p := @foo (h := h) inferInstance
```
-/
addImplicitArg argName
else if let some stx nextArgHole? then
/- Recall that if '@' has been used, and the argument is '_', then we still use type class resolution -/
if let some stx nextArgHole? then
-- We still use typeclass resolution for `_` arguments.
-- This behavior can be suppressed with `(_)`.
let ty getArgExpectedType
let arg mkInstMVar ty
addTermInfo' stx arg ty ( getLCtx)
addTermInfo' stx arg ty
modify fun s => { s with args := s.args.tail! }
main
else
@@ -721,6 +722,104 @@ end
end ElabAppArgs
/-! # Eliminator-like function application elaborator -/
/--
Information about an eliminator used by the elab-as-elim elaborator.
This is not to be confused with `Lean.Meta.ElimInfo`, which is for `induction` and `cases`.
The elab-as-elim routine is less restrictive in what counts as an eliminator, and it doesn't need
to have a strict notion of what is a "target" — all it cares about are
1. that the return type of a function is of the form `m ...` where `m` is a parameter
(unlike `induction` and `cases` eliminators, the arguments to `m`, known as "discriminants",
can be any expressions, not just parameters), and
2. which arguments should be eagerly elaborated, to make discriminants be as elaborated as
possible for the expected type generalization procedure,
and which should be postponed (since they are the "minor premises").
Note that the routine isn't doing induction/cases *on* particular expressions.
The purpose of elab-as-elim is to successfully solve the higher-order unification problem
between the return type of the function and the expected type.
-/
structure ElabElimInfo where
/-- The eliminator. -/
elimExpr : Expr
/-- The type of the eliminator. -/
elimType : Expr
/-- The position of the motive parameter. -/
motivePos : Nat
/--
Positions of "major" parameters (those that should be eagerly elaborated
because they can contribute to the motive inference procedure).
All parameters that are neither the motive nor a major parameter are "minor" parameters.
The major parameters include all of the parameters that transitively appear in the motive's arguments,
as well as "first-order" arguments that include such parameters,
since they too can help with elaborating discriminants.
For example, in the following theorem the argument `h : a = b`
should be elaborated eagerly because it contains `b`, which occurs in `motive b`.
```
theorem Eq.subst' {α} {motive : α → Prop} {a b : α} (h : a = b) : motive a → motive b
```
For another example, the term `isEmptyElim (α := α)` is an underapplied eliminator, and it needs
argument `α` to be elaborated eagerly to create a type-correct motive.
```
def isEmptyElim [IsEmpty α] {p : α → Sort _} (a : α) : p a := ...
example {α : Type _} [IsEmpty α] : id (α → False) := isEmptyElim (α := α)
```
-/
majorsPos : Array Nat := #[]
deriving Repr, Inhabited
def getElabElimExprInfo (elimExpr : Expr) : MetaM ElabElimInfo := do
let elimType inferType elimExpr
trace[Elab.app.elab_as_elim] "eliminator {indentExpr elimExpr}\nhas type{indentExpr elimType}"
forallTelescopeReducing elimType fun xs type => do
let motive := type.getAppFn
let motiveArgs := type.getAppArgs
unless motive.isFVar do
throwError "unexpected eliminator resulting type{indentExpr type}"
let motiveType inferType motive
forallTelescopeReducing motiveType fun motiveParams motiveResultType => do
unless motiveParams.size == motiveArgs.size do
throwError "unexpected number of arguments at motive type{indentExpr motiveType}"
unless motiveResultType.isSort do
throwError "motive result type must be a sort{indentExpr motiveType}"
let some motivePos pure (xs.indexOf? motive) |
throwError "unexpected eliminator type{indentExpr elimType}"
/-
Compute transitive closure of fvars appearing in arguments to the motive.
These are the primary set of major parameters.
-/
let initMotiveFVars : CollectFVars.State := motiveArgs.foldl (init := {}) collectFVars
let motiveFVars xs.size.foldRevM (init := initMotiveFVars) fun i s => do
let x := xs[i]!
if s.fvarSet.contains x.fvarId! then
return collectFVars s ( inferType x)
else
return s
/- Collect the major parameter positions -/
let mut majorsPos := #[]
for i in [:xs.size] do
let x := xs[i]!
unless motivePos == i do
let xType x.fvarId!.getType
/-
We also consider "first-order" types because we can reliably "extract" information from them.
We say a term is "first-order" if all applications are of the form `f ...` where `f` is a constant.
-/
let isFirstOrder (e : Expr) : Bool := Option.isNone <| e.find? fun e => e.isApp && !e.getAppFn.isConst
if motiveFVars.fvarSet.contains x.fvarId!
|| (isFirstOrder xType
&& Option.isSome (xType.find? fun e => e.isFVar && motiveFVars.fvarSet.contains e.fvarId!)) then
majorsPos := majorsPos.push i
trace[Elab.app.elab_as_elim] "motivePos: {motivePos}"
trace[Elab.app.elab_as_elim] "majorsPos: {majorsPos}"
return { elimExpr, elimType, motivePos, majorsPos }
def getElabElimInfo (elimName : Name) : MetaM ElabElimInfo := do
getElabElimExprInfo ( mkConstWithFreshMVarLevels elimName)
builtin_initialize elabAsElim : TagAttribute
registerTagAttribute `elab_as_elim
"instructs elaborator that the arguments of the function application should be elaborated as were an eliminator"
@@ -735,33 +834,15 @@ builtin_initialize elabAsElim : TagAttribute ←
let info getConstInfo declName
if ( hasOptAutoParams info.type) then
throwError "[elab_as_elim] attribute cannot be used in declarations containing optional and auto parameters"
discard <| getElimInfo declName
discard <| getElabElimInfo declName
go.run' {} {}
/-! # Eliminator-like function application elaborator -/
namespace ElabElim
/-- Context of the `elab_as_elim` elaboration procedure. -/
structure Context where
elimInfo : ElimInfo
elimInfo : ElabElimInfo
expectedType : Expr
/--
Position of additional arguments that should be elaborated eagerly
because they can contribute to the motive inference procedure.
For example, in the following theorem the argument `h : a = b`
should be elaborated eagerly because it contains `b` which occurs
in `motive b`.
```
theorem Eq.subst' {α} {motive : α → Prop} {a b : α} (h : a = b) : motive a → motive b
```
For another example, the term `isEmptyElim (α := α)` is an underapplied eliminator, and it needs
argument `α` to be elaborated eagerly to create a type-correct motive.
```
def isEmptyElim [IsEmpty α] {p : α → Sort _} (a : α) : p a := ...
example {α : Type _} [IsEmpty α] : id (α → False) := isEmptyElim (α := α)
```
-/
extraArgsPos : Array Nat
/-- State of the `elab_as_elim` elaboration procedure. -/
structure State where
@@ -773,8 +854,6 @@ structure State where
namedArgs : List NamedArg
/-- User-provided arguments that still have to be processed. -/
args : List Arg
/-- Discriminants (targets) processed so far. -/
discrs : Array (Option Expr)
/-- Instance implicit arguments collected so far. -/
instMVars : Array MVarId := #[]
/-- Position of the next argument to be processed. We use it to decide whether the argument is the motive or a discriminant. -/
@@ -820,7 +899,7 @@ def finalize : M Expr := do
let some motive := ( get).motive?
| throwError "failed to elaborate eliminator, insufficient number of arguments"
trace[Elab.app.elab_as_elim] "motive: {motive}"
forallTelescope ( get).fType fun xs _ => do
forallTelescope ( get).fType fun xs fType => do
trace[Elab.app.elab_as_elim] "xs: {xs}"
let mut expectedType := ( read).expectedType
trace[Elab.app.elab_as_elim] "expectedType:{indentD expectedType}"
@@ -829,6 +908,7 @@ def finalize : M Expr := do
let mut f := ( get).f
if xs.size > 0 then
-- under-application, specialize the expected type using `xs`
-- Note: if we ever wanted to support optParams and autoParams, this is where it could be.
assert! ( get).args.isEmpty
for x in xs do
let .forallE _ t b _ whnf expectedType | throwInsufficient
@@ -845,18 +925,11 @@ def finalize : M Expr := do
trace[Elab.app.elab_as_elim] "expectedType after processing:{indentD expectedType}"
let result := mkAppN f xs
trace[Elab.app.elab_as_elim] "result:{indentD result}"
let mut discrs := ( get).discrs
let idx := ( get).idx
if discrs.any Option.isNone then
for i in [idx:idx + xs.size], x in xs do
if let some tidx := ( read).elimInfo.targetsPos.indexOf? i then
discrs := discrs.set! tidx x
if let some idx := discrs.findIdx? Option.isNone then
-- This should not happen.
trace[Elab.app.elab_as_elim] "Internal error, missing target with index {idx}"
throwError "failed to elaborate eliminator, insufficient number of arguments"
trace[Elab.app.elab_as_elim] "discrs: {discrs.map Option.get!}"
let motiveVal mkMotive (discrs.map Option.get!) expectedType
unless fType.getAppFn == ( get).motive? do
throwError "Internal error, eliminator target type isn't an application of the motive"
let discrs := fType.getAppArgs
trace[Elab.app.elab_as_elim] "discrs: {discrs}"
let motiveVal mkMotive discrs expectedType
unless ( isTypeCorrect motiveVal) do
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motiveVal}"
unless ( isDefEq motive motiveVal) do
@@ -890,10 +963,6 @@ def getNextArg? (binderName : Name) (binderInfo : BinderInfo) : M (LOption Arg)
def setMotive (motive : Expr) : M Unit :=
modify fun s => { s with motive? := motive }
/-- Push the given expression into the `discrs` field in the state, where `i` is which target it is for. -/
def addDiscr (i : Nat) (discr : Expr) : M Unit :=
modify fun s => { s with discrs := s.discrs.set! i discr }
/-- Elaborate the given argument with the given expected type. -/
private def elabArg (arg : Arg) (argExpectedType : Expr) : M Expr := do
match arg with
@@ -936,18 +1005,13 @@ partial def main : M Expr := do
mkImplicitArg binderType binderInfo
setMotive motive
addArgAndContinue motive
else if let some tidx := ( read).elimInfo.targetsPos.indexOf? idx then
else if ( read).elimInfo.majorsPos.contains idx then
match ( getNextArg? binderName binderInfo) with
| .some arg => let discr elabArg arg binderType; addDiscr tidx discr; addArgAndContinue discr
| .some arg => let discr elabArg arg binderType; addArgAndContinue discr
| .undef => finalize
| .none => let discr mkImplicitArg binderType binderInfo; addDiscr tidx discr; addArgAndContinue discr
| .none => let discr mkImplicitArg binderType binderInfo; addArgAndContinue discr
else match ( getNextArg? binderName binderInfo) with
| .some (.stx stx) =>
if ( read).extraArgsPos.contains idx then
let arg elabArg (.stx stx) binderType
addArgAndContinue arg
else
addArgAndContinue ( postponeElabTerm stx binderType)
| .some (.stx stx) => addArgAndContinue ( postponeElabTerm stx binderType)
| .some (.expr val) => addArgAndContinue ( ensureArgType ( get).f val binderType)
| .undef => finalize
| .none => addArgAndContinue ( mkImplicitArg binderType binderInfo)
@@ -1001,13 +1065,10 @@ def elabAppArgs (f : Expr) (namedArgs : Array NamedArg) (args : Array Arg)
let some expectedType := expectedType? | throwError "failed to elaborate eliminator, expected type is not available"
let expectedType instantiateMVars expectedType
if expectedType.getAppFn.isMVar then throwError "failed to elaborate eliminator, expected type is not available"
let extraArgsPos getElabAsElimExtraArgsPos elimInfo
trace[Elab.app.elab_as_elim] "extraArgsPos: {extraArgsPos}"
ElabElim.main.run { elimInfo, expectedType, extraArgsPos } |>.run' {
ElabElim.main.run { elimInfo, expectedType } |>.run' {
f, fType
args := args.toList
namedArgs := namedArgs.toList
discrs := mkArray elimInfo.targetsPos.size none
}
else
ElabAppArgs.main.run { explicit, ellipsis, resultIsOutParamSupport } |>.run' {
@@ -1018,12 +1079,12 @@ def elabAppArgs (f : Expr) (namedArgs : Array NamedArg) (args : Array Arg)
}
where
/-- Return `some info` if we should elaborate as an eliminator. -/
elabAsElim? : TermElabM (Option ElimInfo) := do
elabAsElim? : TermElabM (Option ElabElimInfo) := do
unless ( read).heedElabAsElim do return none
if explicit || ellipsis then return none
let .const declName _ := f | return none
unless ( shouldElabAsElim declName) do return none
let elimInfo getElimInfo declName
let elimInfo getElabElimInfo declName
forallTelescopeReducing ( inferType f) fun xs _ => do
/- Process arguments similar to `Lean.Elab.Term.ElabElim.main` to see if the motive has been
provided, in which case we use the standard app elaborator.
@@ -1054,41 +1115,6 @@ where
return none
| _, _ => return some elimInfo
/--
Collect extra argument positions that must be elaborated eagerly when using `elab_as_elim`.
The idea is that they contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
-/
getElabAsElimExtraArgsPos (elimInfo : ElimInfo) : MetaM (Array Nat) := do
forallTelescope elimInfo.elimType fun xs type => do
let targets := type.getAppArgs
/- Compute transitive closure of fvars appearing in the motive and the targets. -/
let initMotiveFVars : CollectFVars.State := targets.foldl (init := {}) collectFVars
let motiveFVars xs.size.foldRevM (init := initMotiveFVars) fun i s => do
let x := xs[i]!
if elimInfo.motivePos == i || elimInfo.targetsPos.contains i || s.fvarSet.contains x.fvarId! then
return collectFVars s ( inferType x)
else
return s
/- Collect the extra argument positions -/
let mut extraArgsPos := #[]
for i in [:xs.size] do
let x := xs[i]!
unless elimInfo.motivePos == i || elimInfo.targetsPos.contains i do
let xType x.fvarId!.getType
/- We only consider "first-order" types because we can reliably "extract" information from them. -/
if motiveFVars.fvarSet.contains x.fvarId!
|| (isFirstOrder xType
&& Option.isSome (xType.find? fun e => e.isFVar && motiveFVars.fvarSet.contains e.fvarId!)) then
extraArgsPos := extraArgsPos.push i
return extraArgsPos
/-
Helper function for implementing `elab_as_elim`.
We say a term is "first-order" if all applications are of the form `f ...` where `f` is a constant.
-/
isFirstOrder (e : Expr) : Bool :=
Option.isNone <| e.find? fun e =>
e.isApp && !e.getAppFn.isConst
/-- Auxiliary inductive datatype that represents the resolution of an `LVal`. -/
inductive LValResolution where
@@ -1253,7 +1279,7 @@ private partial def mkBaseProjections (baseStructName : Name) (structName : Name
let mut e := e
for projFunName in path do
let projFn mkConst projFunName
e elabAppArgs projFn #[{ name := `self, val := Arg.expr e }] (args := #[]) (expectedType? := none) (explicit := false) (ellipsis := false)
e elabAppArgs projFn #[{ name := `self, val := Arg.expr e, suppressDeps := true }] (args := #[]) (expectedType? := none) (explicit := false) (ellipsis := false)
return e
private def typeMatchesBaseName (type : Expr) (baseName : Name) : MetaM Bool := do
@@ -1337,10 +1363,10 @@ private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (exp
let projFn mkConst info.projFn
let projFn addProjTermInfo lval.getRef projFn
if lvals.isEmpty then
let namedArgs addNamedArg namedArgs { name := `self, val := Arg.expr f }
let namedArgs addNamedArg namedArgs { name := `self, val := Arg.expr f, suppressDeps := true }
elabAppArgs projFn namedArgs args expectedType? explicit ellipsis
else
let f elabAppArgs projFn #[{ name := `self, val := Arg.expr f }] #[] (expectedType? := none) (explicit := false) (ellipsis := false)
let f elabAppArgs projFn #[{ name := `self, val := Arg.expr f, suppressDeps := true }] #[] (expectedType? := none) (explicit := false) (ellipsis := false)
loop f lvals
else
unreachable!

View File

@@ -9,18 +9,22 @@ import Lean.Elab.Term
namespace Lean.Elab.Term
/--
Auxiliary inductive datatype for combining unelaborated syntax
and already elaborated expressions. It is used to elaborate applications. -/
Auxiliary inductive datatype for combining unelaborated syntax
and already elaborated expressions. It is used to elaborate applications.
-/
inductive Arg where
| stx (val : Syntax)
| expr (val : Expr)
deriving Inhabited
/-- Named arguments created using the notation `(x := val)` -/
/-- Named arguments created using the notation `(x := val)`. -/
structure NamedArg where
ref : Syntax := Syntax.missing
name : Name
val : Arg
/-- If `true`, then make all parameters that depend on this one become implicit.
This is used for projection notation, since structure parameters might be explicit for classes. -/
suppressDeps : Bool := false
deriving Inhabited
/--

View File

@@ -468,7 +468,8 @@ where
if h : i < view.parents.size then
let parentStx := view.parents.get i, h
withRef parentStx do
let parentType Term.elabType parentStx
let parentType Term.withSynthesize <| Term.elabType parentStx
let parentType whnf parentType
let parentStructName getStructureName parentType
if let some existingFieldName findExistingField? infos parentStructName then
if structureDiamondWarning.get ( getOptions) then

View File

@@ -228,8 +228,16 @@ builtin_dsimproc [simp, seval] reduceOfNat (BitVec.ofNat _ _) := fun e => do
if bv.toNat == v then return .continue -- already normalized
return .done <| toExpr (BitVec.ofNat n v)
/-- Simplification procedure for `=` on `BitVec`s. -/
builtin_simproc [simp, seval] reduceEq (( _ : BitVec _) = _) := reduceBinPred ``Eq 3 (. = .)
/-- Simplification procedure for `≠` on `BitVec`s. -/
builtin_simproc [simp, seval] reduceNe (( _ : BitVec _) _) := reduceBinPred ``Ne 3 (. .)
/-- Simplification procedure for `==` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceBEq (( _ : BitVec _) == _) :=
reduceBoolPred ``BEq.beq 4 (· == ·)
/-- Simplification procedure for `!=` on `BitVec`s. -/
builtin_dsimproc [simp, seval] reduceBNe (( _ : BitVec _) != _) :=
reduceBoolPred ``bne 4 (· != ·)
/-- Simplification procedure for `<` on `BitVec`s. -/
builtin_simproc [simp, seval] reduceLT (( _ : BitVec _) < _) := reduceBinPred ``LT.lt 4 (· < ·)

View File

@@ -13,16 +13,29 @@ This module contains the `Prop` simplifying part of the `bv_normalize` simp set.
namespace Std.Tactic.BVDecide
namespace Frontend.Normalize
attribute [bv_normalize] not_true
attribute [bv_normalize] ite_true
attribute [bv_normalize] ite_false
attribute [bv_normalize] dite_true
attribute [bv_normalize] dite_false
attribute [bv_normalize] and_true
attribute [bv_normalize] true_and
attribute [bv_normalize] and_false
attribute [bv_normalize] false_and
attribute [bv_normalize] or_true
attribute [bv_normalize] true_or
attribute [bv_normalize] eq_iff_iff
attribute [bv_normalize] or_false
attribute [bv_normalize] false_or
attribute [bv_normalize] iff_true
attribute [bv_normalize] true_iff
attribute [bv_normalize] iff_false
attribute [bv_normalize] false_iff
attribute [bv_normalize] false_implies
attribute [bv_normalize] imp_false
attribute [bv_normalize] implies_true
attribute [bv_normalize] true_implies
attribute [bv_normalize] not_true
attribute [bv_normalize] not_false_iff
attribute [bv_normalize] eq_iff_iff
end Frontend.Normalize
end Std.Tactic.BVDecide

View File

@@ -10,7 +10,10 @@ inductive ConfigLang
| lean | toml
deriving Repr, DecidableEq
instance : Inhabited ConfigLang := .lean
/-- Lake's default configuration language. -/
abbrev ConfigLang.default : ConfigLang := .toml
instance : Inhabited ConfigLang := .default
def ConfigLang.ofString? : String Option ConfigLang
| "lean" => some .lean

View File

@@ -270,7 +270,7 @@ protected def DependencySrc.decodeToml (t : Table) (ref := Syntax.missing) : Exc
instance : DecodeToml DependencySrc := fun v => do DependencySrc.decodeToml ( v.decodeTable) v.ref
protected def Dependency.decodeToml (t : Table) (ref := Syntax.missing) : Except (Array DecodeError) Dependency := ensureDecode do
let name stringToLegalOrSimpleName <$> t.tryDecode `name ref
let name stringToLegalOrSimpleName <$> t.tryDecode `name ref
let rev? t.tryDecode? `rev
let src? : Option DependencySrc id do
if let some dir t.tryDecode? `path then
@@ -316,6 +316,7 @@ def loadTomlConfig (dir relDir relConfigFile : FilePath) : LogIO Package := do
let leanLibConfigs mkRBArray (·.name) <$> table.tryDecodeD `lean_lib #[]
let leanExeConfigs mkRBArray (·.name) <$> table.tryDecodeD `lean_exe #[]
let defaultTargets table.tryDecodeD `defaultTargets #[]
let defaultTargets := defaultTargets.map stringToLegalOrSimpleName
let depConfigs table.tryDecodeD `require #[]
return {
dir, relDir, relConfigFile

View File

@@ -1,9 +1,9 @@
# Lake
Lake (Lean Make) is the new build system and package manager for Lean 4.
With Lake, the package's configuration is written in Lean inside a dedicated `lakefile.lean` stored in the root of the package's directory.
Lake configurations can be written in Lean or TOML and are conventionally stored in a `lakefile` in the root directory of package.
Each `lakefile.lean` includes a `package` declaration (akin to `main`) which defines the package's basic configuration. It also typically includes build configurations for different targets (e.g., Lean libraries and binary executables) and Lean scripts to run on the command line (via `lake script run`).
A Lake configuration file defines the package's basic configuration. It also typically includes build configurations for different targets (e.g., Lean libraries and binary executables) and Lean scripts to run on the command line (via `lake script run`).
***This README provides information about Lake relative to the current commit. If you are looking for documentation for the Lake version shipped with a given Lean release, you should look at the README of that version.***
@@ -63,7 +63,7 @@ Hello/ # library source files; accessible via `import Hello.*`
... # additional files should be added here
Hello.lean # library root; imports standard modules from Hello
Main.lean # main file of the executable (contains `def main`)
lakefile.lean # Lake package configuration
lakefile.toml # Lake package configuration
lean-toolchain # the Lean version used by the package
.gitignore # excludes system-specific files (e.g. `build`) from Git
```
@@ -90,23 +90,21 @@ def main : IO Unit :=
IO.println s!"Hello, {hello}!"
```
Lake also creates a basic `lakefile.lean` for the package along with a `lean-toolchain` file that contains the name of the Lean toolchain Lake belongs to, which tells [`elan`](https://github.com/leanprover/elan) to use that Lean toolchain for the package.
Lake also creates a basic `lakefile.toml` for the package along with a `lean-toolchain` file that contains the name of the Lean toolchain Lake belongs to, which tells [`elan`](https://github.com/leanprover/elan) to use that Lean toolchain for the package.
**lakefile.lean**
```lean
import Lake
open Lake DSL
**lakefile.toml**
```toml
name = "hello"
version = "0.1.0"
defaultTargets = ["hello"]
package «hello» where
-- add package configuration options here
[[lean_lib]]
name = "Hello"
lean_lib «Hello» where
-- add library configuration options here
@[default_target]
lean_exe «hello» where
root := `Main
[[lean_exe]]
name = "hello"
root = "Main"
```
The command `lake build` is used to build the package (and its [dependencies](#adding-dependencies), if it has them) into a native executable. The result will be placed in `.lake/build/bin`. The command `lake clean` deletes `build`.

View File

@@ -20,7 +20,7 @@ fi
# https://github.com/leanprover/lake/issues/119
# a@1/init
$LAKE new a lib
$LAKE new a lib.lean
pushd a
git checkout -b master
git add .
@@ -31,7 +31,7 @@ git tag init
popd
# b@1: require a@master, manifest a@1
$LAKE new b lib
$LAKE new b lib.lean
pushd b
git checkout -b master
cat >>lakefile.lean <<EOF
@@ -52,7 +52,7 @@ git commit -am 'second commit in a'
popd
# c@1: require a@master, manifest a@2
$LAKE new c lib
$LAKE new c lib.lean
pushd c
git checkout -b master
cat >>lakefile.lean <<EOF
@@ -67,7 +67,7 @@ git commit -am 'first commit in c'
popd
# d@1: require b@master c@master => a, manifest a@1 b@1 c@1
$LAKE new d lib
$LAKE new d lib.lean
pushd d
git checkout -b master
cat >>lakefile.lean <<EOF

View File

@@ -38,7 +38,7 @@ done
# Test default (std) template
$LAKE new hello
$LAKE new hello .lean
$LAKE -d hello exe hello
test -f hello/.lake/build/lib/Hello.olean
rm -rf hello
@@ -49,7 +49,7 @@ rm -rf hello
# Test exe template
$LAKE new hello exe
$LAKE new hello exe.lean
test -f hello/Main.lean
$LAKE -d hello exe hello
rm -rf hello
@@ -60,7 +60,7 @@ rm -rf hello
# Test lib template
$LAKE new hello lib
$LAKE new hello lib.lean
$LAKE -d hello build Hello
test -f hello/.lake/build/lib/Hello.olean
rm -rf hello
@@ -71,7 +71,7 @@ rm -rf hello
# Test math template
$LAKE new qed math || true # ignore toolchain download errors
$LAKE new qed math.lean || true # ignore toolchain download errors
# Remove the require, since we do not wish to download mathlib during tests
sed_i '/^require.*/{N;d;}' qed/lakefile.lean
$LAKE -d qed build Qed

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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