lean4-htt/src/Lean/LabelAttribute.lean
Joe Hendrix 89490f648a
fix: address symm and label bugs from #3408 (#3429)
#3408 was somewhat large and didn't properly test the symm and label
attribute code after edits to the builtin versions.

This migrates the code for generating labeled attributes from Init back
to Lean so that the required definitions are in scope.

This also addresses a mistake in the symm elaborator that prevented symm
without location information from elaborating.

Both fixes have been tested on the Std test suite and successfully
passed.
2024-02-21 07:21:07 +00:00

96 lines
3.5 KiB
Text

/-
Copyright (c) 2023 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Lean.ScopedEnvExtension
import Lean.DocString
/-!
# "Label" attributes
Allow creating attributes using `register_label_attr`,
and retrieving the array of `Name`s of declarations which have been tagged with such an attribute.
These differ slightly from the built-in "tag attributes" which can be initialized with the syntax:
```
initialize someName : TagAttribute ← registerTagAttribute `tagName "description"
```
in that a "tag attribute" can only be put on a declaration at the moment it is declared,
and can not be modified by scoping commands.
The "label attributes" constructed here support adding (or locally removing) the attribute
either at the moment of declaration, or later.
-/
namespace Lean
/-- An environment extension that just tracks an array of names. -/
abbrev LabelExtension := SimpleScopedEnvExtension Name (Array Name)
/-- The collection of all current `LabelExtension`s, indexed by name. -/
abbrev LabelExtensionMap := HashMap Name LabelExtension
/-- Store the current `LabelExtension`s. -/
builtin_initialize labelExtensionMapRef : IO.Ref LabelExtensionMap ← IO.mkRef {}
/-- Helper function for `registerLabelAttr`. -/
def mkLabelExt (name : Name := by exact decl_name%) : IO LabelExtension :=
registerSimpleScopedEnvExtension {
name := name
initial := #[]
addEntry := fun d e => if d.contains e then d else d.push e
}
/-- Helper function for `registerLabelAttr`. -/
def mkLabelAttr (attrName : Name) (attrDescr : String) (ext : LabelExtension)
(ref : Name := by exact decl_name%) : IO Unit :=
registerBuiltinAttribute {
ref := ref
name := attrName
descr := attrDescr
applicationTime := AttributeApplicationTime.afterCompilation
add := fun declName _ _ =>
ext.add declName
erase := fun declName => do
let s := ext.getState (← getEnv)
modifyEnv fun env => ext.modifyState env fun _ => s.erase declName
}
/--
Construct a new "label attribute",
which does nothing except keep track of the names of the declarations with that attribute.
Users will generally use the `register_label_attr` macro defined below.
-/
def registerLabelAttr (attrName : Name) (attrDescr : String)
(ref : Name := by exact decl_name%) : IO LabelExtension := do
let ext ← mkLabelExt ref
mkLabelAttr attrName attrDescr ext ref
labelExtensionMapRef.modify fun map => map.insert attrName ext
return ext
/--
Initialize a new "label" attribute.
Declarations tagged with the attribute can be retrieved using `Std.Tactic.LabelAttr.labelled`.
-/
macro (name := _root_.Lean.Parser.Command.registerLabelAttr)
doc:(docComment)? "register_label_attr " id:ident : command => do
let str := id.getId.toString
let idParser := mkIdentFrom id (`Parser.Attr ++ id.getId)
let descr := quote (removeLeadingSpaces
(doc.map (·.getDocString) |>.getD ("labelled declarations for " ++ id.getId.toString)))
`($[$doc:docComment]? initialize ext : Lean.LabelExtension ←
registerLabelAttr $(quote id.getId) $descr $(quote id.getId)
$[$doc:docComment]? syntax (name := $idParser:ident) $(quote str):str : attr)
/-- When `attrName` is an attribute created using `register_labelled_attr`,
return the names of all declarations labelled using that attribute. -/
def labelled (attrName : Name) : CoreM (Array Name) := do
match (← labelExtensionMapRef.get).find? attrName with
| none => throwError "No extension named {attrName}"
| some ext => pure <| ext.getState (← getEnv)
end Lean