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
77 lines
3.5 KiB
Text
77 lines
3.5 KiB
Text
/-
|
||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura, Sebastian Ullrich
|
||
-/
|
||
prelude
|
||
import Lean.Meta.Basic
|
||
import Lean.Meta.Check
|
||
|
||
namespace Lean.Meta
|
||
|
||
def forallTelescopeCompatibleAux {α} (k : Array Expr → Expr → Expr → MetaM α) : Nat → Expr → Expr → Array Expr → MetaM α
|
||
| 0, type₁, type₂, xs => k xs type₁ type₂
|
||
| i+1, type₁, type₂, xs => do
|
||
let type₁ ← whnf type₁
|
||
let type₂ ← whnf type₂
|
||
match type₁, type₂ with
|
||
| Expr.forallE n₁ d₁ b₁ c₁, Expr.forallE n₂ d₂ b₂ c₂ =>
|
||
unless n₁ == n₂ do
|
||
throwError "parameter name mismatch '{n₁}', expected '{n₂}'"
|
||
unless (← isDefEq d₁ d₂) do
|
||
throwError "parameter '{n₁}' {← mkHasTypeButIsExpectedMsg d₁ d₂}"
|
||
unless c₁ == c₂ do
|
||
throwError "binder annotation mismatch at parameter '{n₁}'"
|
||
withLocalDecl n₁ c₁ d₁ fun x =>
|
||
let type₁ := b₁.instantiate1 x
|
||
let type₂ := b₂.instantiate1 x
|
||
forallTelescopeCompatibleAux k i type₁ type₂ (xs.push x)
|
||
| _, _ => throwError "unexpected number of parameters"
|
||
|
||
/-- Given two forall-expressions `type₁` and `type₂`, ensure the first `numParams` parameters are compatible, and
|
||
then execute `k` with the parameters and remaining types. -/
|
||
def forallTelescopeCompatible {α m} [Monad m] [MonadControlT MetaM m] (type₁ type₂ : Expr) (numParams : Nat) (k : Array Expr → Expr → Expr → m α) : m α :=
|
||
controlAt MetaM fun runInBase =>
|
||
forallTelescopeCompatibleAux (fun xs type₁ type₂ => runInBase $ k xs type₁ type₂) numParams type₁ type₂ #[]
|
||
|
||
end Meta
|
||
|
||
namespace Elab
|
||
|
||
def expandOptDeclSig (stx : Syntax) : Syntax × Option Syntax :=
|
||
-- many Term.bracketedBinder >> Term.optType
|
||
let binders := stx[0]
|
||
let optType := stx[1] -- optional (leading_parser " : " >> termParser)
|
||
if optType.isNone then
|
||
(binders, none)
|
||
else
|
||
let typeSpec := optType[0]
|
||
(binders, some typeSpec[1])
|
||
|
||
def expandDeclSig (stx : Syntax) : Syntax × Syntax :=
|
||
-- many Term.bracketedBinder >> Term.typeSpec
|
||
let binders := stx[0]
|
||
let typeSpec := stx[1]
|
||
(binders, typeSpec[1])
|
||
|
||
/--
|
||
Sort the given list of `usedParams` using the following order:
|
||
- If it is an explicit level `allUserParams`, then use user given order.
|
||
- Otherwise, use lexicographical.
|
||
|
||
Remark: `scopeParams` are the universe params introduced using the `universe` command. `allUserParams` contains
|
||
the universe params introduced using the `universe` command *and* the `.{...}` notation.
|
||
|
||
Remark: this function return an exception if there is an `u` not in `usedParams`, that is in `allUserParams` but not in `scopeParams`.
|
||
|
||
Remark: `explicitParams` are in reverse declaration order. That is, the head is the last declared parameter. -/
|
||
def sortDeclLevelParams (scopeParams : List Name) (allUserParams : List Name) (usedParams : Array Name) : Except String (List Name) :=
|
||
match allUserParams.find? fun u => !usedParams.contains u && !scopeParams.elem u with
|
||
| some u => throw s!"unused universe parameter '{u}'"
|
||
| none =>
|
||
let result := allUserParams.foldl (fun result levelName => if usedParams.elem levelName then levelName :: result else result) []
|
||
let remaining := usedParams.filter (fun levelParam => !allUserParams.elem levelParam)
|
||
let remaining := remaining.qsort Name.lt
|
||
pure $ result ++ remaining.toList
|
||
|
||
end Lean.Elab
|