fix: run beforeElaboration attributes on inductives (#13813)
This PR fixes an issue where `beforeElaboration` attributes were not being run on `inductive`/`structure`/`coinductive` commands. Closes #13433. There is also light refactoring of MutualInductive, as well as a mild performance enhancement to avoid repeated re-elaboration of `variable`s.
This commit is contained in:
parent
47e96cd458
commit
ad1c983a43
3 changed files with 110 additions and 44 deletions
|
|
@ -1595,14 +1595,14 @@ private def elabFlatInductiveViews (vars : Array Expr) (elabs : Array InductiveE
|
||||||
enableRealizationsForConst e.view.declName
|
enableRealizationsForConst e.view.declName
|
||||||
|
|
||||||
/-- Ensures that there are no conflicts among or between the type and constructor names defined in `elabs`. -/
|
/-- Ensures that there are no conflicts among or between the type and constructor names defined in `elabs`. -/
|
||||||
private def checkNoInductiveNameConflicts (elabs : Array InductiveElabStep1) (isCoinductive : Bool := false) : TermElabM Unit := do
|
private def checkNoInductiveNameConflicts (elabs : Array InductiveElabStep1) (isCoinductive : Bool := false) : CommandElabM Unit := do
|
||||||
let throwErrorsAt (init cur : Syntax) (msg : MessageData) : TermElabM Unit := do
|
let throwErrorsAt (init cur : Syntax) (msg : MessageData) : CommandElabM Unit := do
|
||||||
logErrorAt init msg
|
logErrorAt init msg
|
||||||
throwErrorAt cur msg
|
throwErrorAt cur msg
|
||||||
-- Maps names of inductive types to `true` and those of constructors to `false`, along with syntax refs
|
-- Maps names of inductive types to `true` and those of constructors to `false`, along with syntax refs
|
||||||
let mut uniqueNames : Std.HashMap Name (Bool × Syntax) := {}
|
let mut uniqueNames : Std.HashMap Name (Bool × Syntax) := {}
|
||||||
let declString := if isCoinductive then "coinductive predicate" else "inductive type"
|
let declString := if isCoinductive then "coinductive predicate" else "inductive type"
|
||||||
trace[Elab.inductive] "deckString: {declString}"
|
trace[Elab.inductive] "declString: {declString}"
|
||||||
for { view, .. } in elabs do
|
for { view, .. } in elabs do
|
||||||
let typeDeclName := privateToUserName view.declName
|
let typeDeclName := privateToUserName view.declName
|
||||||
if let some (prevNameIsType, prevRef) := uniqueNames[typeDeclName]? then
|
if let some (prevNameIsType, prevRef) := uniqueNames[typeDeclName]? then
|
||||||
|
|
@ -1656,46 +1656,23 @@ private def applyDerivingHandlers (views : Array InductiveView) : CommandElabM U
|
||||||
declNames := declNames.push view.declName
|
declNames := declNames.push view.declName
|
||||||
classView.applyHandlers declNames
|
classView.applyHandlers declNames
|
||||||
|
|
||||||
private def elabInductiveViewsPostprocessing (views : Array InductiveView) (res : FinalizeContext)
|
private def elabInductiveViewsFinalize (views : Array InductiveView) (res : FinalizeContext) :
|
||||||
: CommandElabM Unit := do
|
CommandElabM Unit := do
|
||||||
let view0 := views[0]!
|
|
||||||
let ref := view0.ref
|
|
||||||
applyComputedFields views -- NOTE: any generated code before this line is invalid
|
applyComputedFields views -- NOTE: any generated code before this line is invalid
|
||||||
liftTermElabM <| withMCtx res.mctx <| withLCtx res.lctx res.localInsts do
|
liftTermElabM <| withMCtx res.mctx <| withLCtx res.lctx res.localInsts do
|
||||||
let finalizers ← res.elabs.mapM fun elab' => elab'.prefinalize res.levelParams res.params res.replaceIndFVars
|
let finalizers ← res.elabs.mapM fun elab' => elab'.prefinalize res.levelParams res.params res.replaceIndFVars
|
||||||
for view in views do withRef view.declId <|
|
for view in views do withRef view.declId <|
|
||||||
Term.applyAttributesAt view.declName view.modifiers.attrs .afterTypeChecking
|
Term.applyAttributesAt view.declName view.modifiers.attrs .afterTypeChecking
|
||||||
for elab' in finalizers do elab'.finalize
|
for elab' in finalizers do elab'.finalize
|
||||||
applyDerivingHandlers views
|
|
||||||
-- Docstrings are added during postprocessing to allow them to have checked references to
|
|
||||||
-- the type and its constructors, but before attributes to enable e.g. `@[inherit_doc X]`
|
|
||||||
runTermElabM fun _ => Term.withDeclName view0.declName do withRef ref do
|
|
||||||
for view in views do
|
|
||||||
withRef view.declId do
|
|
||||||
if let some (doc, verso) := view.docString? then
|
|
||||||
addDocStringOf verso view.declName view.binders doc
|
|
||||||
for ctor in view.ctors do
|
|
||||||
withRef ctor.declId do
|
|
||||||
if let some (doc, verso) := ctor.modifiers.docString? then
|
|
||||||
addDocStringOf verso ctor.declName ctor.binders doc
|
|
||||||
|
|
||||||
runTermElabM fun _ => Term.withDeclName view0.declName do withRef ref do
|
private def elabInductiveViewsPostprocessing (views : Array InductiveView) :
|
||||||
for view in views do withRef view.declId <|
|
CommandElabM Unit := do
|
||||||
unless (views.any (·.isCoinductive)) do
|
|
||||||
Term.applyAttributesAt view.declName view.modifiers.attrs .afterCompilation
|
|
||||||
|
|
||||||
-- Term info is added here so that docstrings are maximally available in the environment for hovers
|
|
||||||
runTermElabM fun _ => Term.withDeclName view0.declName <| withRef ref <| addTermInfoViews views
|
|
||||||
|
|
||||||
private def elabInductiveViewsPostprocessingCoinductive (views : Array InductiveView)
|
|
||||||
: CommandElabM Unit := do
|
|
||||||
let view0 := views[0]!
|
let view0 := views[0]!
|
||||||
let ref := view0.ref
|
let ref := view0.ref
|
||||||
|
|
||||||
applyDerivingHandlers views
|
applyDerivingHandlers views
|
||||||
-- Docstrings are added during postprocessing to allow them to have checked references to
|
-- Docstrings are added during postprocessing to allow them to have checked references to
|
||||||
-- the type and its constructors, but before attributes to enable e.g. `@[inherit_doc X]`
|
-- the type and its constructors, but before attributes to enable e.g. `@[inherit_doc X]`
|
||||||
runTermElabM fun _ => Term.withDeclName view0.declName do withRef ref do
|
liftTermElabM <| Term.withDeclName view0.declName do withRef ref do
|
||||||
for view in views do
|
for view in views do
|
||||||
withRef view.declId do
|
withRef view.declId do
|
||||||
if let some (doc, verso) := view.docString? then
|
if let some (doc, verso) := view.docString? then
|
||||||
|
|
@ -1705,13 +1682,12 @@ private def elabInductiveViewsPostprocessingCoinductive (views : Array Inductive
|
||||||
if let some (doc, verso) := ctor.modifiers.docString? then
|
if let some (doc, verso) := ctor.modifiers.docString? then
|
||||||
addDocStringOf verso ctor.declName ctor.binders doc
|
addDocStringOf verso ctor.declName ctor.binders doc
|
||||||
|
|
||||||
runTermElabM fun _ => Term.withDeclName view0.declName do withRef ref do
|
|
||||||
for view in views do withRef view.declId <|
|
for view in views do withRef view.declId <|
|
||||||
unless (views.any (·.isCoinductive)) do
|
unless (views.any (·.isCoinductive)) do
|
||||||
Term.applyAttributesAt view.declName view.modifiers.attrs .afterCompilation
|
Term.applyAttributesAt view.declName view.modifiers.attrs .afterCompilation
|
||||||
|
|
||||||
-- Term info is added here so that docstrings are maximally available in the environment for hovers
|
-- Term info is added here so that docstrings are maximally available in the environment for hovers
|
||||||
runTermElabM fun _ => Term.withDeclName view0.declName <| withRef ref <| addTermInfoViews views
|
addTermInfoViews views
|
||||||
|
|
||||||
def InductiveViewToCoinductiveElab (e : InductiveElabStep1) : CoinductiveElabData where
|
def InductiveViewToCoinductiveElab (e : InductiveElabStep1) : CoinductiveElabData where
|
||||||
declId := e.view.declId
|
declId := e.view.declId
|
||||||
|
|
@ -1722,26 +1698,23 @@ def InductiveViewToCoinductiveElab (e : InductiveElabStep1) : CoinductiveElabDat
|
||||||
isGreatest := e.view.isCoinductive
|
isGreatest := e.view.isCoinductive
|
||||||
|
|
||||||
def elabInductives (inductives : Array (Modifiers × Syntax)) : CommandElabM Unit := do
|
def elabInductives (inductives : Array (Modifiers × Syntax)) : CommandElabM Unit := do
|
||||||
let elabs ← runTermElabM fun _ =>
|
let elabs ← runTermElabM fun _ => inductives.mapM fun (modifiers, stx) => mkInductiveView modifiers stx
|
||||||
inductives.mapM fun (modifiers, stx) => mkInductiveView modifiers stx
|
|
||||||
|
|
||||||
let isCoinductive := elabs.any (·.view.isCoinductive)
|
let isCoinductive := elabs.any (·.view.isCoinductive)
|
||||||
|
checkNoInductiveNameConflicts elabs (isCoinductive := isCoinductive)
|
||||||
|
elabs.forM fun e => checkValidInductiveModifier e.view.modifiers
|
||||||
|
liftTermElabM <| elabs.forM fun e => withRef e.view.ref do
|
||||||
|
Term.applyAttributesAt e.view.declName e.view.modifiers.attrs .beforeElaboration
|
||||||
if isCoinductive then
|
if isCoinductive then
|
||||||
runTermElabM fun vars => do
|
runTermElabM fun vars => do
|
||||||
checkNoInductiveNameConflicts elabs (isCoinductive := true)
|
|
||||||
let flatElabs := elabs.map fun e => {e with view := updateViewWithFunctorName e.view}
|
let flatElabs := elabs.map fun e => {e with view := updateViewWithFunctorName e.view}
|
||||||
flatElabs.forM fun e => checkValidInductiveModifier e.view.modifiers
|
|
||||||
elabFlatInductiveViews vars flatElabs
|
elabFlatInductiveViews vars flatElabs
|
||||||
discard <| flatElabs.mapM fun e => MetaM.run' do mkSumOfProducts e.view.declName
|
discard <| flatElabs.mapM fun e => MetaM.run' do mkSumOfProducts e.view.declName
|
||||||
elabCoinductive (flatElabs.map InductiveViewToCoinductiveElab)
|
elabCoinductive (flatElabs.map InductiveViewToCoinductiveElab)
|
||||||
elabInductiveViewsPostprocessingCoinductive (elabs.map (·.view))
|
|
||||||
else
|
else
|
||||||
let res ← runTermElabM fun vars => do
|
let res ← runTermElabM fun vars => do
|
||||||
elabs.forM fun e => checkValidInductiveModifier e.view.modifiers
|
|
||||||
checkNoInductiveNameConflicts elabs
|
|
||||||
elabInductiveViews vars elabs
|
elabInductiveViews vars elabs
|
||||||
elabInductiveViewsPostprocessing (elabs.map (·.view)) res
|
elabInductiveViewsFinalize (elabs.map (·.view)) res
|
||||||
|
elabInductiveViewsPostprocessing (elabs.map (·.view))
|
||||||
|
|
||||||
def elabInductive (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit := do
|
def elabInductive (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit := do
|
||||||
elabInductives #[(modifiers, stx)]
|
elabInductives #[(modifiers, stx)]
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,32 @@ public meta initialize registerBuiltinAttribute {
|
||||||
-- applicationTime := .afterCompilation
|
-- applicationTime := .afterCompilation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syntax (name := myattr_beforeElaboration) "myattr_beforeElaboration" : attr
|
||||||
|
public meta initialize registerBuiltinAttribute {
|
||||||
|
name := `myattr_beforeElaboration
|
||||||
|
descr := "Simply traces when added, to debug application bugs"
|
||||||
|
add decl _ _ := do
|
||||||
|
let c := if (← getEnv).contains decl then m!"already in environment" else m!"not in environment"
|
||||||
|
logInfo m!"declaration `{decl}` tagged `myattr_beforeElaboration`, {c}"
|
||||||
|
applicationTime := .beforeElaboration
|
||||||
|
}
|
||||||
|
syntax (name := myattr_afterTypeChecking) "myattr_afterTypeChecking" : attr
|
||||||
|
public meta initialize registerBuiltinAttribute {
|
||||||
|
name := `myattr_afterTypeChecking
|
||||||
|
descr := "Simply traces when added, to debug application bugs"
|
||||||
|
add decl _ _ := do
|
||||||
|
let c := if (← getEnv).contains decl then m!"already in environment" else m!"not in environment"
|
||||||
|
logInfo m!"declaration `{decl}` tagged `myattr_afterTypeChecking`, {c}"
|
||||||
|
applicationTime := .afterTypeChecking
|
||||||
|
}
|
||||||
|
syntax (name := myattr_afterCompilation) "myattr_afterCompilation" : attr
|
||||||
|
public meta initialize registerBuiltinAttribute {
|
||||||
|
name := `myattr_afterCompilation
|
||||||
|
descr := "Simply traces when added, to debug application bugs"
|
||||||
|
add decl _ _ := do
|
||||||
|
let c := if (← getEnv).contains decl then m!"already in environment" else m!"not in environment"
|
||||||
|
logInfo m!"declaration `{decl}` tagged `myattr_afterCompilation`, {c}"
|
||||||
|
applicationTime := .afterCompilation
|
||||||
|
}
|
||||||
|
|
||||||
register_grind_attr my_grind
|
register_grind_attr my_grind
|
||||||
|
|
|
||||||
|
|
@ -225,3 +225,68 @@ example : boo (f (f (f x))) (f (f x)) = x := by
|
||||||
|
|
||||||
|
|
||||||
end GrindAttr
|
end GrindAttr
|
||||||
|
|
||||||
|
|
||||||
|
namespace Issue13433
|
||||||
|
/-!
|
||||||
|
Attributes with `beforeElaboration` were not being applied to `inductive` or `structure` commands
|
||||||
|
-/
|
||||||
|
|
||||||
|
/--
|
||||||
|
info: declaration `Issue13433.A` tagged `myattr_beforeElaboration`, not in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.A` tagged `myattr_afterTypeChecking`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.A` tagged `myattr_afterCompilation`, already in environment
|
||||||
|
-/
|
||||||
|
#guard_msgs in
|
||||||
|
@[myattr_beforeElaboration, myattr_afterTypeChecking, myattr_afterCompilation]
|
||||||
|
structure A where
|
||||||
|
/--
|
||||||
|
info: declaration `Issue13433.A` tagged `myattr_beforeElaboration`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.A` tagged `myattr_afterTypeChecking`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.A` tagged `myattr_afterCompilation`, already in environment
|
||||||
|
-/
|
||||||
|
#guard_msgs in attribute [myattr_beforeElaboration, myattr_afterTypeChecking, myattr_afterCompilation] A
|
||||||
|
|
||||||
|
/--
|
||||||
|
info: declaration `Issue13433.B` tagged `myattr_beforeElaboration`, not in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.B` tagged `myattr_afterTypeChecking`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.B` tagged `myattr_afterCompilation`, already in environment
|
||||||
|
-/
|
||||||
|
#guard_msgs in
|
||||||
|
@[myattr_beforeElaboration, myattr_afterTypeChecking, myattr_afterCompilation]
|
||||||
|
inductive B where
|
||||||
|
/--
|
||||||
|
info: declaration `Issue13433.B` tagged `myattr_beforeElaboration`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.B` tagged `myattr_afterTypeChecking`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.B` tagged `myattr_afterCompilation`, already in environment
|
||||||
|
-/
|
||||||
|
#guard_msgs in attribute [myattr_beforeElaboration, myattr_afterTypeChecking, myattr_afterCompilation] B
|
||||||
|
|
||||||
|
/--
|
||||||
|
info: declaration `Issue13433.C` tagged `myattr_beforeElaboration`, not in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.C` tagged `myattr_afterTypeChecking`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.C` tagged `myattr_afterCompilation`, already in environment
|
||||||
|
-/
|
||||||
|
#guard_msgs in
|
||||||
|
@[myattr_beforeElaboration, myattr_afterTypeChecking, myattr_afterCompilation]
|
||||||
|
coinductive C : Prop where
|
||||||
|
/--
|
||||||
|
info: declaration `Issue13433.C` tagged `myattr_beforeElaboration`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.C` tagged `myattr_afterTypeChecking`, already in environment
|
||||||
|
---
|
||||||
|
info: declaration `Issue13433.C` tagged `myattr_afterCompilation`, already in environment
|
||||||
|
-/
|
||||||
|
#guard_msgs in attribute [myattr_beforeElaboration, myattr_afterTypeChecking, myattr_afterCompilation] C
|
||||||
|
|
||||||
|
end Issue13433
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue