Skip to content

Commit 04e92ad

Browse files
committed
feat: show info on to_additive even if the declaration already exists (#816)
* We move the place where we declare `termInfo` so that it also exists if the additive declaration already existed. * If it already exists, we set `isBinder := false` * Other than that there are some code changes that do not affect the behavior of `@[to_additive]`. * We move `Config` (formerly `ValueType`) up in preparation to add a `reorder` configuration so that `@[to_additive]` can reorder arguments.
1 parent ed45c42 commit 04e92ad

File tree

2 files changed

+51
-54
lines changed

2 files changed

+51
-54
lines changed

Mathlib/Tactic/ToAdditive.lean

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,26 @@ You can enable this with `@[to_additive!]`-/
225225
def replaceAll [Functor M] [MonadOptions M] : M Bool :=
226226
(·.getBool `to_additive.replaceAll) <$> getOptions
227227

228+
/-- `Config` is the type of the arguments that can be provided to `to_additive`. -/
229+
structure Config : Type where
230+
/-- Replace all multiplicative declarations, do not use the heuristic. -/
231+
replaceAll : Bool := false
232+
/-- View the trace of the to_additive procedure.
233+
Equivalent to `set_option trace.to_additive true`. -/
234+
trace : Bool := false
235+
/-- The name of the target (the additive declaration).-/
236+
tgt : Name := Name.anonymous
237+
/-- An optional doc string.-/
238+
doc : Option String := none
239+
/-- If `allowAutoName` is `false` (default) then
240+
`@[to_additive]` will check whether the given name can be auto-generated. -/
241+
allowAutoName : Bool := false
242+
/-- The `Syntax` element corresponding to the original multiplicative declaration
243+
(or the `to_additive` attribute if it is added later),
244+
which we need for adding definition ranges. -/
245+
ref : Syntax
246+
deriving Repr
247+
228248
variable [Monad M] [MonadOptions M] [MonadEnv M]
229249

230250
/-- Auxilliary function for `additiveTest`. The bool argument *only* matters when applied
@@ -390,7 +410,7 @@ using the transforms dictionary.
390410
`pre` is the declaration that got the `@[to_additive]` attribute and `tgt_pre` is the target of this
391411
declaration. -/
392412
partial def transformDeclAux
393-
(ref : Option Syntax) (pre tgt_pre : Name) : Name → CoreM Unit := fun src ↦ do
413+
(cfg : Config) (pre tgt_pre : Name) : Name → CoreM Unit := fun src ↦ do
394414
-- if this declaration is not `pre` or an internal declaration, we do nothing.
395415
if not (src == pre || isInternal' src) then
396416
if (findTranslation? (← getEnv) src).isSome then
@@ -413,19 +433,19 @@ partial def transformDeclAux
413433
let prefixes : NameSet := .ofList [pre, env.mainModule ++ `_auxLemma]
414434
-- we first transform all auxiliary declarations generated when elaborating `pre`
415435
for n in srcDecl.type.listNamesWithPrefixes prefixes do
416-
transformDeclAux none pre tgt_pre n
436+
transformDeclAux cfg pre tgt_pre n
417437
if let some value := srcDecl.value? then
418438
for n in value.listNamesWithPrefixes prefixes do
419-
transformDeclAux none pre tgt_pre n
439+
transformDeclAux cfg pre tgt_pre n
420440
-- if the auxilliary declaration doesn't have prefix `pre`, then we have to add this declaration
421441
-- to the translation dictionary, since otherwise we cannot find the additive name.
422442
if !pre.isPrefixOf src then
423443
insertTranslation src tgt
424444
-- now transform the source declaration
425445
let trgDecl : ConstantInfo ← MetaM.run' $ updateDecl tgt srcDecl
426-
if ¬ trgDecl.hasValue then
427-
throwError "Expected {trgDecl.name} to have a value."
428-
trace[to_additive] "generating\n{trgDecl.name} :=\n {trgDecl.value!}"
446+
if !trgDecl.hasValue then
447+
throwError "Expected {tgt} to have a value."
448+
trace[to_additive] "generating\n{tgt} :=\n {trgDecl.value!}"
429449
try
430450
-- make sure that the type is correct,
431451
-- and emit a more helpful error message if it fails
@@ -434,25 +454,17 @@ partial def transformDeclAux
434454
| Exception.error _ msg => throwError "@[to_additive] failed.
435455
Type mismatch in additive declaration. For help, see the docstring
436456
of `to_additive.attr`, section `Troubleshooting`.
437-
Failed to add declaration\n{trgDecl.name}:\n{msg}"
457+
Failed to add declaration\n{tgt}:\n{msg}"
438458
| _ => panic! "unreachable"
439459
if isNoncomputable env src then
440460
addDecl trgDecl.toDeclaration!
441-
setEnv $ addNoncomputable (← getEnv) trgDecl.name
461+
setEnv $ addNoncomputable (← getEnv) tgt
442462
else
443463
addAndCompile trgDecl.toDeclaration!
444464
-- now add declaration ranges so jump-to-definition works
445465
addDeclarationRanges tgt {
446466
range := ← getDeclarationRange (← getRef)
447-
selectionRange := ← getDeclarationRange (ref.getD (← getRef))
448-
}
449-
if let some ref := ref then
450-
-- TODO: make a function for this
451-
pushInfoLeaf <| .ofTermInfo {
452-
elaborator := .anonymous, lctx := {}, expectedType? := none
453-
stx := ref, isBinder := true
454-
expr := ← mkConstWithLevelParams trgDecl.name
455-
}
467+
selectionRange := ← getDeclarationRange cfg.ref }
456468
if isProtected (← getEnv) src then
457469
setEnv $ addProtected (← getEnv) tgt
458470

