Compare commits

...

11 Commits

Author SHA1 Message Date
Leonardo de Moura
5adf19cb4f fix: log errors produced by a reserved name action 2024-03-14 17:12:30 -07:00
Leonardo de Moura
02f4d716e1 fix: ensure reserved names are available 2024-03-14 17:12:28 -07:00
Leonardo de Moura
e494abebb1 fix: relax reserved name check 2024-03-14 17:11:32 -07:00
Leonardo de Moura
07c2d2ea0f fix: relax reserved name check
TODO: check whether reserved names are available at declaration time.
2024-03-14 17:11:32 -07:00
Leonardo de Moura
fc50f9c3d0 chore: fix tests 2024-03-14 17:11:32 -07:00
Leonardo de Moura
094150d413 chore: add comment saying when reserved name actions are executed 2024-03-14 17:11:32 -07:00
Leonardo de Moura
7dd3ff10f0 chore: rename resolveGlobalConst' => realizeGlobalConst
And all other related functions.
2024-03-14 17:11:32 -07:00
Leonardo de Moura
60240b49a9 fix: simplify and fix reserved name support 2024-03-14 17:11:32 -07:00
Leonardo de Moura
d90c15a266 feat: using non-private equational theorem names 2024-03-14 17:11:32 -07:00
Leonardo de Moura
04c1dff573 feat: reserved names and actions for generating their definitions 2024-03-14 17:11:32 -07:00
Leonardo de Moura
9b12af1692 feat: equation theorems are non-private 2024-03-14 17:11:32 -07:00
46 changed files with 470 additions and 178 deletions

View File

@@ -19,7 +19,7 @@ which applies to all applications of the function).
-/
@[simp] def inline {α : Sort u} (a : α) : α := a
theorem id.def {α : Sort u} (a : α) : id a = a := rfl
theorem id_def {α : Sort u} (a : α) : id a = a := rfl
/--
`flip f a b` is `f b a`. It is useful for "point-free" programming,

View File

@@ -4581,6 +4581,12 @@ def resolveNamespace (n : Name) : MacroM (List Name) := do
Resolves the given name to an overload list of global definitions.
The `List String` in each alternative is the deduced list of projections
(which are ambiguous with name components).
Remark: it will not trigger actions associated with reserved names. Recall that Lean
has reserved names. For example, a definition `foo` has a reserved name `foo.def` for theorem
containing stating that `foo` is equal to its definition. The action associated with `foo.def`
automatically proves the theorem. At the macro level, the name is resolved, but the action is not
executed. The actions are executed by the elaborator when converting `Syntax` into `Expr`.
-/
def resolveGlobalName (n : Name) : MacroM (List (Prod Name (List String))) := do
( getMethods).resolveGlobalName n

View File

@@ -21,7 +21,7 @@ set_option linter.missingDocs true -- keep it documented
| rfl, rfl, _ => rfl
@[simp] theorem eq_true_eq_id : Eq True = id := by
funext _; simp only [true_iff, id.def, eq_iff_iff]
funext _; simp only [true_iff, id_def, eq_iff_iff]
/-! ## not -/

View File

@@ -24,6 +24,7 @@ import Lean.Eval
import Lean.Structure
import Lean.PrettyPrinter
import Lean.CoreM
import Lean.ReservedNameAction
import Lean.InternalExceptionId
import Lean.Server
import Lean.ScopedEnvExtension

View File

@@ -17,7 +17,7 @@ builtin_initialize implementedByAttr : ParametricAttribute Name ← registerPara
getParam := fun declName stx => do
let decl getConstInfo declName
let fnNameStx Attribute.Builtin.getIdent stx
let fnName Elab.resolveGlobalConstNoOverloadWithInfo fnNameStx
let fnName Elab.realizeGlobalConstNoOverloadWithInfo fnNameStx
let fnDecl getConstInfo fnName
unless decl.levelParams.length == fnDecl.levelParams.length do
throwError "invalid 'implemented_by' argument '{fnName}', '{fnName}' has {fnDecl.levelParams.length} universe level parameter(s), but '{declName}' has {decl.levelParams.length}"

View File

