88prelude
99public import Lean.CoreM
1010public import Lean.Compiler.MetaAttr -- TODO: public because of specializations
11+ import Init.Data.Range.Polymorphic.Stream
1112
1213/-!
1314Infrastructure for recording extra import dependencies not implied by the environment constants for
@@ -16,6 +17,41 @@ the benefit of `shake`.
1617
1718namespace Lean
1819
20+ public structure IndirectModUse where
21+ kind : String
22+ declName : Name
23+ deriving BEq
24+
25+ public builtin_initialize indirectModUseExt : SimplePersistentEnvExtension IndirectModUse (Std.HashMap Name (Array ModuleIdx)) ←
26+ registerSimplePersistentEnvExtension {
27+ addEntryFn s _ := s
28+ addImportedFn es := Id.run do
29+ let mut s := {}
30+ for es in es, modIdx in 0 ...* do
31+ for e in es do
32+ s := s.alter e.declName (·.getD #[] |>.push modIdx)
33+ return s
34+ asyncMode := .sync
35+ }
36+
37+ public def getIndirectModUses (env : Environment) (modIdx : ModuleIdx) : Array IndirectModUse :=
38+ indirectModUseExt.getModuleEntries env modIdx
39+
40+ variable [Monad m] [MonadEnv m] [MonadTrace m] [MonadOptions m] [MonadRef m] [AddMessageContext m]
41+
42+ /--
43+ Lets `shake` know that references to `declName` may also require importing the current module due to
44+ some additional metaprogramming dependency expressed by `kind`. Currently this is always the name of
45+ an attribute applied to `declName`, which is not from the current module, in the current module.
46+ `kind` is not currently used to further filter what references to `declName` require importing the
47+ current module but may in the future.
48+ -/
49+ public def recordIndirectModUse (kind : String) (declName : Name) : m Unit := do
50+ -- We can assume this is called from the main thread only and that the list of entries is short
51+ if !(indirectModUseExt.getEntries (asyncMode := .mainOnly) (← getEnv) |>.contains { kind, declName }) then
52+ trace[extraModUses] "recording indirect mod use of `{declName}` ({kind})"
53+ modifyEnv (indirectModUseExt.addEntry · { kind, declName })
54+
1955/-- Additional import dependency for elaboration. -/
2056public structure ExtraModUse where
2157 /-- Dependency's module name. -/
@@ -49,8 +85,6 @@ public def copyExtraModUses (src dest : Environment) : Environment := Id.run do
4985 env := extraModUses.addEntry env entry
5086 env
5187
52- variable [Monad m] [MonadEnv m] [MonadTrace m] [MonadOptions m] [MonadRef m] [AddMessageContext m]
53-
5488def recordExtraModUseCore (mod : Name) (isMeta : Bool) (hint : Name := .anonymous) : m Unit := do
5589 let entry := { module := mod, isExported := (← getEnv).isExporting, isMeta }
5690 if !(extraModUses.getState (asyncMode := .local) (← getEnv)).contains entry then
@@ -62,6 +96,9 @@ def recordExtraModUseCore (mod : Name) (isMeta : Bool) (hint : Name := .anonymou
6296/--
6397Records an additional import dependency for the current module, using `Environment.isExporting` as
6498the visibility level.
99+
100+ NOTE: Directly recording a module name does not trigger including indirect dependencies recorded via
101+ `recordIndirectModUse`, prefer `recordExtraModUseFromDecl` instead.
65102-/
66103public def recordExtraModUse (modName : Name) (isMeta : Bool) : m Unit := do
67104 if modName != (← getEnv).mainModule then
@@ -78,6 +115,8 @@ public def recordExtraModUseFromDecl (declName : Name) (isMeta : Bool) : m Unit
78115 -- If the declaration itself is already `meta`, no need to mark the import.
79116 let isMeta := isMeta && !isMarkedMeta (← getEnv) declName
80117 recordExtraModUseCore mod.module isMeta (hint := declName)
118+ for modIdx in (indirectModUseExt.getState (asyncMode := .local) env)[declName]?.getD #[] do
119+ recordExtraModUseCore env.header.modules[modIdx]!.module (isMeta := false ) (hint := declName)
81120
82121builtin_initialize isExtraRevModUseExt : SimplePersistentEnvExtension Unit Unit ←
83122 registerSimplePersistentEnvExtension {
0 commit comments