lean4-htt/src/Lean/KeyedDeclsAttribute.lean
Rob23oba 5d3df7b5f4
fix: some ExtraModUses (#10620)
This PR records extra mod uses that previously caused wrong unnecessary
import reports from shake.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2025-10-03 15:50:40 +00:00

188 lines
7.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
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
-/
module
prelude
public import Lean.ScopedEnvExtension
import Lean.Compiler.InitAttr
import Lean.Compiler.IR.CompilerM
import Lean.ExtraModUses
public section
/-!
A builder for attributes that are applied to declarations of a common type and
group them by the given attribute argument (an arbitrary `Name`, currently).
Also creates a second "builtin" attribute used for bootstrapping, which saves
the applied declarations in an `IO.Ref` instead of an environment extension.
Used to register elaborators, macros, tactics, and delaborators.
-/
namespace Lean
namespace KeyedDeclsAttribute
-- could be a parameter as well, but right now it's all names
abbrev Key := Name
/--
`KeyedDeclsAttribute` definition.
Important: `mkConst valueTypeName` and `γ` must be definitionally equal. -/
structure Def (γ : Type) where
/-- Builtin attribute name, if any (e.g., `builtin_term_elab) -/
builtinName : Name := Name.anonymous
/-- Attribute name (e.g., `term_elab) -/
name : Name
/-- Attribute description -/
descr : String
valueTypeName : Name
/-- Convert `Syntax` into a `Key`, the default implementation expects an identifier. -/
evalKey (builtin : Bool) (stx : Syntax) : AttrM Key := private_decl% (do
let stx ← Attribute.Builtin.getIdent stx
let kind := stx.getId
if (← getEnv).contains kind then
recordExtraModUseFromDecl (isMeta := false) kind
if (← Elab.getInfoState).enabled then
Elab.addConstInfo stx kind none
pure kind)
onAdded (builtin : Bool) (declName : Name) (key : Key) : AttrM Unit := pure ()
deriving Inhabited
structure OLeanEntry where
key : Key
/-- Name of a declaration stored in the environment which has type `mkConst Def.valueTypeName`. -/
declName : Name
deriving Inhabited
structure AttributeEntry (γ : Type) extends OLeanEntry where
isBuiltin : Bool
/-- Recall that we cannot store `γ` into .olean files because it is a closure.
Given `OLeanEntry.declName`, we convert it into a `γ` by using the unsafe function `evalConstCheck`. -/
value : γ
abbrev Table (γ : Type) := SMap Key (List (AttributeEntry γ))
structure ExtensionState (γ : Type) where
newEntries : List OLeanEntry := []
table : Table γ := {}
declNames : PHashSet Name := {}
erased : PHashSet Name := {}
deriving Inhabited
abbrev Extension (γ : Type) := ScopedEnvExtension OLeanEntry (AttributeEntry γ) (ExtensionState γ)
end KeyedDeclsAttribute
structure KeyedDeclsAttribute (γ : Type) where
defn : KeyedDeclsAttribute.Def γ
-- imported/builtin instances
tableRef : IO.Ref (KeyedDeclsAttribute.Table γ)
-- instances from current module
ext : KeyedDeclsAttribute.Extension γ
deriving Nonempty
namespace KeyedDeclsAttribute
private def Table.insert (table : Table γ) (v : AttributeEntry γ) : Table γ :=
match table.find? v.key with
| some vs => SMap.insert table v.key (v::vs)
| none => SMap.insert table v.key [v]
def ExtensionState.insert (s : ExtensionState γ) (v : AttributeEntry γ) : ExtensionState γ := {
table := s.table.insert v
newEntries := v.toOLeanEntry :: s.newEntries
declNames := s.declNames.insert v.declName
erased := s.erased.erase v.declName
}
def addBuiltin (attr : KeyedDeclsAttribute γ) (key : Key) (declName : Name) (value : γ) : IO Unit :=
attr.tableRef.modify fun m => m.insert { key, declName, value, isBuiltin := true }
def mkStateOfTable (table : Table γ) : ExtensionState γ := {
table
declNames := table.fold (init := {}) fun s _ es => es.foldl (init := s) fun s e => s.insert e.declName
}
def ExtensionState.erase (s : ExtensionState γ) (attrName : Name) (declName : Name) : CoreM (ExtensionState γ) := do
unless s.declNames.contains declName do
throwError "Cannot erase attribute `{attrName}`: `{.ofConstName declName}` does not have this attribute"
return { s with erased := s.erased.insert declName, declNames := s.declNames.erase declName }
protected unsafe def init {γ} (df : Def γ) (attrDeclName : Name := by exact decl_name%) : IO (KeyedDeclsAttribute γ) := do
let tableRef ← IO.mkRef ({} : Table γ)
let ext : Extension γ ← registerScopedEnvExtension {
name := attrDeclName
mkInitial := return mkStateOfTable (← tableRef.get)
ofOLeanEntry := fun _ entry => do
let ctx ← read
match ctx.env.evalConstCheck γ ctx.opts df.valueTypeName entry.declName with
| Except.ok f => return { toOLeanEntry := entry, value := f, isBuiltin := false }
| Except.error ex => throw (IO.userError ex)
addEntry := fun s e => s.insert e
toOLeanEntry := (·.toOLeanEntry)
}
unless df.builtinName.isAnonymous do
registerBuiltinAttribute {
ref := attrDeclName
name := df.builtinName
descr := "(builtin) " ++ df.descr
add := fun declName stx kind => do
unless kind == AttributeKind.global do throwAttrMustBeGlobal df.builtinName kind
let key ← df.evalKey true stx
let decl ← getConstInfo declName
let throwUnexpectedType :=
throwAttrDeclNotOfExpectedType df.builtinName declName decl.type (mkConst df.valueTypeName)
match decl.type with
| Expr.const c _ =>
if c != df.valueTypeName then throwUnexpectedType
else
/- builtin_initialize @addBuiltin $(mkConst valueTypeName) $(mkConst attrDeclName) $(key) $(declName) $(mkConst declName) -/
let val := mkAppN (mkConst ``addBuiltin) #[mkConst df.valueTypeName, mkConst attrDeclName, toExpr key, toExpr declName, mkConst declName]
declareBuiltin declName val
df.onAdded true declName key
| _ => throwUnexpectedType
applicationTime := AttributeApplicationTime.afterCompilation
}
registerBuiltinAttribute {
ref := attrDeclName
name := df.name
descr := df.descr
erase := fun declName => do
let s := ext.getState (← getEnv)
let s ← s.erase df.name declName
modifyEnv fun env => ext.modifyState env fun _ => s
add := fun declName stx attrKind => do
ensureAttrDeclIsMeta attrDeclName declName attrKind
let key ← df.evalKey false stx
match IR.getSorryDep (← getEnv) declName with
| none =>
let val ← evalConstCheck γ df.valueTypeName declName
ext.add { key := key, declName := declName, value := val, isBuiltin := true } attrKind
df.onAdded false declName key
| _ =>
-- If the declaration contains `sorry`, we skip `evalConstCheck` to avoid unnecessary bizarre error message
pure ()
applicationTime := AttributeApplicationTime.afterCompilation
}
pure { defn := df, tableRef := tableRef, ext := ext }
/-- Retrieve entries tagged with `[attr key]` or `[builtinAttr key]`. -/
def getEntries {γ} (attr : KeyedDeclsAttribute γ) (env : Environment) (key : Name) : List (AttributeEntry γ) :=
let s := attr.ext.getState env
let attrs := s.table.findD key []
if s.erased.isEmpty then
attrs
else
attrs.filter fun attr => !s.erased.contains attr.declName
/-- Retrieve values tagged with `[attr key]` or `[builtinAttr key]`. -/
def getValues {γ} (attr : KeyedDeclsAttribute γ) (env : Environment) (key : Name) : List γ :=
(getEntries attr env key).map AttributeEntry.value
end KeyedDeclsAttribute
end Lean