@@ -44,7 +44,7 @@ unsafe def registerInitAttrUnsafe (attrName : Name) (runAfterImport : Bool) (ref
let decl getConstInfo declName
match ( Attribute.Builtin.getIdent? stx) with
| some initFnName =>
let initFnName Elab.resolveGlobalConstNoOverloadWithInfo initFnName
let initFnName Elab.realizeGlobalConstNoOverloadWithInfo initFnName
let initDecl getConstInfo initFnName
match getIOTypeArg initDecl.type with
| none => throwError "initialization function '{initFnName}' must have type of the form `IO <type>`"

View File

@@ -536,7 +536,7 @@ def elabCheckCore (ignoreStuckTC : Bool) : CommandElab
-- show signature for `#check id`/`#check @id`
if let `($id:ident) := term then
try
for c in ( resolveGlobalConstWithInfos term) do
for c in ( realizeGlobalConstWithInfos term) do
addCompletionInfo <| .id term id.getId (danglingDot := false) {} none
logInfoAt tk <| .ofPPFormat { pp := fun
| some ctx => ctx.runMetaM <| PrettyPrinter.ppSignature c
@@ -760,7 +760,7 @@ def elabRunMeta : CommandElab := fun stx =>
@[builtin_command_elab Parser.Command.addDocString] def elabAddDeclDoc : CommandElab := fun stx => do
match stx with
| `($doc:docComment add_decl_doc $id) =>
let declName resolveGlobalConstNoOverloadWithInfo id
let declName liftCoreM <| realizeGlobalConstNoOverloadWithInfo id
unless (( getEnv).getModuleIdxFor? declName).isNone do
throwError "invalid 'add_decl_doc', declaration is in an imported module"
if let .none findDeclarationRangesCore? declName then

View File

@@ -223,7 +223,7 @@ def elabScientificLit : TermElab := fun stx expectedType? => do
| none => throwIllFormedSyntax
@[builtin_term_elab doubleQuotedName] def elabDoubleQuotedName : TermElab := fun stx _ =>
return toExpr ( resolveGlobalConstNoOverloadWithInfo stx[2])
return toExpr ( realizeGlobalConstNoOverloadWithInfo stx[2])
@[builtin_term_elab declName] def elabDeclName : TermElab := adaptExpander fun _ => do
let some declName getDeclName?

View File

@@ -27,6 +27,8 @@ def checkNotAlreadyDeclared {m} [Monad m] [MonadEnv m] [MonadError m] [MonadInfo
match privateToUserName? declName with
| none => throwError "'{declName}' has already been declared"
| some declName => throwError "private declaration '{declName}' has already been declared"
if isReservedName env declName then
throwError "'{declName}' is a reserved name"
if env.contains (mkPrivateName env declName) then
addInfo (mkPrivateName env declName)
throwError "a private declaration '{declName}' has already been declared"

View File

@@ -166,7 +166,7 @@ private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) : Comm
return { ref := ctor, modifiers := ctorModifiers, declName := ctorName, binders := binders, type? := type? : CtorView }
let computedFields (decl[5].getOptional?.map (·[1].getArgs) |>.getD #[]).mapM fun cf => withRef cf do
return { ref := cf, modifiers := cf[0], fieldId := cf[1].getId, type := cf[3], matchAlts := cf[4] }
let classes getOptDerivingClasses decl[6]
let classes liftCoreM <| getOptDerivingClasses decl[6]
return {
ref := decl
shortDeclName := name
@@ -354,7 +354,7 @@ def elabMutual : CommandElab := fun stx => do
-/
let declNames
try
resolveGlobalConst ident
realizeGlobalConst ident
catch _ =>
let name := ident.getId.eraseMacroScopes
if ( Simp.isBuiltinSimproc name) then

View File

@@ -100,10 +100,10 @@ private def tryApplyDefHandler (className : Name) (declName : Name) : CommandEla
@[builtin_command_elab «deriving»] def elabDeriving : CommandElab
| `(deriving instance $[$classes $[with $argss?]?],* for $[$declNames],*) => do
let declNames declNames.mapM resolveGlobalConstNoOverloadWithInfo
let declNames liftCoreM <| declNames.mapM realizeGlobalConstNoOverloadWithInfo
for cls in classes, args? in argss? do
try
let className resolveGlobalConstNoOverloadWithInfo cls
let className liftCoreM <| realizeGlobalConstNoOverloadWithInfo cls
withRef cls do
if declNames.size == 1 && args?.isNone then
if ( tryApplyDefHandler className declNames[0]!) then
@@ -118,12 +118,12 @@ structure DerivingClassView where
className : Name
args? : Option (TSyntax ``Parser.Term.structInst)
def getOptDerivingClasses [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadInfoTree m] (optDeriving : Syntax) : m (Array DerivingClassView) := do
def getOptDerivingClasses (optDeriving : Syntax) : CoreM (Array DerivingClassView) := do
match optDeriving with
| `(Parser.Command.optDeriving| deriving $[$classes $[with $argss?]?],*) =>
let mut ret := #[]
for cls in classes, args? in argss? do
let className resolveGlobalConstNoOverloadWithInfo cls
let className realizeGlobalConstNoOverloadWithInfo cls
ret := ret.push { ref := cls, className := className, args? }
return ret
| _ => return #[]

View File

@@ -10,8 +10,8 @@ import Lean.Meta.Injective
namespace Lean.Elab.Command
@[builtin_command_elab genInjectiveTheorems] def elabGenInjectiveTheorems : CommandElab := fun stx => do
let declName resolveGlobalConstNoOverloadWithInfo stx[1]
liftTermElabM do
let declName realizeGlobalConstNoOverloadWithInfo stx[1]
Meta.mkInjectiveTheorems declName
end Lean.Elab.Command

View File

@@ -6,6 +6,7 @@ Authors: Wojciech Nawrocki, Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Lean.Meta.PPGoal
import Lean.ReservedNameAction
namespace Lean.Elab.CommandContextInfo
@@ -281,31 +282,28 @@ def addConstInfo [MonadEnv m] [MonadError m]
expectedType?
}
/-- This does the same job as `resolveGlobalConstNoOverload`; resolving an identifier
/-- This does the same job as `realizeGlobalConstNoOverload`; resolving an identifier
syntax to a unique fully resolved name or throwing if there are ambiguities.
But also adds this resolved name to the infotree. This means that when you hover
over a name in the sourcefile you will see the fully resolved name in the hover info.-/
def resolveGlobalConstNoOverloadWithInfo [MonadResolveName m] [MonadEnv m] [MonadError m]
(id : Syntax) (expectedType? : Option Expr := none) : m Name := do
let n resolveGlobalConstNoOverload id
def realizeGlobalConstNoOverloadWithInfo (id : Syntax) (expectedType? : Option Expr := none) : CoreM Name := do
let n realizeGlobalConstNoOverload id
if ( getInfoState).enabled then
-- we do not store a specific elaborator since identifiers are special-cased by the server anyway
addConstInfo id n expectedType?
return n
/-- Similar to `resolveGlobalConstNoOverloadWithInfo`, except if there are multiple name resolutions then it returns them as a list. -/
def resolveGlobalConstWithInfos [MonadResolveName m] [MonadEnv m] [MonadError m]
(id : Syntax) (expectedType? : Option Expr := none) : m (List Name) := do
let ns resolveGlobalConst id
/-- Similar to `realizeGlobalConstNoOverloadWithInfo`, except if there are multiple name resolutions then it returns them as a list. -/
def realizeGlobalConstWithInfos (id : Syntax) (expectedType? : Option Expr := none) : CoreM (List Name) := do
let ns realizeGlobalConst id
if ( getInfoState).enabled then
for n in ns do
addConstInfo id n expectedType?
return ns
/-- Similar to `resolveGlobalName`, but it also adds the resolved name to the info tree. -/
def resolveGlobalNameWithInfos [MonadResolveName m] [MonadEnv m] [MonadError m]
(ref : Syntax) (id : Name) : m (List (Name × List String)) := do
let ns resolveGlobalName id
/-- Similar to `realizeGlobalName`, but it also adds the resolved name to the info tree. -/
def realizeGlobalNameWithInfos (ref : Syntax) (id : Name) : CoreM (List (Name × List String)) := do
let ns realizeGlobalName id
if ( getInfoState).enabled then
for (n, _) in ns do
addConstInfo ref n

View File

@@ -20,7 +20,7 @@ builtin_initialize
| `(attr| inherit_doc $[$id?:ident]?) => withRef stx[0] do
let some id := id?
| throwError "invalid `[inherit_doc]` attribute, could not infer doc source"
let declName Elab.resolveGlobalConstNoOverloadWithInfo id
let declName Elab.realizeGlobalConstNoOverloadWithInfo id
if ( findDocString? ( getEnv) decl).isSome then
logWarning m!"{← mkConstWithLevelParams decl} already has a doc string"
let some doc findDocString? ( getEnv) declName

View File

@@ -833,7 +833,7 @@ where
for header in headers, view in views do
if let some classNamesStx := view.deriving? then
for classNameStx in classNamesStx do
let className resolveGlobalConstNoOverload classNameStx
let className realizeGlobalConstNoOverload classNameStx
withRef classNameStx do
unless ( processDefDeriving className header.declName) do
throwError "failed to synthesize instance '{className}' for '{header.declName}'"

View File

@@ -317,14 +317,6 @@ def whnfReducibleLHS? (mvarId : MVarId) : MetaM (Option MVarId) := mvarId.withCo
def tryContradiction (mvarId : MVarId) : MetaM Bool := do
mvarId.contradictionCore { genDiseq := true }
structure UnfoldEqnExtState where
map : PHashMap Name Name := {}
deriving Inhabited
/- We generate the unfold equation on demand, and do not save them on .olean files. -/
builtin_initialize unfoldEqnExt : EnvExtension UnfoldEqnExtState
registerEnvExtension (pure {})
/--
Auxiliary method for `mkUnfoldEq`. The structure is based on `mkEqnTypes`.
`mvarId` is the goal to be proved. It is a goal of the form
@@ -370,9 +362,8 @@ partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
/-- Generate the "unfold" lemma for `declName`. -/
def mkUnfoldEq (declName : Name) (info : EqnInfoCore) : MetaM Name := withLCtx {} {} do
let env getEnv
withOptions (tactic.hygienic.set · false) do
let baseName := mkPrivateName env declName
let baseName := declName
lambdaTelescope info.value fun xs body => do
let us := info.levelParams.map mkLevelParam
let type mkEq (mkAppN (Lean.mkConst declName us) xs) body
@@ -388,13 +379,8 @@ def mkUnfoldEq (declName : Name) (info : EqnInfoCore) : MetaM Name := withLCtx {
return name
def getUnfoldFor? (declName : Name) (getInfo? : Unit Option EqnInfoCore) : MetaM (Option Name) := do
let env getEnv
if let some eq := unfoldEqnExt.getState env |>.map.find? declName then
return some eq
else if let some info := getInfo? () then
let eq mkUnfoldEq declName info
modifyEnv fun env => unfoldEqnExt.modifyState env fun s => { s with map := s.map.insert declName eq }
return some eq
if let some info := getInfo? () then
return some ( mkUnfoldEq declName info)
else
return none

View File

@@ -105,6 +105,7 @@ def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLC
See issue #2321
-/
let preDef eraseRecAppSyntax preDefs[0]!
ensureEqnReservedNamesAvailable preDef.declName
if preDef.modifiers.isNoncomputable then
addNonRec preDef
else

View File

@@ -63,7 +63,7 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
let target mkEq (mkAppN (Lean.mkConst info.declName us) xs) body
let goal mkFreshExprSyntheticOpaqueMVar target
mkEqnTypes #[info.declName] goal.mvarId!
let baseName := mkPrivateName ( getEnv) info.declName
let baseName := info.declName
let mut thmNames := #[]
for i in [: eqnTypes.size] do
let type := eqnTypes[i]!
@@ -81,6 +81,7 @@ def mkEqns (info : EqnInfo) : MetaM (Array Name) :=
builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo mkMapDeclarationExtension
def registerEqnsInfo (preDef : PreDefinition) (recArgPos : Nat) : CoreM Unit := do
ensureEqnReservedNamesAvailable preDef.declName
modifyEnv fun env => eqnInfoExt.insert env preDef.declName { preDef with recArgPos }
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do

View File

@@ -109,7 +109,7 @@ private partial def mkProof (declName : Name) (type : Expr) : MetaM Expr := do
def mkEqns (declName : Name) (info : EqnInfo) : MetaM (Array Name) :=
withOptions (tactic.hygienic.set · false) do
let baseName := mkPrivateName ( getEnv) declName
let baseName := declName
let eqnTypes withNewMCtxDepth <| lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let us := info.levelParams.map mkLevelParam
let target mkEq (mkAppN (Lean.mkConst declName us) xs) body
@@ -133,6 +133,7 @@ builtin_initialize eqnInfoExt : MapDeclarationExtension EqnInfo ← mkMapDeclara
def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Name) (fixedPrefixSize : Nat)
(argsPacker : ArgsPacker) : MetaM Unit := do
preDefs.forM fun preDef => ensureEqnReservedNamesAvailable preDef.declName
/-
See issue #2327.
Remark: we could do better for mutual declarations that mix theorems and definitions. However, this is a rare

View File

@@ -80,7 +80,7 @@ private def printIdCore (id : Name) : CommandElabM Unit := do
private def printId (id : Syntax) : CommandElabM Unit := do
addCompletionInfo <| CompletionInfo.id id id.getId (danglingDot := false) {} none
let cs resolveGlobalConstWithInfos id
let cs liftCoreM <| realizeGlobalConstWithInfos id
cs.forM printIdCore
@[builtin_command_elab «print»] def elabPrint : CommandElab
@@ -125,7 +125,7 @@ private def printAxiomsOf (constName : Name) : CommandElabM Unit := do
@[builtin_command_elab «printAxioms»] def elabPrintAxioms : CommandElab
| `(#print%$tk axioms $id) => withRef tk do
let cs resolveGlobalConstWithInfos id
let cs liftCoreM <| realizeGlobalConstWithInfos id
cs.forM printAxiomsOf
| _ => throwUnsupportedSyntax
@@ -140,7 +140,7 @@ private def printEqnsOf (constName : Name) : CommandElabM Unit := do
@[builtin_command_elab «printEqns»] def elabPrintEqns : CommandElab := fun stx => do
let id := stx[2]
let cs resolveGlobalConstWithInfos id
let cs liftCoreM <| realizeGlobalConstWithInfos id
cs.forM printEqnsOf
end Lean.Elab.Command

View File

@@ -92,7 +92,7 @@ private def isSectionVariable (e : Expr) : TermElabM Bool := do
notation "x++" => x.foo
```
-/
if let _::_ resolveGlobalNameWithInfos stx val then
if let _::_ realizeGlobalNameWithInfos stx val then
return
if ( read).quotLCtx.contains val then
return

View File

@@ -892,7 +892,7 @@ def elabStructure (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit :=
let exts := stx[3]
let parents := if exts.isNone then #[] else exts[0][1].getSepArgs
let optType := stx[4]
let derivingClassViews getOptDerivingClasses stx[6]
let derivingClassViews liftCoreM <| getOptDerivingClasses stx[6]
let type if optType.isNone then `(Sort _) else pure optType[0][1]
let declName
runTermElabM fun scopeVars => do

View File

@@ -11,7 +11,7 @@ namespace Lean.Elab.Tactic.Conv
open Meta
@[builtin_tactic Lean.Parser.Tactic.Conv.delta] def evalDelta : Tactic := fun stx => withMainContext do
let declNames stx[1].getArgs.mapM resolveGlobalConstNoOverloadWithInfo
let declNames stx[1].getArgs.mapM fun stx => realizeGlobalConstNoOverloadWithInfo stx
let lhsNew deltaExpand ( instantiateMVars ( getLhs)) declNames.contains
changeLhs lhsNew

View File

@@ -12,7 +12,7 @@ open Meta
@[builtin_tactic Lean.Parser.Tactic.Conv.unfold] def evalUnfold : Tactic := fun stx => withMainContext do
for declNameId in stx[1].getArgs do
let declName resolveGlobalConstNoOverloadWithInfo declNameId
let declName realizeGlobalConstNoOverloadWithInfo declNameId
applySimpResult ( unfold ( getLhs) declName)
end Lean.Elab.Tactic.Conv

View File

@@ -29,7 +29,7 @@ def deltaTarget (declNames : Array Name) : TacticM Unit := do
/-- "delta " ident+ (location)? -/
@[builtin_tactic Lean.Parser.Tactic.delta] def evalDelta : Tactic := fun stx => do
let declNames stx[1].getArgs.mapM resolveGlobalConstNoOverloadWithInfo
let declNames stx[1].getArgs.mapM fun stx => realizeGlobalConstNoOverloadWithInfo stx
let loc := expandOptLocation stx[2]
withLocation loc (deltaLocalDecl declNames) (deltaTarget declNames)
(throwTacticEx `delta · m!"did not delta reduce {declNames}")

View File

@@ -153,7 +153,7 @@ elaborating to `x.1 = y.1 → x.2 = y.2 → x = y`, for example.
@[builtin_term_elab extType] def elabExtType : TermElab := fun stx _ => do
match stx with
| `(ext_type% $flat:term $struct:ident) => do
withExtHyps ( resolveGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
withExtHyps ( realizeGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
let ty := hyps.foldr (init := mkEq x y) fun (f, h) ty =>
mkForall f BinderInfo.default h ty
mkForallFVars (params |>.push x |>.push y) ty
@@ -166,7 +166,7 @@ elaborating to `x = y ↔ x.1 = y.1 ∧ x.2 = y.2`, for example.
@[builtin_term_elab extIffType] def elabExtIffType : TermElab := fun stx _ => do
match stx with
| `(ext_iff_type% $flat:term $struct:ident) => do
withExtHyps ( resolveGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
withExtHyps ( realizeGlobalConstNoOverloadWithInfo struct) flat fun params x y hyps => do
mkForallFVars (params |>.push x |>.push y) <|
mkIff ( mkEq x y) <| mkAndN (hyps.map (·.2)).toList
| _ => throwUnsupportedSyntax

View File

@@ -268,7 +268,7 @@ open Command in
match stx with
| `(norm_cast_add_elim $id:ident) =>
Elab.Command.liftCoreM do MetaM.run' do
addElim ( resolveGlobalConstNoOverload id)
addElim ( realizeGlobalConstNoOverload id)
| _ => throwUnsupportedSyntax
end Lean.Elab.Tactic.NormCast

View File

@@ -51,11 +51,13 @@ def withRWRulesSeq (token : Syntax) (rwRulesSeqStx : Syntax) (x : (symm : Bool)
x symm term
else
-- Try to get equation theorems for `id`.
let declName try resolveGlobalConstNoOverload id catch _ => return ( x symm term)
let declName try realizeGlobalConstNoOverload id catch _ => return ( x symm term)
let some eqThms getEqnsFor? declName (nonRec := true) | x symm term
let rec go : List Name TacticM Unit
| [] => throwError "failed to rewrite using equation theorems for '{declName}'"
| eqThm::eqThms => (x symm (mkIdentFrom id eqThm)) <|> go eqThms
-- Remark: we prefix `eqThm` with `_root_` to ensure it is resolved correctly.
-- See test: `rwPrioritizesLCtxOverEnv.lean`
| eqThm::eqThms => (x symm (mkIdentFrom id (`_root_ ++ eqThm))) <|> go eqThms
go eqThms.toList
discard <| Term.addTermInfo id ( mkConstWithFreshMVarLevels declName) (lctx? := getLCtx)
match term with

View File

@@ -179,7 +179,7 @@ def elabSimpArgs (stx : Syntax) (ctx : Simp.Context) (simprocs : Simp.SimprocsAr
thms := thms.eraseCore (.fvar fvar.fvarId!)
else
let id := arg[1]
let declNames? try pure (some ( resolveGlobalConst id)) catch _ => pure none
let declNames? try pure (some ( realizeGlobalConst id)) catch _ => pure none
if let some declNames := declNames? then
let declName ensureNonAmbiguous id declNames
if ( Simp.isSimproc declName) then

View File

@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Init.Simproc
import Lean.ReservedNameAction
import Lean.Meta.Tactic.Simp.Simproc
import Lean.Elab.Binders
import Lean.Elab.SyntheticMVars
@@ -37,16 +38,16 @@ namespace Command
@[builtin_command_elab Lean.Parser.simprocPattern] def elabSimprocPattern : CommandElab := fun stx => do
let `(simproc_pattern% $pattern => $declName) := stx | throwUnsupportedSyntax
let declName resolveGlobalConstNoOverload declName
liftTermElabM do
let declName realizeGlobalConstNoOverload declName
discard <| checkSimprocType declName
let keys elabSimprocKeys pattern
registerSimproc declName keys
@[builtin_command_elab Lean.Parser.simprocPatternBuiltin] def elabSimprocPatternBuiltin : CommandElab := fun stx => do
let `(builtin_simproc_pattern% $pattern => $declName) := stx | throwUnsupportedSyntax
let declName resolveGlobalConstNoOverload declName
liftTermElabM do
let declName realizeGlobalConstNoOverload declName
let dsimp checkSimprocType declName
let keys elabSimprocKeys pattern
let registerProcName := if dsimp then ``registerBuiltinDSimproc else ``registerBuiltinSimproc

View File

@@ -24,7 +24,7 @@ def unfoldTarget (declName : Name) : TacticM Unit := do
go declNameId loc
where
go (declNameId : Syntax) (loc : Location) : TacticM Unit := do
let declName resolveGlobalConstNoOverloadWithInfo declNameId
let declName realizeGlobalConstNoOverloadWithInfo declNameId
withLocation loc (unfoldLocalDecl declName) (unfoldTarget declName) (throwTacticEx `unfold · m!"did not unfold '{declName}'")
end Lean.Elab.Tactic

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Lean.ReservedNameAction
import Lean.Meta.AppBuilder
import Lean.Meta.CollectMVars
import Lean.Meta.Coe
@@ -1653,7 +1654,7 @@ def resolveName (stx : Syntax) (n : Name) (preresolved : List Syntax.Preresolved
if let some (e, projs) := preresolved.findSome? fun (n, projs) => ctx.sectionFVars.find? n |>.map (·, projs) then
return [(e, projs)] -- section variables should shadow global decls
if preresolved.isEmpty then
process ( resolveGlobalName n)
process ( realizeGlobalName n)
else
process preresolved
where

View File

@@ -209,8 +209,13 @@ def getModuleIdxFor? (env : Environment) (declName : Name) : Option ModuleIdx :=
def isConstructor (env : Environment) (declName : Name) : Bool :=
match env.find? declName with
| ConstantInfo.ctorInfo _ => true
| _ => false
| some (.ctorInfo _) => true
| _ => false
def isSafeDefinition (env : Environment) (declName : Name) : Bool :=
match env.find? declName with
| some (.defnInfo { safety := .safe, .. }) => true
| _ => false
def getModuleIdx? (env : Environment) (moduleName : Name) : Option ModuleIdx :=
env.header.moduleNames.findIdx? (· == moduleName)

View File

@@ -23,7 +23,7 @@ builtin_initialize deprecatedAttr : ParametricAttribute (Option Name) ←
match stx with
| `(attr| deprecated $[$id?]?) =>
let some id := id? | return none
let declNameNew Elab.resolveGlobalConstNoOverloadWithInfo id
let declNameNew Elab.realizeGlobalConstNoOverloadWithInfo id
return some declNameNew
| _ => throwError "invalid `[deprecated]` attribute"
}

View File

@@ -87,7 +87,7 @@ builtin_initialize
throwError "invalid attribute '{name}', declaration is in an imported module"
let decl getConstInfo declName
let fnNameStx Attribute.Builtin.getIdent stx
let key Elab.resolveGlobalConstNoOverloadWithInfo fnNameStx
let key Elab.realizeGlobalConstNoOverloadWithInfo fnNameStx
unless decl.levelParams.isEmpty && (decl.type == .const ``Handler [] || decl.type == .const ``SimpleHandler []) do
throwError "unexpected missing docs handler at '{declName}', `MissingDocs.Handler` or `MissingDocs.SimpleHandler` expected"
if builtin then

View File

@@ -4,46 +4,71 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.ReservedNameAction
import Lean.Meta.Basic
import Lean.Meta.AppBuilder
namespace Lean.Meta
/-- Returns `true` if `s` is of the form `eq_<idx>` -/
def isEqnReservedNameSuffix (s : String) : Bool :=
"eq_".isPrefixOf s && (s.drop 3).isNat
/-- Returns `true` if `s == "def"` -/
def isUnfoldReservedNameSuffix (s : String) : Bool :=
s == "def"
/--
Throw an error if names for equation theorems for `declName` are not available.
-/
def ensureEqnReservedNamesAvailable (declName : Name) : CoreM Unit := do
ensureReservedNameAvailable declName "def"
ensureReservedNameAvailable declName "eq_1"
-- TODO: `declName` may need to reserve multiple `eq_<idx>` names, but we check only the first one.
-- Possible improvement: try to efficiently compute the number of equation theorems at declaration time, and check all of them.
/--
Ensures that `f.def` and `f.eq_<idx>` are reserved names if `f` is a safe definition.
-/
builtin_initialize registerReservedNamePredicate fun env n =>
match n with
| .str p s => (isEqnReservedNameSuffix s || isUnfoldReservedNameSuffix s) && env.isSafeDefinition p
| _ => false
def GetEqnsFn := Name MetaM (Option (Array Name))
private builtin_initialize getEqnsFnsRef : IO.Ref (List GetEqnsFn) IO.mkRef []
/--
Register a new function for retrieving equation theorems.
We generate equations theorems on demand, and they are generated by more than one module.
For example, the structural and well-founded recursion modules generate them.
Most recent getters are tried first.
Registers a new function for retrieving equation theorems.
We generate equations theorems on demand, and they are generated by more than one module.
For example, the structural and well-founded recursion modules generate them.
Most recent getters are tried first.
A getter returns an `Option (Array Name)`. The result is `none` if the getter failed.
Otherwise, it is a sequence of theorem names where each one of them corresponds to
an alternative. Example: the definition
A getter returns an `Option (Array Name)`. The result is `none` if the getter failed.
Otherwise, it is a sequence of theorem names where each one of them corresponds to
an alternative. Example: the definition
```
def f (xs : List Nat) : List Nat :=
match xs with
| [] => []
| x::xs => (x+1)::f xs
```
should have two equational theorems associated with it
```
f [] = []
```
and
```
(x : Nat) → (xs : List Nat) → f (x :: xs) = (x+1) :: f xs
```
```
def f (xs : List Nat) : List Nat :=
match xs with
| [] => []
| x::xs => (x+1)::f xs
```
should have two equational theorems associated with it
```
f [] = []
```
and
```
(x : Nat) → (xs : List Nat) → f (x :: xs) = (x+1) :: f xs
```
-/
def registerGetEqnsFn (f : GetEqnsFn) : IO Unit := do
unless ( initializing) do
throw (IO.userError "failed to register equation getter, this kind of extension can only be registered during initialization")
getEqnsFnsRef.modify (f :: ·)
/-- Return true iff `declName` is a definition and its type is not a proposition. -/
/-- Returns `true` iff `declName` is a definition and its type is not a proposition. -/
private def shouldGenerateEqnThms (declName : Name) : MetaM Bool := do
if let some (.defnInfo info) := ( getEnv).find? declName then
return !( isProp info.type)
@@ -52,23 +77,23 @@ private def shouldGenerateEqnThms (declName : Name) : MetaM Bool := do
structure EqnsExtState where
map : PHashMap Name (Array Name) := {}
mapInv : PHashMap Name Name := {}
mapInv : PHashMap Name Name := {} -- TODO: delete?
deriving Inhabited
/- We generate the equations on demand, and do not save them on .olean files. -/
/- We generate the equations on demand. -/
builtin_initialize eqnsExt : EnvExtension EqnsExtState
registerEnvExtension (pure {})
/--
Simple equation theorem for nonrecursive definitions.
Simple equation theorem for nonrecursive definitions.
-/
private def mkSimpleEqThm (declName : Name) : MetaM (Option Name) := do
private def mkSimpleEqThm (declName : Name) (suffix := `def) : MetaM (Option Name) := do
if let some (.defnInfo info) := ( getEnv).find? declName then
lambdaTelescope (cleanupAnnotations := true) info.value fun xs body => do
let lhs := mkAppN (mkConst info.name <| info.levelParams.map mkLevelParam) xs
let type mkForallFVars xs ( mkEq lhs body)
let value mkLambdaFVars xs ( mkEqRefl lhs)
let name := mkPrivateName ( getEnv) declName ++ `_eq_1
let name := declName ++ suffix
addDecl <| Declaration.thmDecl {
name, type, value
levelParams := info.levelParams
@@ -93,20 +118,41 @@ private def registerEqnThms (declName : Name) (eqThms : Array Name) : CoreM Unit
}
/--
Returns equation theorems for the given declaration.
By default, we do not create equation theorems for nonrecursive definitions.
You can use `nonRec := true` to override this behavior, a dummy `rfl` proof is created on the fly.
Equation theorems are generated on demand, check whether they were generated in an imported file.
-/
private partial def alreadyGenerated? (declName : Name) : MetaM (Option (Array Name)) := do
let env getEnv
let eq1 := declName ++ `eq_1
if env.contains eq1 then
let rec loop (idx : Nat) (eqs : Array Name) : MetaM (Array Name) := do
let nextEq := declName ++ (`eq).appendIndexAfter idx
if env.contains nextEq then
loop (idx+1) (eqs.push nextEq)
else
return eqs
let eqs loop 2 #[eq1]
registerEqnThms declName eqs
return some eqs
else
return none
/--
Returns equation theorems for the given declaration.
By default, we do not create equation theorems for nonrecursive definitions.
You can use `nonRec := true` to override this behavior, a dummy `rfl` proof is created on the fly.
-/
def getEqnsFor? (declName : Name) (nonRec := false) : MetaM (Option (Array Name)) := withLCtx {} {} do
if let some eqs := eqnsExt.getState ( getEnv) |>.map.find? declName then
return some eqs
else if let some eqs alreadyGenerated? declName then
return some eqs
else if ( shouldGenerateEqnThms declName) then
for f in ( getEqnsFnsRef.get) do
if let some r f declName then
registerEqnThms declName r
return some r
if nonRec then
let some eqThm mkSimpleEqThm declName | return none
let some eqThm mkSimpleEqThm declName (suffix := `eq_1) | return none
let r := #[eqThm]
registerEqnThms declName r
return some r
@@ -117,29 +163,29 @@ def GetUnfoldEqnFn := Name → MetaM (Option Name)
private builtin_initialize getUnfoldEqnFnsRef : IO.Ref (List GetUnfoldEqnFn) IO.mkRef []
/--
Register a new function for retrieving a "unfold" equation theorem.
Registers a new function for retrieving a "unfold" equation theorem.
We generate this kind of equation theorem on demand, and it is generated by more than one module.
For example, the structural and well-founded recursion modules generate it.
Most recent getters are tried first.
We generate this kind of equation theorem on demand, and it is generated by more than one module.
For example, the structural and well-founded recursion modules generate it.
Most recent getters are tried first.
A getter returns an `Option Name`. The result is `none` if the getter failed.
Otherwise, it is a theorem name. Example: the definition
A getter returns an `Option Name`. The result is `none` if the getter failed.
Otherwise, it is a theorem name. Example: the definition
```
def f (xs : List Nat) : List Nat :=
```
def f (xs : List Nat) : List Nat :=
match xs with
| [] => []
| x::xs => (x+1)::f xs
```
should have the theorem
```
(xs : Nat) →
f xs =
match xs with
| [] => []
| x::xs => (x+1)::f xs
```
should have the theorem
```
(xs : Nat) →
f xs =
match xs with
| [] => []
| x::xs => (x+1)::f xs
```
```
-/
def registerGetUnfoldEqnFn (f : GetUnfoldEqnFn) : IO Unit := do
unless ( initializing) do
@@ -147,18 +193,33 @@ def registerGetUnfoldEqnFn (f : GetUnfoldEqnFn) : IO Unit := do
getUnfoldEqnFnsRef.modify (f :: ·)
/--
Return an "unfold" theorem for the given declaration.
By default, we do not create unfold theorems for nonrecursive definitions.
You can use `nonRec := true` to override this behavior.
Returns an "unfold" theorem for the given declaration.
By default, we do not create unfold theorems for nonrecursive definitions.
You can use `nonRec := true` to override this behavior.
-/
def getUnfoldEqnFor? (declName : Name) (nonRec := false) : MetaM (Option Name) := withLCtx {} {} do
let env getEnv
let unfoldName := declName ++ `def
if env.contains unfoldName then
return some unfoldName
if ( shouldGenerateEqnThms declName) then
for f in ( getUnfoldEqnFnsRef.get) do
if let some r f declName then
unless r == unfoldName do
throwError "invalid unfold theorem name `{r}` has been generated expected `{unfoldName}`"
return some r
if nonRec then
let some #[eqThm] getEqnsFor? declName (nonRec := true) | return none
return some eqThm
return ( mkSimpleEqThm declName)
return none
builtin_initialize
registerReservedNameAction fun name => do
let .str p s := name | return false
unless ( getEnv).isSafeDefinition p do return false
if isEqnReservedNameSuffix s then
return ( MetaM.run' <| getEqnsFor? p (nonRec := true)).isSome
if isUnfoldReservedNameSuffix s then
return ( MetaM.run' <| getUnfoldEqnFor? p (nonRec := true)).isSome
return false
end Lean.Meta

View File

@@ -725,7 +725,7 @@ def deriveInduction (name : Name) : MetaM Unit := do
@[builtin_command_elab Parser.Command.deriveInduction]
def elabDeriveInduction : Command.CommandElab := fun stx => Command.runTermElabM fun _xs => do
let ident := stx[1]
let name resolveGlobalConstNoOverloadWithInfo ident
let name realizeGlobalConstNoOverloadWithInfo ident
deriveInduction name
end Lean.Tactic.FunInd

View File

@@ -30,7 +30,7 @@ def registerCombinatorAttribute (name : Name) (descr : String) (ref : Name := by
descr := descr,
add := fun decl stx _ => do
let env getEnv
let parserDeclName Elab.resolveGlobalConstNoOverloadWithInfo ( Attribute.Builtin.getIdent stx)
let parserDeclName Elab.realizeGlobalConstNoOverloadWithInfo ( Attribute.Builtin.getIdent stx)
setEnv <| ext.addEntry env (parserDeclName, decl)
}
registerBuiltinAttribute attrImpl

View File

@@ -373,7 +373,7 @@ passed the result of pre-pretty printing the application *without* implicitly pa
to true or `pp.notation` is set to false, it will not be called at all.",
valueTypeName := `Lean.PrettyPrinter.Unexpander
evalKey := fun _ stx => do
Elab.resolveGlobalConstNoOverloadWithInfo ( Attribute.Builtin.getIdent stx)
Elab.realizeGlobalConstNoOverloadWithInfo ( Attribute.Builtin.getIdent stx)
} `Lean.PrettyPrinter.Delaborator.appUnexpanderAttribute
@[builtin_init mkAppUnexpanderAttribute] opaque appUnexpanderAttribute : KeyedDeclsAttribute Unexpander

View File

@@ -0,0 +1,80 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.CoreM
namespace Lean
/--
When trying to resolve a reserved name, an action can be executed to generate the actual definition/theorem.
The action returns `true` if it "handled" the given name.
Remark: usually when one install a reserved name predicate, an associated action is also installed.
-/
def ReservedNameAction := Name CoreM Bool
private builtin_initialize reservedNameActionsRef : IO.Ref (Array ReservedNameAction) IO.mkRef #[]
/--
Register a new function that is invoked when trying to resolve a reserved name.
-/
def registerReservedNameAction (act : ReservedNameAction) : IO Unit := do
unless ( initializing) do
throw (IO.userError "failed to register reserved name action, this kind of extension can only be registered during initialization")
reservedNameActionsRef.modify (·.push act)
/--
Execute a registered reserved action for the given reserved name.
Note that the handler can throw an exception.
-/
def executeReservedNameAction (name : Name) : CoreM Unit := do
for act in ( reservedNameActionsRef.get) do
if ( act name) then
return ()
/--
Similar to `resolveGlobalName`, but also executes reserved name actions.
-/
def realizeGlobalName (id : Name) : CoreM (List (Name × List String)) := do
let cs resolveGlobalName id
cs.filterM fun (c, _) => do
if ( getEnv).contains c then
return true
else
try
executeReservedNameAction c
return ( getEnv).contains c
catch ex =>
-- We record the error produced by then reserved name action generator
logError ex.toMessageData
return false
/--
Similar to `resolveGlobalConstCore`, but also executes reserved name actions.
-/
def realizeGlobalConstCore (n : Name) : CoreM (List Name) := do
let cs realizeGlobalName n
filterFieldList n cs
/--
Similar to `realizeGlobalConstNoOverloadCore`, but also executes reserved name actions.
-/
def realizeGlobalConstNoOverloadCore (n : Name) : CoreM Name := do
ensureNoOverload n ( realizeGlobalConstCore n)
/--
Similar to `resolveGlobalConst`, but also executes reserved name actions.
-/
def realizeGlobalConst (stx : Syntax) : CoreM (List Name) :=
withRef stx do preprocessSyntaxAndResolve stx realizeGlobalConstCore
/--
Similar to `realizeGlobalConstNoOverload`, but also executes reserved name actions.
-/
def realizeGlobalConstNoOverload (id : Syntax) : CoreM Name := do
ensureNonAmbiguous id ( realizeGlobalConst id)
end Lean

View File

@@ -10,6 +10,43 @@ import Lean.Modifiers
import Lean.Exception
namespace Lean
/-!
Reserved names.
We use reserved names for automatically generated theorems (e.g., equational theorems).
Automation may register new reserved name predicates.
In this module, we just check the registered predicates, but do not trigger actions associated with them.
For example, give a definition `foo`, we flag `foo.def` as reserved symbol.
-/
def throwReservedNameNotAvailable [Monad m] [MonadError m] (declName : Name) (reservedName : Name) : m Unit := do
throwError "failed to declare `{declName}` because `{reservedName}` has already been declared"
def ensureReservedNameAvailable [Monad m] [MonadEnv m] [MonadError m] (declName : Name) (suffix : String) : m Unit := do
let reservedName := .str declName suffix
if ( getEnv).contains reservedName then
throwReservedNameNotAvailable declName reservedName
/-- Global reference containing all reserved name predicates. -/
builtin_initialize reservedNamePredicatesRef : IO.Ref (Array (Environment Name Bool)) IO.mkRef #[]
/--
Registers a new reserved name predicate.
-/
def registerReservedNamePredicate (p : Environment Name Bool) : IO Unit := do
unless ( initializing) do
throw (IO.userError "failed to register reserved name suffix predicate, this operation can only be performed during initialization")
reservedNamePredicatesRef.modify fun ps => ps.push p
builtin_initialize reservedNamePredicatesExt : EnvExtension (Array (Environment Name Bool))
registerEnvExtension reservedNamePredicatesRef.get
/--
Returns `true` if `name` is a reserved name.
-/
def isReservedName (env : Environment) (name : Name) : Bool :=
reservedNamePredicatesExt.getState env |>.any (· env name)
/-!
We use aliases to implement the `export <id> (<id>+)` command.
An `export A (x)` in the namespace `B` produces an alias `B.x ~> A.x`.
@@ -113,6 +150,13 @@ private def resolveOpenDecls (env : Environment) (id : Name) : List OpenDecl →
resolvedIds
resolveOpenDecls env id openDecls resolvedIds
/--
Primitive global name resolution procedure. It does not trigger actions associated with reserved names.
Recall that Lean has reserved names. For example, a definition `foo` has a reserved name `foo.def` for theorem
containing stating that `foo` is equal to its definition. The action associated with `foo.def`
automatically proves the theorem. At the macro level, the name is resolved, but the action is not
executed.
-/
def resolveGlobalName (env : Environment) (ns : Name) (openDecls : List OpenDecl) (id : Name) : List (Name × List String) :=
-- decode macro scopes from name before recursion
let extractionResult := extractMacroScopes id
@@ -127,9 +171,9 @@ def resolveGlobalName (env : Environment) (ns : Name) (openDecls : List OpenDecl
match resolveExact env id with
| some newId => [(newId, projs)]
| none =>
let resolvedIds := if env.contains id then [id] else []
let resolvedIds := if env.contains id || isReservedName env id then [id] else []
let idPrv := mkPrivateName env id
let resolvedIds := if env.contains idPrv then [idPrv] ++ resolvedIds else resolvedIds
let resolvedIds := if env.contains idPrv || isReservedName env idPrv then [idPrv] ++ resolvedIds else resolvedIds
let resolvedIds := resolveOpenDecls env id openDecls resolvedIds
let resolvedIds := getAliases env id (skipProtected := id.isAtomic) ++ resolvedIds
match resolvedIds with
@@ -183,27 +227,27 @@ instance (m n) [MonadLift m n] [MonadResolveName m] : MonadResolveName n where
getOpenDecls := liftM (m:=m) getOpenDecls
/--
Given a name `n`, return a list of possible interpretations.
Each interpretation is a pair `(declName, fieldList)`, where `declName`
is the name of a declaration in the current environment, and `fieldList` are
(potential) field names.
The pair is needed because in Lean `.` may be part of a qualified name or
a field (aka dot-notation).
As an example, consider the following definitions
```
def Boo.x := 1
def Foo.x := 2
def Foo.x.y := 3
```
After `open Foo`, we have
- `resolveGlobalName x` => `[(Foo.x, [])]`
- `resolveGlobalName x.y` => `[(Foo.x.y, [])]`
- `resolveGlobalName x.z.w` => `[(Foo.x, [z, w])]`
Given a name `n`, return a list of possible interpretations.
Each interpretation is a pair `(declName, fieldList)`, where `declName`
is the name of a declaration in the current environment, and `fieldList` are
(potential) field names.
The pair is needed because in Lean `.` may be part of a qualified name or
a field (aka dot-notation).
As an example, consider the following definitions
```
def Boo.x := 1
def Foo.x := 2
def Foo.x.y := 3
```
After `open Foo`, we have
- `resolveGlobalName x` => `[(Foo.x, [])]`
- `resolveGlobalName x.y` => `[(Foo.x.y, [])]`
- `resolveGlobalName x.z.w` => `[(Foo.x, [z, w])]`
After `open Foo open Boo`, we have
- `resolveGlobalName x` => `[(Foo.x, []), (Boo.x, [])]`
- `resolveGlobalName x.y` => `[(Foo.x.y, [])]`
- `resolveGlobalName x.z.w` => `[(Foo.x, [z, w]), (Boo.x, [z, w])]`
After `open Foo open Boo`, we have
- `resolveGlobalName x` => `[(Foo.x, []), (Boo.x, [])]`
- `resolveGlobalName x.y` => `[(Foo.x.y, [])]`
- `resolveGlobalName x.z.w` => `[(Foo.x, [z, w]), (Boo.x, [z, w])]`
-/
def resolveGlobalName [Monad m] [MonadResolveName m] [MonadEnv m] (id : Name) : m (List (Name × List String)) := do
return ResolveName.resolveGlobalName ( getEnv) ( getCurrNamespace) ( getOpenDecls) id
@@ -236,24 +280,44 @@ def resolveUniqueNamespace [Monad m] [MonadResolveName m] [MonadEnv m] [MonadErr
| [ns] => return ns
| nss => throwError s!"ambiguous namespace '{id.getId}', possible interpretations: '{nss}'"
/-- Given a name `n`, return a list of possible interpretations for global constants.
Similar to `resolveGlobalName`, but discard any candidate whose `fieldList` is not empty.
For identifiers taken from syntax, use `resolveGlobalConst` instead, which respects preresolved names. -/
def resolveGlobalConstCore [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] (n : Name) : m (List Name) := do
let cs resolveGlobalName n
/-- Helper function for `resolveGlobalConstCore`. -/
def filterFieldList [Monad m] [MonadError m] (n : Name) (cs : List (Name × List String)) : m (List Name) := do
let cs := cs.filter fun (_, fieldList) => fieldList.isEmpty
if cs.isEmpty then throwUnknownConstant n
return cs.map (·.1)
/-- For identifiers taken from syntax, use `resolveGlobalConstNoOverload` instead, which respects preresolved names. -/
def resolveGlobalConstNoOverloadCore [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] (n : Name) : m Name := do
let cs resolveGlobalConstCore n
/-- Given a name `n`, return a list of possible interpretations for global constants.
Similar to `resolveGlobalName`, but discard any candidate whose `fieldList` is not empty.
For identifiers taken from syntax, use `resolveGlobalConst` instead, which respects preresolved names. -/
private def resolveGlobalConstCore [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] (n : Name) : m (List Name) := do
let cs resolveGlobalName n
filterFieldList n cs
/-- Helper function for `resolveGlobalConstNoOverloadCore` -/
def ensureNoOverload [Monad m] [MonadError m] (n : Name) (cs : List Name) : m Name := do
match cs with
| [c] => pure c
| _ => throwError s!"ambiguous identifier '{mkConst n}', possible interpretations: {cs.map mkConst}"
/-- Interpret the syntax `n` as an identifier for a global constant, and return a list of resolved
/-- For identifiers taken from syntax, use `resolveGlobalConstNoOverload` instead, which respects preresolved names. -/
def resolveGlobalConstNoOverloadCore [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] (n : Name) : m Name := do
ensureNoOverload n ( resolveGlobalConstCore n)
def preprocessSyntaxAndResolve [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] (stx : Syntax) (k : Name m (List Name)) : m (List Name) := do
match stx with
| .ident _ _ n pre => do
let pre := pre.filterMap fun
| .decl n [] => some n
| _ => none
if pre.isEmpty then
withRef stx <| k n
else
return pre
| _ => throwErrorAt stx s!"expected identifier"
/--
Interpret the syntax `n` as an identifier for a global constant, and return a list of resolved
constant names that it could be referring to based on the currently open namespaces.
This should be used instead of `resolveGlobalConstCore` for identifiers taken from syntax
because `Syntax` objects may have names that have already been resolved.
@@ -274,16 +338,8 @@ After `open Foo open Boo`, we have
- `resolveGlobalConst x.y` => `[Foo.x.y]`
- `resolveGlobalConst x.z.w` => error: unknown constant
-/
def resolveGlobalConst [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] : Syntax m (List Name)
| stx@(Syntax.ident _ _ n pre) => do
let pre := pre.filterMap fun
| .decl n [] => some n
| _ => none
if pre.isEmpty then
withRef stx <| resolveGlobalConstCore n
else
return pre
| stx => throwErrorAt stx s!"expected identifier"
def resolveGlobalConst [Monad m] [MonadResolveName m] [MonadEnv m] [MonadError m] (stx : Syntax) : m (List Name) :=
preprocessSyntaxAndResolve stx resolveGlobalConstCore
/--
Given a list of names produced by `resolveGlobalConst`, throw an error if the list does not contain

View File

@@ -121,7 +121,7 @@ builtin_initialize
unless kind == AttributeKind.global do
throwError "invalid attribute 'command_code_action', must be global"
let `(attr| command_code_action $args*) := stx | return
let args args.mapM resolveGlobalConstNoOverloadWithInfo
let args args.mapM realizeGlobalConstNoOverloadWithInfo
if (IR.getSorryDep ( getEnv) decl).isSome then return -- ignore in progress definitions
modifyEnv (cmdCodeActionExt.addEntry · (decl, args, mkCommandCodeAction decl))
}
@@ -144,7 +144,7 @@ builtin_initialize
throwError "invalid attribute 'command_code_action', must be global"
let `(attr| builtin_command_code_action $args*) := stx |
throwError "unexpected 'command_code_action' attribute syntax"
let args args.mapM resolveGlobalConstNoOverloadWithInfo
let args args.mapM realizeGlobalConstNoOverloadWithInfo
if (IR.getSorryDep ( getEnv) decl).isSome then return -- ignore in progress definitions
addBuiltin decl args
}

View File

@@ -5,7 +5,7 @@ open Lean.Elab
open Lean.Elab.Command
@[command_elab test] def elabTest : CommandElab := fun stx => do
let id resolveGlobalConstNoOverloadWithInfo stx[1]
let id liftCoreM <| realizeGlobalConstNoOverloadWithInfo stx[1]
liftTermElabM do
IO.println (repr ( Lean.Meta.Match.getEquationsFor id))
return ()

View File

@@ -5,7 +5,7 @@ open Lean.Elab
open Lean.Elab.Command
@[command_elab test] def elabTest : CommandElab := fun stx => do
let id resolveGlobalConstNoOverloadWithInfo stx[1]
let id liftCoreM <| realizeGlobalConstNoOverloadWithInfo stx[1]
liftTermElabM do
IO.println (repr ( Lean.Meta.Match.getEquationsFor id))
return ()

View File

@@ -1,7 +1,7 @@
/--
info: equations:
private theorem List.append.eq_1.{u} : ∀ {α : Type u} (x : List α), List.append [] x = x
private theorem List.append.eq_2.{u} : ∀ {α : Type u} (x : List α) (a : α) (l : List α),
theorem List.append.eq_1.{u} : ∀ {α : Type u} (x : List α), List.append [] x = x
theorem List.append.eq_2.{u} : ∀ {α : Type u} (x : List α) (a : α) (l : List α),
List.append (a :: l) x = a :: List.append l x
-/
#guard_msgs in
@@ -9,8 +9,8 @@ private theorem List.append.eq_2.{u} : ∀ {α : Type u} (x : List α) (a : α)
/--
info: equations:
private theorem List.append.eq_1.{u} : ∀ {α : Type u} (x : List α), List.append [] x = x
private theorem List.append.eq_2.{u} : ∀ {α : Type u} (x : List α) (a : α) (l : List α),
theorem List.append.eq_1.{u} : ∀ {α : Type u} (x : List α), List.append [] x = x
theorem List.append.eq_2.{u} : ∀ {α : Type u} (x : List α) (a : α) (l : List α),
List.append (a :: l) x = a :: List.append l x
-/
#guard_msgs in
@@ -23,9 +23,9 @@ private theorem List.append.eq_2.{u} : ∀ {α : Type u} (x : List α) (a : α)
/--
info: equations:
private theorem ack.eq_1 : ∀ (x : Nat), ack 0 x = x + 1
private theorem ack.eq_2 : ∀ (x_2 : Nat), ack (Nat.succ x_2) 0 = ack x_2 1
private theorem ack.eq_3 : ∀ (x_2 y : Nat), ack (Nat.succ x_2) (Nat.succ y) = ack x_2 (ack (x_2 + 1) y)
theorem ack.eq_1 : ∀ (x : Nat), ack 0 x = x + 1
theorem ack.eq_2 : ∀ (x_2 : Nat), ack (Nat.succ x_2) 0 = ack x_2 1
theorem ack.eq_3 : ∀ (x_2 y : Nat), ack (Nat.succ x_2) (Nat.succ y) = ack x_2 (ack (x_2 + 1) y)
-/
#guard_msgs in
#print eqns ack

View File

@@ -0,0 +1,90 @@
-- `g.def` is not reserved yet
theorem g.def : 1 + x = x + 1 := Nat.add_comm ..
/--
error: failed to declare `g` because `g.def` has already been declared
-/
#guard_msgs (error) in
def g (x : Nat) := x + 1
def f (x : Nat) := x + 1
/--
error: 'f.def' is a reserved name
-/
#guard_msgs (error) in
theorem f.def : f x = x + 1 := rfl
/--
error: 'f.eq_1' is a reserved name
-/
#guard_msgs (error) in
theorem f.eq_1 : f x = x + 1 := rfl
def f.eq_2_ := 10 -- Should be ok
/-- info: f.eq_1 (x : Nat) : f x = x + 1 -/
#guard_msgs in
#check f.eq_1
/-- error: unknown identifier 'f.eq_2' -/
#guard_msgs (error) in
#check f.eq_2
/-- info: f.def (x : Nat) : f x = x + 1 -/
#guard_msgs in
#check f.def
def fact : Nat Nat
| 0 => 1
| n+1 => (n+1) * fact n
/--
info: fact.def :
∀ (x : Nat),
fact x =
match x with
| 0 => 1
| Nat.succ n => (n + 1) * fact n
-/
#guard_msgs in
#check fact.def
/-- info: fact.eq_1 : fact 0 = 1 -/
#guard_msgs in
#check fact.eq_1
/-- info: fact.eq_2 (n : Nat) : fact (Nat.succ n) = (n + 1) * fact n -/
#guard_msgs in
#check fact.eq_2
/-- error: unknown identifier 'fact.eq_3' -/
#guard_msgs (error) in
#check fact.eq_3
def fact' : Nat Nat
| 0 => 1
| n+1 => (n+1) * fact' n
example : fact' 0 + fact' 0 = 2 := by
simp [fact'.eq_1]
example : fact' 0 + fact' 1 = 2 := by
rw [fact'.eq_1]
guard_target = 1 + fact' 1 = 2
rw [fact'.eq_2]
guard_target = 1 + (0+1) * fact' 0 = 2
rw [fact'.eq_1]
example : fact' 0 + fact' 1 = 2 := by
rw [fact'.def, fact'.def]; simp
guard_target = 1 + fact' 0 = 2
rw [fact'.def]
guard_target =
(1 + fact.match_1 (fun _ => Nat) 0 (fun _ => 1) fun n => (n + 1) * fact' n) = 2
simp
theorem bla : 0 = 0 := rfl
def bla.def := 1 -- should work since `bla` is a theorem
def bla.eq_1 := 2 -- should work since `bla` is a theorem