@@ -491,8 +503,8 @@ Make a new copy of a declaration, replacing fragments of the names of identifier
491503
the body using the `translations` dictionary.
492504
This is used to implement `@[to_additive]`.
493505
-/
494-
def transformDecl (ref : Option Syntax) (src tgt : Name) : CoreM Unit := do
495-
transformDeclAux ref src tgt src
506+
def transformDecl (cfg : Config) (src tgt : Name) : CoreM Unit := do
507+
transformDeclAux cfg src tgt src
496508
/- We need to generate all equation lemmas for `src` and `tgt`, even for non-recursive
497509
definitions. If we don't do that, the equation lemma for `src` might be generated later
498510
when doing a `rw`, but it won't be generated for `tgt`. -/
@@ -534,26 +546,6 @@ def firstMultiplicativeArg (nm : Name) : MetaM Nat := do
534546
| [] => return 0
535547
| (head :: tail) => return tail.foldr Nat.min head
536548

537-
/-- `ValueType` is the type of the arguments that can be provided to `to_additive`. -/
538-
structure ValueType : Type where
539-
/-- Replace all multiplicative declarations, do not use the heuristic. -/
540-
replaceAll : Bool := false
541-
/-- View the trace of the to_additive procedure.
542-
Equivalent to `set_option trace.to_additive true`. -/
543-
trace : Bool := false
544-
/-- The name of the target (the additive declaration).-/
545-
tgt : Name := Name.anonymous
546-
/-- An optional doc string.-/
547-
doc : Option String := none
548-
/-- If `allowAutoName` is `false` (default) then
549-
`@[to_additive]` will check whether the given name can be auto-generated. -/
550-
allowAutoName : Bool := false
551-
/-- The `Syntax` element corresponding to the original multiplicative declaration
552-
(or the `to_additive` attribute if it is added later),
553-
which we need for adding definition ranges. -/
554-
ref : Syntax
555-
deriving Repr
556-
557549
/-- Helper for `capitalizeLike`. -/
558550
partial def capitalizeLikeAux (s : String) (i : String.Pos := 0) (p : String) : String :=
559551
if p.atEnd i || s.atEnd i then
@@ -729,38 +721,44 @@ def proceedFields (src tgt : Name) : CoreM Unit := do
729721
-- `Name.mapPrefix` will do that automatically.
730722

731723
private def elabToAdditiveAux (ref : Syntax) (replaceAll trace : Bool) (tgt : Option Syntax)
732-
(doc : Option Syntax) : ValueType :=
724+
(doc : Option Syntax) : Config :=
733725
{ replaceAll := replaceAll
734726
trace := trace
735727
tgt := match tgt with | some tgt => tgt.getId | none => Name.anonymous
736728
doc := doc.bind (·.isStrLit?)
737729
allowAutoName := false
738730
ref }
739731

