Skip to content

Commit cb0d344

Browse files
committed
feat: revamp aux decl name generation
1 parent 6ca31ba commit cb0d344

27 files changed

+174
-87
lines changed

src/Lean/Compiler/InitAttr.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def setBuiltinInitAttr (env : Environment) (declName : Name) (initFnName : Name
140140
builtinInitAttr.setParam env declName initFnName
141141

142142
def declareBuiltin (forDecl : Name) (value : Expr) : CoreM Unit := do
143-
let name ← mkAuxName (`_regBuiltin ++ forDecl) 1
143+
let name ← mkAuxDeclName (kind := `_regBuiltin ++ forDecl)
144144
let type := mkApp (mkConst `IO) (mkConst `Unit)
145145
let decl := Declaration.defnDecl { name, levelParams := [], type, value, hints := ReducibilityHints.opaque,
146146
safety := DefinitionSafety.safe }

src/Lean/CoreM.lean

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,91 @@ def useDiagnosticMsg : MessageData :=
7070
else
7171
pure s!"\n\nAdditional diagnostic information may be available using the `set_option {diagnostics.name} true` command."
7272

73+
/-- Name generator that creates user-accessible names. -/
74+
structure DeclNameGenerator where
75+
namePrefix : Name := .anonymous
76+
-- We use a non-nil list instead of changing `namePrefix` as we want to distinguish between
77+
-- numeric components in the original name (e.g. from macro scopes) and ones added by `mkChild`.
78+
private idx : Nat := 1
79+
private parentIdxs : List Nat := .nil
80+
deriving Inhabited
81+
82+
namespace DeclNameGenerator
83+
84+
private def idxs (g : DeclNameGenerator) : List Nat :=
85+
g.idx :: g.parentIdxs
86+
87+
def next (g : DeclNameGenerator) : DeclNameGenerator :=
88+
{ g with idx := g.idx + 1 }
89+
90+
/--
91+
Creates a user-accessible unique name of the following structure:
92+
```
93+
<name prefix>.<infix>_<numeric components>_...
94+
```
95+
Uniqueness is guaranteed for the current branch of elaboration. When entering parallelism and other
96+
branching elaboration steps, `mkChild` must be used (automatically done in `wrapAsync*`).
97+
-/
98+
partial def mkUniqueName (env : Environment) (g : DeclNameGenerator) («infix» : Name) :
99+
(Name × DeclNameGenerator) := Id.run do
100+
-- `Name.append` does not allow macro scopes on both operands; as the result of this function is
101+
-- unlikely to be referenced in a macro; the choice doesn't really matter.
102+
let «infix» := if g.namePrefix.hasMacroScopes && infix.hasMacroScopes then infix.eraseMacroScopes else «infix»
103+
let base := g.namePrefix ++ «infix»
104+
let mut g := g
105+
-- NOTE: We only check the current branch and rely on the documented invariant instead because we
106+
-- do not want to block here and because it would not solve the issue for completely separated
107+
-- threads of elaboration such as in Aesop's backtracking search.
108+
while env.containsOnBranch (curr g base) do
109+
g := g.next
110+
return (curr g base, g)
111+
where curr (g : DeclNameGenerator) (base : Name) : Name :=
112+
g.idxs.foldr (fun i n => n.appendIndexAfter i) base
113+
114+
def mkChild (g : DeclNameGenerator) : DeclNameGenerator × DeclNameGenerator :=
115+
({ g with parentIdxs := g.idx :: g.parentIdxs, idx := 1 },
116+
{ g with idx := g.idx + 1 })
117+
118+
end DeclNameGenerator
119+
120+
class MonadDeclNameGenerator (m : TypeType) where
121+
getDeclNGen : m DeclNameGenerator
122+
setDeclNGen : DeclNameGenerator → m Unit
123+
124+
export MonadDeclNameGenerator (getDeclNGen setDeclNGen)
125+
126+
instance [MonadLift m n] [MonadDeclNameGenerator m] : MonadDeclNameGenerator n where
127+
getDeclNGen := liftM (getDeclNGen : m _)
128+
setDeclNGen := fun ngen => liftM (setDeclNGen ngen : m _)
129+
130+
/--
131+
Creates a new name for use as an auxiliary declaration that can be assumed to be globally unique.
132+
133+
Uniqueness is guaranteed for the current branch of elaboration. When entering parallelism and other
134+
branching elaboration steps, `mkChild` must be used (automatically done in `wrapAsync*`).
135+
-/
136+
def mkAuxDeclName [Monad m] [MonadEnv m] [MonadDeclNameGenerator m] (kind : Name := `_aux) : m Name := do
137+
let ngen ← getDeclNGen
138+
let (n, ngen) := ngen.mkUniqueName (← getEnv) («infix» := kind)
139+
setDeclNGen ngen
140+
return n
141+
142+
/--
143+
Adjusts the `namePrefix` of `getDeclNGen` to the given name and resets the nested counter.
144+
-/
145+
def withDeclNameForAuxNaming [Monad m] [MonadFinally m] [MonadDeclNameGenerator m]
146+
(name : Name) (x : m α) : m α := do
147+
let ngen ← getDeclNGen
148+
-- do not reset index if prefix unchanged
149+
if ngen.namePrefix != name then
150+
try
151+
setDeclNGen { namePrefix := name }
152+
x
153+
finally
154+
setDeclNGen ngen
155+
else
156+
x
157+
73158
namespace Core
74159

75160
builtin_initialize registerTraceClass `Kernel
@@ -93,6 +178,11 @@ structure State where
93178
nextMacroScope : MacroScope := firstFrontendMacroScope + 1
94179
/-- Name generator for producing unique `FVarId`s, `MVarId`s, and `LMVarId`s -/
95180
ngen : NameGenerator := {}
181+
/--
182+
Name generator for creating persistent auxiliary declarations. Separate from `ngen` to keep
183+
numbers smaller and create user-accessible names.
184+
-/
185+
auxDeclNGen : DeclNameGenerator := {}
96186
/-- Trace messages -/
97187
traceState : TraceState := {}
98188
/-- Cache for instantiating universe polymorphic declarations. -/
@@ -197,6 +287,10 @@ instance : MonadNameGenerator CoreM where
197287
getNGen := return (← get).ngen
198288
setNGen ngen := modify fun s => { s with ngen := ngen }
199289

290+
instance : MonadDeclNameGenerator CoreM where
291+
getDeclNGen := return (← get).auxDeclNGen
292+
setDeclNGen ngen := modify fun s => { s with auxDeclNGen := ngen }
293+
200294
instance : MonadRecDepth CoreM where
201295
withRecDepth d x := withReader (fun ctx => { ctx with currRecDepth := d }) x
202296
getRecDepth := return (← read).currRecDepth
@@ -220,8 +314,8 @@ instance : Elab.MonadInfoTree CoreM where
220314
modifyInfoState f := modify fun s => { s with infoState := f s.infoState }
221315

222316
@[inline] def modifyCache (f : Cache → Cache) : CoreM Unit :=
223-
modify fun ⟨env, next, ngen, trace, cache, messages, infoState, snaps⟩ =>
224-
⟨env, next, ngen, trace, f cache, messages, infoState, snaps⟩
317+
modify fun ⟨env, next, ngen, auxDeclNGen, trace, cache, messages, infoState, snaps⟩ =>
318+
⟨env, next, ngen, auxDeclNGen, trace, f cache, messages, infoState, snaps⟩
225319

226320
@[inline] def modifyInstLevelTypeCache (f : InstantiateLevelCache → InstantiateLevelCache) : CoreM Unit :=
227321
modifyCache fun ⟨c₁, c₂⟩ => ⟨f c₁, c₂⟩
@@ -435,7 +529,10 @@ def logSnapshotTask (task : Language.SnapshotTask Language.SnapshotTree) : CoreM
435529
/-- Wraps the given action for use in `EIO.asTask` etc., discarding its final monadic state. -/
436530
def wrapAsync {α : Type} (act : α → CoreM β) (cancelTk? : Option IO.CancelToken) :
437531
CoreM (α → EIO Exception β) := do
532+
let (childNGen, parentNGen) := (← getDeclNGen).mkChild
533+
setDeclNGen parentNGen
438534
let st ← get
535+
let st := { st with auxDeclNGen := childNGen }
439536
let ctx ← read
440537
let ctx := { ctx with cancelTk? }
441538
let heartbeats := (← IO.getNumHeartbeats) - ctx.initHeartbeats

src/Lean/Elab/AuxDef.lean

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ def elabAuxDef : CommandElab
2424
let id := `_aux ++ (← getMainModule) ++ `_ ++ id
2525
let id := String.intercalate "_" <| id.components.map (·.toString (escape := false))
2626
let ns ← getCurrNamespace
27-
-- make sure we only add a single component so that scoped works
28-
let id ← mkAuxName (ns.mkStr id) 1
27+
-- We use a new generator here because we want more control over the name; the default would
28+
-- create a private name that then breaks the macro below. We assume that `aux_def` is not used
29+
-- with the same arguments in parallel contexts.
30+
let (id, _) := { namePrefix := ns : DeclNameGenerator }.mkUniqueName (← getEnv) («infix» := Name.mkSimple id)
2931
let id := id.replacePrefix ns Name.anonymous -- TODO: replace with def _root_.id
3032
elabCommand <|
3133
← `($[$doc?:docComment]? $[$attrs?:attributes]?

src/Lean/Elab/Command.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ structure State where
8888
nextMacroScope : Nat := firstFrontendMacroScope + 1
8989
maxRecDepth : Nat
9090
ngen : NameGenerator := {}
91+
auxDeclNGen : DeclNameGenerator := {}
9192
infoState : InfoState := {}
9293
traceState : TraceState := {}
9394
snapshotTasks : Array (Language.SnapshotTask Language.SnapshotTree) := #[]
@@ -158,6 +159,8 @@ def mkState (env : Environment) (messages : MessageLog := {}) (opts : Options :=
158159
messages := messages
159160
scopes := [{ header := "", opts := opts }]
160161
maxRecDepth := maxRecDepth.get opts
162+
-- Outside of declarations, fall back to a module-specific prefix
163+
auxDeclNGen := { namePrefix := mkPrivateName env .anonymous }
161164
}
162165

163166
/- Linters should be loadable as plugins, so store in a global IO ref instead of an attribute managed by the
@@ -203,6 +206,10 @@ instance : AddErrorMessageContext CommandElabM where
203206
let msg ← addMacroStack msg ctx.macroStack
204207
return (ref, msg)
205208

209+
instance : MonadDeclNameGenerator CommandElabM where
210+
getDeclNGen := return (← get).auxDeclNGen
211+
setDeclNGen ngen := modify fun s => { s with auxDeclNGen := ngen }
212+
206213
private def runCore (x : CoreM α) : CommandElabM α := do
207214
let s ← get
208215
let ctx ← read
@@ -225,6 +232,7 @@ private def runCore (x : CoreM α) : CommandElabM α := do
225232
let x : EIO _ _ := x.run coreCtx {
226233
env
227234
ngen := s.ngen
235+
auxDeclNGen := s.auxDeclNGen
228236
nextMacroScope := s.nextMacroScope
229237
infoState.enabled := s.infoState.enabled
230238
traceState := s.traceState
@@ -235,6 +243,7 @@ private def runCore (x : CoreM α) : CommandElabM α := do
235243
env := coreS.env
236244
nextMacroScope := coreS.nextMacroScope
237245
ngen := coreS.ngen
246+
auxDeclNGen := coreS.auxDeclNGen
238247
infoState.trees := s.infoState.trees.append coreS.infoState.trees
239248
-- we assume substitution of `assingment` has already happened, but for lazy assignments we only
240249
-- do it at the very end
@@ -312,7 +321,10 @@ def wrapAsync {α β : Type} (act : α → CommandElabM β) (cancelTk? : Option
312321
CommandElabM (α → EIO Exception β) := do
313322
let ctx ← read
314323
let ctx := { ctx with cancelTk? }
324+
let (childNGen, parentNGen) := (← getDeclNGen).mkChild
325+
setDeclNGen parentNGen
315326
let st ← get
327+
let st := { st with auxDeclNGen := childNGen }
316328
return (act · |>.run ctx |>.run' st)
317329

318330
open Language in
@@ -811,13 +823,15 @@ private def liftCommandElabMCore (cmd : CommandElabM α) (throwOnError : Bool) :
811823
nextMacroScope := s.nextMacroScope
812824
maxRecDepth := ctx.maxRecDepth
813825
ngen := s.ngen
826+
auxDeclNGen := s.auxDeclNGen
814827
scopes := [{ header := "", opts := ctx.options }]
815828
infoState.enabled := s.infoState.enabled
816829
}
817830
modify fun coreState => { coreState with
818831
env := commandState.env
819832
nextMacroScope := commandState.nextMacroScope
820833
ngen := commandState.ngen
834+
auxDeclNGen := commandState.auxDeclNGen
821835
traceState.traces := coreState.traceState.traces ++ commandState.traceState.traces
822836
}
823837
if throwOnError then

src/Lean/Elab/MutualDef.lean

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,11 @@ where
11341134

11351135
-- now start new thread for body elaboration, then nested thread for kernel checking
11361136
let cancelTk ← IO.CancelToken.new
1137-
let act ← wrapAsyncAsSnapshot (desc := s!"elaborating proof of {declId.declName}")
1137+
let act ←
1138+
-- NOTE: We must set the decl name before going async to ensure that the `auxDeclNGen` is
1139+
-- forked correctly.
1140+
withDeclName header.declName do
1141+
wrapAsyncAsSnapshot (desc := s!"elaborating proof of {declId.declName}")
11381142
(cancelTk? := cancelTk) fun _ => do profileitM Exception "elaboration" (← getOptions) do
11391143
setEnv async.asyncEnv
11401144
try

src/Lean/Elab/PreDefinition/Basic.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ def abstractNestedProofs (preDef : PreDefinition) (cache := true) : MetaM PreDef
9191
if preDef.kind.isTheorem || preDef.kind.isExample then
9292
pure preDef
9393
else do
94-
let value ← Meta.abstractNestedProofs (cache := cache) preDef.declName preDef.value
94+
let value ←
95+
withDeclNameForAuxNaming preDef.declName do
96+
Meta.abstractNestedProofs (cache := cache) preDef.value
9597
pure { preDef with value := value }
9698

9799
/-- Auxiliary method for (temporarily) adding pre definition as an axiom -/

src/Lean/Elab/Tactic/AsAuxLemma.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ def elabAsAuxLemma : Lean.Elab.Tactic.Tactic
2020
unless mvars.isEmpty do
2121
throwError "Cannot abstract term into auxiliary lemma because there are open goals."
2222
let e ← instantiateMVars (mkMVar mvarId)
23-
let e ← mkAuxTheorem (prefix? := (← Term.getDeclName?)) (← mvarId.getType) e
23+
let e ← mkAuxTheorem (← mvarId.getType) e
2424
mvarId.assign e
2525
| _ => throwError "Invalid as_aux_lemma syntax"

src/Lean/Elab/Tactic/Grind.lean

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,17 @@ def grind
135135
(mvarId : MVarId) (config : Grind.Config)
136136
(only : Bool)
137137
(ps : TSyntaxArray ``Parser.Tactic.grindParam)
138-
(mainDeclName : Name) (fallback : Grind.Fallback) : TacticM Grind.Trace := do
138+
(fallback : Grind.Fallback) : TacticM Grind.Trace := do
139139
mvarId.withContext do
140140
let params ← mkGrindParams config only ps
141141
let type ← mvarId.getType
142142
let mvar' ← mkFreshExprSyntheticOpaqueMVar type
143-
let result ← Grind.main mvar'.mvarId! params mainDeclName fallback
143+
let result ← Grind.main mvar'.mvarId! params fallback
144144
if result.hasFailures then
145145
throwError "`grind` failed\n{← result.toMessageData}"
146146
-- `grind` proofs are often big
147147
let e ← if (← isProp type) then
148-
mkAuxTheorem (prefix? := mainDeclName) type (← instantiateMVarsProfiling mvar') (zetaDelta := true)
148+
mkAuxTheorem type (← instantiateMVarsProfiling mvar') (zetaDelta := true)
149149
else
150150
let auxName ← Term.mkAuxName `grind
151151
mkAuxDefinition auxName type (← instantiateMVarsProfiling mvar') (zetaDelta := true)
@@ -181,9 +181,8 @@ def evalGrindCore
181181
let params := if let some params := params then params.getElems else #[]
182182
if Grind.grind.warning.get (← getOptions) then
183183
logWarningAt ref "The `grind` tactic is experimental and still under development. Avoid using it in production projects."
184-
let declName := (← Term.getDeclName?).getD `_grind
185184
withMainContext do
186-
let result ← grind (← getMainGoal) config only params declName fallback
185+
let result ← grind (← getMainGoal) config only params fallback
187186
replaceMainGoal []
188187
return result
189188

src/Lean/Elab/Tactic/Omega/Frontend.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,6 @@ open Lean Elab Tactic Parser.Tactic
672672

673673
/-- The `omega` tactic, for resolving integer and natural linear arithmetic problems. -/
674674
def omegaTactic (cfg : OmegaConfig) : TacticM Unit := do
675-
let declName? ← Term.getDeclName?
676675
liftMetaFinishingTactic fun g => do
677676
if debug.terminalTacticsAsSorry.get (← getOptions) then
678677
g.admit
@@ -685,7 +684,7 @@ def omegaTactic (cfg : OmegaConfig) : TacticM Unit := do
685684
trace[omega] "analyzing {hyps.length} hypotheses:\n{← hyps.mapM inferType}"
686685
omega hyps g'.mvarId! cfg
687686
-- Omega proofs are typically rather large, so hide them in a separate definition
688-
let e ← mkAuxTheorem (prefix? := declName?) type (← instantiateMVarsProfiling g') (zetaDelta := true)
687+
let e ← mkAuxTheorem type (← instantiateMVarsProfiling g') (zetaDelta := true)
689688
g.assign e
690689

691690

src/Lean/Elab/Term.lean

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,16 @@ def getMVarDecl (mvarId : MVarId) : TermElabM MetavarDecl := return (← getMCtx
609609
instance : MonadParentDecl TermElabM where
610610
getParentDeclName? := getDeclName?
611611

612-
/-- Execute `withSaveParentDeclInfoContext x` with `declName? := name`. See `getDeclName?`. -/
612+
/--
613+
Executes `x` in the context of the given declaration name. Ensures that the info tree is set up
614+
correctly and adjusts the declaration name generator to generate names below this name, resetting
615+
the nested counter.
616+
-/
613617
def withDeclName (name : Name) (x : TermElabM α) : TermElabM α :=
614-
withReader (fun ctx => { ctx with declName? := name }) <| withSaveParentDeclInfoContext x
618+
withReader (fun ctx => { ctx with declName? := name }) do
619+
withSaveParentDeclInfoContext do
620+
withDeclNameForAuxNaming name do
621+
x
615622

616623
/-- Update the universe level parameter names. -/
617624
def setLevelNames (levelNames : List Name) : TermElabM Unit :=
@@ -1894,10 +1901,7 @@ def addAutoBoundImplicits' (xs : Array Expr) (type : Expr) (k : Array Expr → E
18941901
else
18951902
forallBoundedTelescope (← mkForallFVars xs type) xs.size fun xs type => k xs type
18961903

1897-
def mkAuxName (suffix : Name) : TermElabM Name := do
1898-
match (← read).declName? <|> (← getEnv).asyncPrefix? with
1899-
| none => Lean.mkAuxName (mkPrivateName (← getEnv) `aux) 1
1900-
| some declName => Lean.mkAuxName (declName ++ suffix) 1
1904+
def mkAuxName (suffix : Name) : TermElabM Name := mkAuxDeclName (kind := suffix)
19011905

19021906
builtin_initialize registerTraceClass `Elab.letrec
19031907

0 commit comments

Comments
 (0)