lean4-htt/src/Lean/ExtraModUses.lean
Sebastian Ullrich fd3f51012f
feat: shake import minimizer aware of the module system and arbitrary elaboration dependencies (#10575)
This PR adds the necessary infrastructure for recording elaboration
dependencies that may not be apparent from the resulting environment
such as notations and other metaprograms. An adapted version of `shake`
from Mathlib is added to `script/` but may be moved to another location
or repo in the future.
2025-09-28 16:00:00 +00:00

99 lines
4 KiB
Text

/-
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 && !Lean.isMeta (← 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