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.
111 lines
3.7 KiB
Text
111 lines
3.7 KiB
Text
/-
|
|
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Author: Leonardo de Moura
|
|
-/
|
|
prelude
|
|
import Lean.Expr
|
|
|
|
namespace Lean
|
|
|
|
/--
|
|
Datastructure for representing the "head symbol" of an expression.
|
|
It is the key of `KExprMap`.
|
|
Examples:
|
|
- The head of `f a` is `.const f`
|
|
- The head of `let x := 1; f x` is `.const f`
|
|
- The head of `fun x => fun` is `.lam`
|
|
|
|
`HeadIndex` is a very simple index, and is used in situations where
|
|
we want to find definitionally equal terms, but we want to minimize
|
|
the search by checking only pairs of terms that have the same
|
|
`HeadIndex`.
|
|
-/
|
|
inductive HeadIndex where
|
|
| fvar (fvarId : FVarId)
|
|
| mvar (mvarId : MVarId)
|
|
| const (constName : Name)
|
|
| proj (structName : Name) (idx : Nat)
|
|
| lit (litVal : Literal)
|
|
| sort
|
|
| lam
|
|
| forallE
|
|
deriving Inhabited, BEq, Repr
|
|
|
|
/-- Hash code for a `HeadIndex` value. -/
|
|
protected def HeadIndex.hash : HeadIndex → UInt64
|
|
| fvar fvarId => mixHash 11 <| hash fvarId
|
|
| mvar mvarId => mixHash 13 <| hash mvarId
|
|
| const constName => mixHash 17 <| hash constName
|
|
| proj structName idx => mixHash 19 <| mixHash (hash structName) (hash idx)
|
|
| lit litVal => mixHash 23 <| hash litVal
|
|
| sort => 29
|
|
| lam => 31
|
|
| forallE => 37
|
|
|
|
instance : Hashable HeadIndex := ⟨HeadIndex.hash⟩
|
|
|
|
namespace Expr
|
|
|
|
/-- Return the number of arguments in the given expression with respect to its `HeadIndex` -/
|
|
def headNumArgs (e : Expr) : Nat :=
|
|
go e 0
|
|
where
|
|
go : Expr → Nat → Nat
|
|
| app f _, n => go f (n + 1)
|
|
| letE _ _ _ b _, n => go b n
|
|
| mdata _ e, n => go e n
|
|
| _, n => n
|
|
|
|
/--
|
|
Quick version that may fail if it "hits" a loose bound variable.
|
|
This can happen, for example, if the input expression is of the form.
|
|
```
|
|
let f := fun x => x + 1;
|
|
f 0
|
|
```
|
|
-/
|
|
private def toHeadIndexQuick? : Expr → Option HeadIndex
|
|
| mvar mvarId => HeadIndex.mvar mvarId
|
|
| fvar fvarId => HeadIndex.fvar fvarId
|
|
| const constName _ => HeadIndex.const constName
|
|
| proj structName idx _ => HeadIndex.proj structName idx
|
|
| sort _ => HeadIndex.sort
|
|
| lam .. => HeadIndex.lam
|
|
| forallE .. => HeadIndex.forallE
|
|
| lit v => HeadIndex.lit v
|
|
| app f _ => toHeadIndexQuick? f
|
|
| letE _ _ _ b _ => toHeadIndexQuick? b
|
|
| mdata _ e => toHeadIndexQuick? e
|
|
| _ => none
|
|
|
|
/--
|
|
Slower version of `toHeadIndexQuick?` that "expands" let-declarations to make
|
|
sure we never hit a loose bound variable.
|
|
The performance of the `letE` alternative can be improved, but this function should not be in the hotpath
|
|
since `toHeadIndexQuick?` succeeds most of the time.
|
|
-/
|
|
private partial def toHeadIndexSlow : Expr → HeadIndex
|
|
| mvar mvarId => HeadIndex.mvar mvarId
|
|
| fvar fvarId => HeadIndex.fvar fvarId
|
|
| const constName _ => HeadIndex.const constName
|
|
| proj structName idx _ => HeadIndex.proj structName idx
|
|
| sort _ => HeadIndex.sort
|
|
| lam .. => HeadIndex.lam
|
|
| forallE .. => HeadIndex.forallE
|
|
| lit v => HeadIndex.lit v
|
|
| app f _ => toHeadIndexSlow f
|
|
| letE _ _ v b _ => toHeadIndexSlow (b.instantiate1 v)
|
|
| mdata _ e => toHeadIndexSlow e
|
|
| _ => panic! "unexpected expression kind"
|
|
|
|
/--
|
|
Convert the given expression into a `HeadIndex`.
|
|
-/
|
|
def toHeadIndex (e : Expr) : HeadIndex :=
|
|
match toHeadIndexQuick? e with
|
|
| some i => i
|
|
| none => toHeadIndexSlow e
|
|
|
|
end Expr
|
|
end Lean
|