Modifies how the declaration command elaborator reports when there are unassigned metavariables. The visible effects are that (1) now errors like "don't know how to synthesize implicit argument" and "failed to infer 'let' declaration type" take precedence over universe level issues, (2) universe level metavariables are reported as metavariables (rather than as `u_1`, `u_2`, etc.), and (3) if the universe level metavariables appear in `let` binding types or `fun` binder types, the error is localized there. Motivation: Reporting unsolved expression metavariables is more important than universe level issues (typically universe issues are from unsolved expression metavariables). Furthermore, `let` and `fun` binders can't introduce universe polymorphism, so we can "blame" such bindings for universe metavariables, if possible. Example 1: Now the errors are on `x` and `none` (reporting expression metavariables) rather than on `example` (which reported universe level metavariables). ```lean example : IO Unit := do let x := none pure () ``` Example 2: Now there is a "failed to infer universe levels in 'let' declaration type" error on `PUnit`. ```lean def foo : IO Unit := do let x : PUnit := PUnit.unit pure () ``` In more detail: * `elabMutualDef` used to turn all level mvars into fresh level parameters before doing an analysis for "hidden levels". This analysis turns out to be exactly the same as instead creating fresh parameters for level mvars in only pre-definitions' types and then looking for level metavariables in their bodies. With this PR, error messages refer to the same level metavariables in the Infoview, rather than obscure generated `u_1`, `u_2`, ... level parameters. * This PR made it possible to push the "hidden levels" check into `addPreDefinitions`, after the checks for unassigned expression mvars. It used to be that if the "hidden levels" check produced an "invalid occurrence of universe level" error it would suppress errors for unassigned expression mvars, and now it is the other way around. * There is now a list of `LevelMVarErrorInfo` objects in the `TermElabM` state. These record expressions that should receive a localized error if they still contain level metavariables. Currently `let` expressions and binder types in general register such info. Error messages make use of a new `exposeLevelMVars` function that adds pretty printer annotations that try to expose all universe level metavariables. * When there are universe level metavariables, for error recovery the definition is still added to the environment after assigning each metavariable to level 0. * There's a new `Lean.Util.CollectLevelMVars` module for collecting level metavariables from expressions. Closes #2058
86 lines
4.2 KiB
Text
86 lines
4.2 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
|
||
| .forallE n₁ d₁ b₁ c₁, .forallE n₂ d₂ b₂ c₂ =>
|
||
-- Remark: we use `mkIdent` to ensure macroscopes do not leak into error messages
|
||
unless c₁ == c₂ do
|
||
throwError "binder annotation mismatch at parameter '{mkIdent n₁}'"
|
||
/-
|
||
Remark: recall that users may suppress parameter names for instance implicit arguments.
|
||
A fresh name (with macro scopes) is generated in this case. Thus, we allow the names
|
||
to be different in this case. See issue #4310.
|
||
-/
|
||
unless n₁ == n₂ || (c₁.isInstImplicit && n₁.hasMacroScopes && n₂.hasMacroScopes) do
|
||
throwError "parameter name mismatch '{mkIdent n₁}', expected '{mkIdent n₂}'"
|
||
unless (← isDefEq d₁ d₂) do
|
||
throwError "parameter '{mkIdent n₁}' {← mkHasTypeButIsExpectedMsg d₁ d₂}"
|
||
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 [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 in `allUserParams`, then use user-given order.
|
||
- All other levels come in lexicographic order after these.
|
||
|
||
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: `scopeParams` and `allUserParams` 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 =>
|
||
-- Recall that `allUserParams` (like `scopeParams`) are in reverse order. That is, the last declared universe is the first element of the list.
|
||
-- The following `foldl` will reverse the elements and produce a list of universe levels using the user given order.
|
||
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
|
||
return result ++ remaining.toList
|
||
|
||
end Lean.Elab
|