lean4-htt/src/Lean/Elab/Level.lean
jrr6 3a3c816a27
chore: break up universe level error message (#9637)
This PR improves the readability of the "maximum universe level offset
exceeded" error message.
2025-07-30 23:52:53 +00:00

97 lines
3.4 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
-/
module
prelude
public import Lean.Log
public import Lean.Parser.Level
public import Lean.Elab.Exception
public import Lean.Elab.AutoBound
public section
namespace Lean.Elab.Level
structure Context where
options : Options
ref : Syntax
autoBoundImplicit : Bool
structure State where
ngen : NameGenerator
mctx : MetavarContext
levelNames : List Name
abbrev LevelElabM := ReaderT Context (EStateM Exception State)
instance : MonadOptions LevelElabM where
getOptions := return (← read).options
@[always_inline]
instance : MonadRef LevelElabM where
getRef := return (← read).ref
withRef ref x := withReader (fun ctx => { ctx with ref := ref }) x
instance : AddMessageContext LevelElabM where
addMessageContext msg := pure msg
@[always_inline]
instance : MonadNameGenerator LevelElabM where
getNGen := return (← get).ngen
setNGen ngen := modify fun s => { s with ngen := ngen }
def mkFreshLevelMVar : LevelElabM Level := do
let mvarId ← mkFreshLMVarId
modify fun s => { s with mctx := s.mctx.addLevelMVarDecl mvarId }
return mkLevelMVar mvarId
register_builtin_option maxUniverseOffset : Nat := {
defValue := 32
descr := "maximum universe level offset"
}
private def checkUniverseOffset [Monad m] [MonadError m] [MonadOptions m] (n : Nat) : m Unit := do
let max := maxUniverseOffset.get (← getOptions)
unless n <= max do
throwError m!"Universe level offset `{n}` exceeds maximum offset `{max}`"
++ .note m!"This code is probably misusing universe levels, since they are usually small natural numbers. \
If you are confident this is not the case, you can increase the limit using `set_option maxUniverseOffset <limit>`"
partial def elabLevel (stx : Syntax) : LevelElabM Level := withRef stx do
let kind := stx.getKind
if kind == ``Lean.Parser.Level.paren then
elabLevel (stx.getArg 1)
else if kind == ``Lean.Parser.Level.max then
let args := stx.getArg 1 |>.getArgs
args[*...(args.size - 1)].foldrM (init := ← elabLevel args.back!) fun stx lvl =>
return mkLevelMax' (← elabLevel stx) lvl
else if kind == ``Lean.Parser.Level.imax then
let args := stx.getArg 1 |>.getArgs
args[*...(args.size - 1)].foldrM (init := ← elabLevel args.back!) fun stx lvl =>
return mkLevelIMax' (← elabLevel stx) lvl
else if kind == ``Lean.Parser.Level.hole then
mkFreshLevelMVar
else if kind == numLitKind then
match stx.isNatLit? with
| some val => checkUniverseOffset val; return Level.ofNat val
| none => throwIllFormedSyntax
else if kind == identKind then
let paramName := stx.getId
unless (← get).levelNames.contains paramName do
if (← read).autoBoundImplicit && isValidAutoBoundLevelName paramName (relaxedAutoImplicit.get (← read).options) then
modify fun s => { s with levelNames := paramName :: s.levelNames }
else
throwError "unknown universe level '{mkIdent paramName}'"
return mkLevelParam paramName
else if kind == `Lean.Parser.Level.addLit then
let lvl ← elabLevel (stx.getArg 0)
match stx.getArg 2 |>.isNatLit? with
| some val => checkUniverseOffset val; return lvl.addOffset val
| none => throwIllFormedSyntax
else
throwError "unexpected universe level syntax kind"
end Lean.Elab.Level