740-
private def elabToAdditive : Syntax → CoreM ValueType
732+
private def elabToAdditive : Syntax → CoreM Config
741733
| `(attr| to_additive%$tk $[!%$replaceAll]? $[?%$trace]? $[$tgt]? $[$doc]?) =>
742734
return elabToAdditiveAux ((tgt.map (·.raw)).getD tk) replaceAll.isSome trace.isSome tgt doc
743735
| _ => throwUnsupportedSyntax
744736

745-
/-- `addToAdditiveAttr src val` adds a `@[to_additive]` attribute to `src` with configuration `val`.
737+
/-- `addToAdditiveAttr src cfg` adds a `@[to_additive]` attribute to `src` with configuration `cfg`.
746738
See the attribute implementation for more details. -/
747-
def addToAdditiveAttr (src : Name) (val : ValueType) : AttrM Unit :=
748-
withOptions (· |>.setBool `to_additive.replaceAll val.replaceAll
749-
|>.updateBool `trace.to_additive (val.trace || ·)) <| do
750-
let tgt ← targetName src val.tgt val.allowAutoName
739+
def addToAdditiveAttr (src : Name) (cfg : Config) : AttrM Unit :=
740+
withOptions (· |>.setBool `to_additive.replaceAll cfg.replaceAll
741+
|>.updateBool `trace.to_additive (cfg.trace || ·)) <| do
742+
let tgt ← targetName src cfg.tgt cfg.allowAutoName
751743
insertTranslation src tgt
752744
let firstMultArg ← MetaM.run' <| firstMultiplicativeArg src
753745
if firstMultArg != 0 then
754746
trace[to_additive_detail] "Setting relevant_arg for {src} to be {firstMultArg}."
755747
relevantArgAttr.add src firstMultArg
756-
if (← getEnv).contains tgt then
748+
let alreadyExists := (← getEnv).contains tgt
749+
if alreadyExists then
757750
-- since `tgt` already exists, we just need to add translations `src.x ↦ tgt.x'`
758751
-- for any subfields.
759752
proceedFields src tgt
760753
else
761754
-- tgt doesn't exist, so let's make it
762-
transformDecl val.ref src tgt
763-
if let some doc := val.doc then
755+
transformDecl cfg src tgt
756+
-- add pop-up information when mousing over `additive_name` of `@[to_additive additive_name]`
757+
-- (the information will be over the attribute of no additive name is given)
758+
pushInfoLeaf <| .ofTermInfo {
759+
elaborator := .anonymous, lctx := {}, expectedType? := none, isBinder := !alreadyExists,
760+
stx := cfg.ref, expr := ← mkConstWithLevelParams tgt }
761+
if let some doc := cfg.doc then
764762
addDocString tgt doc
765763
return ()
766764

@@ -969,8 +967,8 @@ initialize registerBuiltinAttribute {
969967
add := fun src stx kind ↦ do
970968
if (kind != AttributeKind.global) then
971969
throwError "`to_additive` can't be used as a local attribute"
972-
let val ← elabToAdditive stx
973-
addToAdditiveAttr src val
970+
let cfg ← elabToAdditive stx
971+
addToAdditiveAttr src cfg
974972
-- Because `@[simp]` runs after compilation,
975973
-- we have to as well to be able to copy attributes correctly.
976974
applicationTime := .afterCompilation

test/toAdditive.lean

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,8 @@ if some_def.in_namespace then x * x else x
116116

117117

118118
-- cannot apply `@[to_additive]` to `some_def` if `some_def.in_namespace` doesn't have the attribute
119-
run_cmd do
120-
Elab.Command.liftCoreM <| successIfFail (ToAdditive.transformDecl (← getRef)
121-
`Test.some_def `Test.add_some_def)
119+
run_cmd Elab.Command.liftCoreM <| successIfFail <|
120+
ToAdditive.transformDecl { ref := ← getRef} `Test.some_def `Test.add_some_def
122121

123122

124123
attribute [to_additive some_other_name] some_def.in_namespace

0 commit comments

Comments
 (0)