/- Copyright (c) 2025 Lean FRO. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Sebastian Ullrich -/ module prelude public import Lean.CoreM public import Lean.Compiler.MetaAttr -- TODO: public because of specializations /-! Infrastructure for recording extra import dependencies not implied by the environment constants for the benefit of `shake`. -/ namespace Lean /-- Additional import dependency for elaboration. -/ public structure ExtraModUse where /-- Dependency's module name. -/ module : Name /-- Whether dependency must be imported as `public`. -/ isExported : Bool /-- Whether dependency must be imported as `meta`. -/ isMeta : Bool deriving BEq, Hashable, Repr builtin_initialize extraModUses : SimplePersistentEnvExtension ExtraModUse (PHashSet ExtraModUse) ← registerSimplePersistentEnvExtension { addEntryFn m k := m.insert k addImportedFn _ := {} exportEntriesFnEx? := some fun _ _ entries => fun | .private => entries.toArray | _ => #[] asyncMode := .sync replay? := some <| SimplePersistentEnvExtension.replayOfFilter (·.contains ·) (·.insert ·) } /-- Returns additional recorded import dependencies of the given module. -/ public def getExtraModUses (env : Environment) (modIdx : ModuleIdx) : Array ExtraModUse := extraModUses.getModuleEntries env modIdx /-- Copies additional recorded import dependencies from one environment to another. -/ public def copyExtraModUses (src dest : Environment) : Environment := Id.run do let mut env := dest for entry in extraModUses.getEntries (asyncMode := .local) src do if !(extraModUses.getState (asyncMode := .local) env).contains entry then env := extraModUses.addEntry env entry env variable [Monad m] [MonadEnv m] [MonadTrace m] [MonadOptions m] [MonadRef m] [AddMessageContext m] def recordExtraModUseCore (mod : Name) (isMeta : Bool) (hint : Name := .anonymous) : m Unit := do let entry := { module := mod, isExported := (← getEnv).isExporting, isMeta } if !(extraModUses.getState (asyncMode := .local) (← getEnv)).contains entry then trace[extraModUses] "recording {if entry.isExported then "public" else "private"} \ {if isMeta then "meta" else "regular"} extra mod use {mod}\ {if hint.isAnonymous then m!"" else m!" of {hint}"}" modifyEnv (extraModUses.addEntry · entry) /-- Records an additional import dependency for the current module, using `Environment.isExporting` as the visibility level. -/ public def recordExtraModUse (modName : Name) (isMeta : Bool) : m Unit := do if modName != (← getEnv).mainModule then recordExtraModUseCore modName isMeta /-- Records the module of the given declaration as an additional import dependency for the current module, using `Environment.isExporting` as the visibility level. If the declaration itself is already `meta`, the module dependency is recorded as a non-`meta` dependency. -/ public def recordExtraModUseFromDecl (declName : Name) (isMeta : Bool) : m Unit := do let env ← getEnv if let some mod := env.getModuleIdxFor? declName |>.bind (env.header.modules[·]?) then -- If the declaration itself is already `meta`, no need to mark the import. let isMeta := isMeta && !isMarkedMeta (← getEnv) declName recordExtraModUseCore mod.module isMeta (hint := declName) builtin_initialize isExtraRevModUseExt : SimplePersistentEnvExtension Unit Unit ← registerSimplePersistentEnvExtension { addEntryFn s e := () addImportedFn _ := () asyncMode := .sync } /-- Checks whether this module should be preserved as an import by `shake`. -/ public def isExtraRevModUse (env : Environment) (modIdx : ModuleIdx) : Bool := !(isExtraRevModUseExt.getModuleEntries env modIdx |>.isEmpty) /-- Records this module to be preserved as an import by `shake`. -/ public def recordExtraRevUseOfCurrentModule : m Unit := do if isExtraRevModUseExt.getEntries (asyncMode := .local) (← getEnv) |>.isEmpty then modifyEnv (isExtraRevModUseExt.addEntry · ()) builtin_initialize registerTraceClass `extraModUses