Implements a new method to generate instance names for anonymous instances that uses a heuristic that tends to produce shorter names. A design goal is to make them relatively unique within projects and definitely unique across projects, while also using accessible names so that they can be referred to as needed, both in Lean code and in discussions. The new method also takes into account binders provided to the instance, and it adds project-based suffixes. Despite this, a median new name is 73% its original auto-generated length. (Compare: [old generated names](https://gist.github.com/kmill/b72bb43f5b01dafef41eb1d2e57a8237) and [new generated names](https://gist.github.com/kmill/393acc82e7a8d67fc7387829f4ed547e).) Some notes: * The naming is sensitive to what is explicitly provided as a binder vs what is provided via a `variable`. It does not make use of `variable`s since, when names are generated, it is not yet known which variables are used in the body of the instance. * If the instance name refers to declarations in the current "project" (given by the root module), then it does not add a suffix. Otherwise, it adds the project name as a suffix to protect against cross-project collisions. * `set_option trace.Elab.instance.mkInstanceName true` can be used to see what name the auto-generator would give, even if the instance already has an explicit name. There were a number of instances that were referred to explicitly in meta code, and these have been given explicit names. Removes the unused `Lean.Elab.mkFreshInstanceName` along with the Command state's `nextInstIdx`. Fixes #2343
156 lines
5.8 KiB
Text
156 lines
5.8 KiB
Text
/-
|
||
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura
|
||
-/
|
||
prelude
|
||
import Lean.Data.LBool
|
||
import Lean.Meta.InferType
|
||
import Lean.Meta.NatInstTesters
|
||
|
||
namespace Lean.Meta
|
||
|
||
private abbrev withInstantiatedMVars (e : Expr) (k : Expr → OptionT MetaM α) : OptionT MetaM α := do
|
||
let eNew ← instantiateMVars e
|
||
if eNew.getAppFn.isMVar then
|
||
failure
|
||
else
|
||
k eNew
|
||
|
||
/--
|
||
Evaluate simple `Nat` expressions.
|
||
Remark: this method assumes the given expression has type `Nat`. -/
|
||
partial def evalNat (e : Expr) : OptionT MetaM Nat := do
|
||
match e with
|
||
| .lit (.natVal n) => return n
|
||
| .mdata _ e => evalNat e
|
||
| .const ``Nat.zero .. => return 0
|
||
| .app .. => visit e
|
||
| .mvar .. => visit e
|
||
| _ => failure
|
||
where
|
||
visit e := do
|
||
match_expr e with
|
||
| OfNat.ofNat _ n i => guard (← isInstOfNatNat i); evalNat n
|
||
| Nat.succ a => return (← evalNat a) + 1
|
||
| Nat.add a b => return (← evalNat a) + (← evalNat b)
|
||
| Add.add _ i a b => guard (← isInstAddNat i); return (← evalNat a) + (← evalNat b)
|
||
| HAdd.hAdd _ _ _ i a b => guard (← isInstHAddNat i); return (← evalNat a) + (← evalNat b)
|
||
| Nat.sub a b => return (← evalNat a) - (← evalNat b)
|
||
| Sub.sub _ i a b => guard (← isInstSubNat i); return (← evalNat a) - (← evalNat b)
|
||
| HSub.hSub _ _ _ i a b => guard (← isInstHSubNat i); return (← evalNat a) - (← evalNat b)
|
||
| Nat.mul a b => return (← evalNat a) * (← evalNat b)
|
||
| Mul.mul _ i a b => guard (← isInstMulNat i); return (← evalNat a) * (← evalNat b)
|
||
| HMul.hMul _ _ _ i a b => guard (← isInstHMulNat i); return (← evalNat a) * (← evalNat b)
|
||
| Nat.div a b => return (← evalNat a) / (← evalNat b)
|
||
| Div.div _ i a b => guard (← isInstDivNat i); return (← evalNat a) / (← evalNat b)
|
||
| HDiv.hDiv _ _ _ i a b => guard (← isInstHDivNat i); return (← evalNat a) / (← evalNat b)
|
||
| Nat.mod a b => return (← evalNat a) % (← evalNat b)
|
||
| Mod.mod _ i a b => guard (← isInstModNat i); return (← evalNat a) % (← evalNat b)
|
||
| HMod.hMod _ _ _ i a b => guard (← isInstHModNat i); return (← evalNat a) % (← evalNat b)
|
||
| Nat.pow a b => return (← evalNat a) ^ (← evalNat b)
|
||
| NatPow.pow _ i a b => guard (← isInstNatPowNat i); return (← evalNat a) ^ (← evalNat b)
|
||
| Pow.pow _ _ i a b => guard (← isInstPowNat i); return (← evalNat a) ^ (← evalNat b)
|
||
| HPow.hPow _ _ _ i a b => guard (← isInstHPowNat i); return (← evalNat a) ^ (← evalNat b)
|
||
| _ => failure
|
||
|
||
/--
|
||
Checks that expression `e` is definitional equal to `inst`.
|
||
|
||
Uses `instances` transparency so that reducible terms and instances extended
|
||
other instances are unfolded.
|
||
-/
|
||
def matchesInstance (e inst : Expr) : MetaM Bool :=
|
||
-- Note. We use withNewMCtxDepth to avoid assigning meta-variables in isDefEq checks
|
||
withNewMCtxDepth (withTransparency .instances (isDefEq e inst))
|
||
|
||
mutual
|
||
|
||
/--
|
||
Quick function for converting `e` into `s + k` s.t. `e` is definitionally equal to `Nat.add s k`.
|
||
This function always succeeds in finding such `s` and `k`
|
||
(as a last resort it returns `e` and `0`).
|
||
-/
|
||
private partial def getOffset (e : Expr) : MetaM (Expr × Nat) :=
|
||
return (← isOffset? e).getD (e, 0)
|
||
|
||
/--
|
||
Similar to `getOffset` but returns `none` if the expression is not an offset.
|
||
-/
|
||
partial def isOffset? (e : Expr) : OptionT MetaM (Expr × Nat) := do
|
||
let add (a b : Expr) := do
|
||
let v ← evalNat b
|
||
let (s, k) ← getOffset a
|
||
return (s, k+v)
|
||
match_expr e with
|
||
| Nat.succ a =>
|
||
let (s, k) ← getOffset a
|
||
return (s, k+1)
|
||
| Nat.add a b => add a b
|
||
| Add.add _ i a b => guard (← matchesInstance i Nat.mkInstAdd); add a b
|
||
| HAdd.hAdd _ _ _ i a b => guard (← matchesInstance i Nat.mkInstHAdd); add a b
|
||
| _ => failure
|
||
|
||
end
|
||
|
||
private def isNatZero (e : Expr) : MetaM Bool := do
|
||
match (← evalNat e) with
|
||
| some v => return v == 0
|
||
| _ => return false
|
||
|
||
private def mkOffset (e : Expr) (offset : Nat) : MetaM Expr := do
|
||
if offset == 0 then
|
||
return e
|
||
else if (← isNatZero e) then
|
||
return mkNatLit offset
|
||
else
|
||
return mkNatAdd e (mkNatLit offset)
|
||
|
||
def isDefEqOffset (s t : Expr) : MetaM LBool := do
|
||
let ifNatExpr (x : MetaM LBool) : MetaM LBool := do
|
||
let type ← inferType s
|
||
-- Remark: we use `withNewMCtxDepth` to make sure we don't assign metavariables when performing the `isDefEq` test
|
||
if (← withNewMCtxDepth <| Meta.isExprDefEqAux type (mkConst ``Nat)) then
|
||
x
|
||
else
|
||
return LBool.undef
|
||
let isDefEq (s t) : MetaM LBool :=
|
||
ifNatExpr <| toLBoolM <| Meta.isExprDefEqAux s t
|
||
if !(← getConfig).offsetCnstrs then
|
||
return LBool.undef
|
||
else
|
||
match (← isOffset? s) with
|
||
| some (s, k₁) =>
|
||
match (← isOffset? t) with
|
||
| some (t, k₂) => -- s+k₁ =?= t+k₂
|
||
if k₁ == k₂ then
|
||
isDefEq s t
|
||
else if k₁ < k₂ then
|
||
isDefEq s (← mkOffset t (k₂ - k₁))
|
||
else
|
||
isDefEq (← mkOffset s (k₁ - k₂)) t
|
||
| none =>
|
||
match (← evalNat t) with
|
||
| some v₂ => -- s+k₁ =?= v₂
|
||
if v₂ ≥ k₁ then
|
||
isDefEq s (mkNatLit <| v₂ - k₁)
|
||
else
|
||
ifNatExpr <| return LBool.false
|
||
| none =>
|
||
return LBool.undef
|
||
| none =>
|
||
match (← evalNat s) with
|
||
| some v₁ =>
|
||
match (← isOffset? t) with
|
||
| some (t, k₂) => -- v₁ =?= t+k₂
|
||
if v₁ ≥ k₂ then
|
||
isDefEq (mkNatLit <| v₁ - k₂) t
|
||
else
|
||
ifNatExpr <| return LBool.false
|
||
| none =>
|
||
match (← evalNat t) with
|
||
| some v₂ => ifNatExpr <| return (v₁ == v₂).toLBool -- v₁ =?= v₂
|
||
| none => return LBool.undef
|
||
| none => return LBool.undef
|
||
|
||
end Lean.Meta
|