Skip to content

Commit 388883d

Browse files
authored
refactor: environment extension state splitting (#8653)
Replaces the previous `export/saveEntriesFn` split with a stricly more general function such that `exportEntriesFn` could be deprecated at a later point. Also gives the new function access to the `Environment` while we're at it. Also gives `getModuleEntries` access to more olean levels in preparation for `meta import`.
1 parent 91b5e19 commit 388883d

File tree

7 files changed

+95
-73
lines changed

7 files changed

+95
-73
lines changed

src/Lean/DeclarationRange.lean

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ namespace Lean
1515

1616
builtin_initialize builtinDeclRanges : IO.Ref (NameMap DeclarationRanges) ← IO.mkRef {}
1717
builtin_initialize declRangeExt : MapDeclarationExtension DeclarationRanges ←
18-
mkMapDeclarationExtension (exportEntriesFn := fun _ => #[])
18+
mkMapDeclarationExtension (exportEntriesFn := fun _ s level =>
19+
if level < .server then
20+
#[]
21+
else s.toArray)
1922

2023
def addBuiltinDeclarationRanges (declName : Name) (declRanges : DeclarationRanges) : IO Unit :=
2124
builtinDeclRanges.modify (·.insert declName declRanges)
@@ -24,7 +27,7 @@ def addDeclarationRanges [Monad m] [MonadEnv m] (declName : Name) (declRanges :
2427
modifyEnv fun env => declRangeExt.insert env declName declRanges
2528

2629
def findDeclarationRangesCore? [Monad m] [MonadEnv m] (declName : Name) : m (Option DeclarationRanges) :=
27-
return declRangeExt.find? (includeServer := true) (← getEnv) declName
30+
return declRangeExt.find? (level := .server) (← getEnv) declName
2831

2932
def findDeclarationRanges? [Monad m] [MonadEnv m] [MonadLiftT BaseIO m] (declName : Name) : m (Option DeclarationRanges) := do
3033
let env ← getEnv

src/Lean/DocString/Extension.lean

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ structure ModuleDoc where
5858
private builtin_initialize moduleDocExt : SimplePersistentEnvExtension ModuleDoc (PersistentArray ModuleDoc) ← registerSimplePersistentEnvExtension {
5959
addImportedFn := fun _ => {}
6060
addEntryFn := fun s e => s.push e
61-
toArrayFn := fun es => es.toArray
62-
exported := false
61+
exportEntriesFnEx? := some fun _ _ es => fun
62+
| .exported => #[]
63+
| _ => es.toArray
6364
}
6465

6566
def addMainModuleDoc (env : Environment) (doc : ModuleDoc) : Environment :=
@@ -70,7 +71,7 @@ def getMainModuleDoc (env : Environment) : PersistentArray ModuleDoc :=
7071

7172
def getModuleDoc? (env : Environment) (moduleName : Name) : Option (Array ModuleDoc) :=
7273
env.getModuleIdx? moduleName |>.map fun modIdx =>
73-
moduleDocExt.getModuleEntries (includeServer := true) env modIdx
74+
moduleDocExt.getModuleEntries (level := .server) env modIdx
7475

7576
def getDocStringText [Monad m] [MonadError m] (stx : TSyntax `Lean.Parser.Command.docComment) : m String :=
7677
match stx.raw[1] with

src/Lean/Elab/RecommendedSpelling.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ open Lean.Parser.Command
2828
def allRecommendedSpellings : MetaM (Array RecommendedSpelling) := do
2929
let all := recommendedSpellingExt.toEnvExtension.getState (← getEnv)
3030
|>.importedEntries
31-
|>.push (recommendedSpellingExt.exportEntriesFn (recommendedSpellingExt.getState (← getEnv)))
31+
|>.push (recommendedSpellingExt.exportEntriesFn (← getEnv) (recommendedSpellingExt.getState (← getEnv)) .exported)
3232
return all.flatMap id
3333

3434
end Lean.Elab.Term.Doc

src/Lean/Elab/Tactic/Doc.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Displays all available tactic tags, with documentation.
9898
@[builtin_command_elab printTacTags] def elabPrintTacTags : CommandElab := fun _stx => do
9999
let all :=
100100
tacticTagExt.toEnvExtension.getState (← getEnv)
101-
|>.importedEntries |>.push (tacticTagExt.exportEntriesFn (tacticTagExt.getState (← getEnv)))
101+
|>.importedEntries |>.push (tacticTagExt.exportEntriesFn (← getEnv) (tacticTagExt.getState (← getEnv)) .exported)
102102
let mut mapping : NameMap NameSet := {}
103103
for arr in all do
104104
for (tac, tag) in arr do
@@ -148,7 +148,7 @@ def allTacticDocs : MetaM (Array TacticDoc) := do
148148
let env ← getEnv
149149
let all :=
150150
tacticTagExt.toEnvExtension.getState (← getEnv)
151-
|>.importedEntries |>.push (tacticTagExt.exportEntriesFn (tacticTagExt.getState (← getEnv)))
151+
|>.importedEntries |>.push (tacticTagExt.exportEntriesFn (← getEnv) (tacticTagExt.getState (← getEnv)) .exported)
152152
let mut tacTags : NameMap NameSet := {}
153153
for arr in all do
154154
for (tac, tag) in arr do

src/Lean/EnvExtension.lean

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,10 @@ structure SimplePersistentEnvExtensionDescr (α σ : Type) where
2121
addEntryFn : σ → α → σ
2222
addImportedFn : Array (Array α) → σ
2323
toArrayFn : List α → Array α := fun es => es.toArray
24+
exportEntriesFnEx? :
25+
Option (Environment → σ → List α → OLeanLevel → Array α) := none
2426
asyncMode : EnvExtension.AsyncMode := .mainOnly
2527
replay? : Option ((newEntries : List α) → (newState : σ) → σ → List α × σ) := none
26-
/--
27-
Whether entries should be imported into other modules. Entries are always accessible in the
28-
language server via `getModuleEntries (includeServer := true)`.
29-
-/
30-
exported : Bool := true
3128

3229
/--
3330
Returns a function suitable for `SimplePersistentEnvExtensionDescr.replay?` that replays all new
@@ -47,8 +44,9 @@ def registerSimplePersistentEnvExtension {α σ : Type} [Inhabited σ] (descr :
4744
addImportedFn := fun as => pure ([], descr.addImportedFn as),
4845
addEntryFn := fun s e => match s with
4946
| (entries, s) => (e::entries, descr.addEntryFn s e),
50-
exportEntriesFn := fun s => if descr.exported then descr.toArrayFn s.1.reverse else #[],
51-
saveEntriesFn := fun s => descr.toArrayFn s.1.reverse,
47+
exportEntriesFnEx env s level := match descr.exportEntriesFnEx? with
48+
| some fn => fn env s.2 s.1.reverse level
49+
| none => descr.toArrayFn s.1.reverse
5250
statsFn := fun s => format "number of local entries: " ++ format s.1.length
5351
asyncMode := descr.asyncMode
5452
replay? := descr.replay?.map fun replay oldState newState _ (entries, s) =>
@@ -129,14 +127,15 @@ structure MapDeclarationExtension (α : Type) extends PersistentEnvExtension (Na
129127
deriving Inhabited
130128

131129
def mkMapDeclarationExtension (name : Name := by exact decl_name%)
132-
(exportEntriesFn : NameMap α → Array (Name × α) := (·.toArray)) : IO (MapDeclarationExtension α) :=
130+
(exportEntriesFn : Environment → NameMap α → OLeanLevel → Array (Name × α) :=
131+
fun _ s _ => s.toArray) :
132+
IO (MapDeclarationExtension α) :=
133133
.mk <$> registerPersistentEnvExtension {
134134
name := name,
135135
mkInitial := pure {}
136136
addImportedFn := fun _ => pure {}
137137
addEntryFn := fun s (n, v) => s.insert n v
138-
saveEntriesFn := fun s => s.toArray
139-
exportEntriesFn
138+
exportEntriesFnEx env s level := exportEntriesFn env s level
140139
asyncMode := .async
141140
replay? := some fun _ newState newConsts s =>
142141
newConsts.foldl (init := s) fun s c =>
@@ -156,10 +155,10 @@ def insert (ext : MapDeclarationExtension α) (env : Environment) (declName : Na
156155
ext.addEntry env (declName, val)
157156

158157
def find? [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name)
159-
(includeServer := false) : Option α :=
158+
(level := OLeanLevel.exported) : Option α :=
160159
match env.getModuleIdxFor? declName with
161160
| some modIdx =>
162-
match (ext.getModuleEntries (includeServer := includeServer) env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
161+
match (ext.getModuleEntries (level := level) env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
163162
| some e => some e.2
164163
| none => none
165164
| none => (ext.findStateAsync env declName).find? declName

src/Lean/Environment.lean

Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -526,11 +526,10 @@ structure Environment where
526526
-/
527527
private base : VisibilityMap Kernel.Environment
528528
/--
529-
Additional imported environment extension state for use in the language server. This field is
530-
identical to `base.extensions` in other contexts. Access via
531-
`getModuleEntries (includeServer := true)`.
529+
Additional imported environment extension state for use in the language server, `none` elsewhere.
530+
Access via `getModuleEntries (level := .server)`.
532531
-/
533-
private serverBaseExts : Array EnvExtensionState := base.private.extensions
532+
private serverBaseExts? : Option (Array EnvExtensionState) := none
534533
/--
535534
Kernel environment task that is fulfilled when all asynchronously elaborated declarations are
536535
finished, containing the resulting environment. Also collects the environment extension state of
@@ -1457,6 +1456,19 @@ structure ImportM.Context where
14571456

14581457
abbrev ImportM := ReaderT Lean.ImportM.Context IO
14591458

1459+
/-- The level of information to save/load. Each level includes all previous ones. -/
1460+
inductive OLeanLevel where
1461+
/-- Information from exported contexts. -/
1462+
| exported
1463+
/-- Environment extension state for the language server. -/
1464+
| server
1465+
/-- Private module data. -/
1466+
| «private»
1467+
deriving DecidableEq, Ord, Repr
1468+
1469+
instance : LE OLeanLevel := leOfOrd
1470+
instance : LT OLeanLevel := ltOfOrd
1471+
14601472
/--
14611473
An environment extension with support for storing/retrieving entries from a .olean file.
14621474
- α is the type of the entries that are stored in .olean files.
@@ -1507,15 +1519,17 @@ structure PersistentEnvExtension (α : Type) (β : Type) (σ : Type) where
15071519
name : Name
15081520
addImportedFn : Array (Array α) → ImportM σ
15091521
addEntryFn : σ → β → σ
1510-
/-- Function to transform state into data that should always be imported into other modules. -/
1511-
exportEntriesFn : σ → Array α
15121522
/--
1513-
Function to transform state into data that should be imported into other modules when the module
1514-
system is disabled. When it is enabled, the data is loaded only in the language server and
1515-
accessible via `getModuleEntries (includeServer := true)`. Conventionally, this is a superset of
1516-
the data returned by `exportEntriesFn`.
1523+
Function to transform state into data that should be imported into other modules. When using the
1524+
module system without `import all`, `OLeanLevel.exported` is imported, else `OLeanLevel.private`.
1525+
Additionally, when using the module system in the language server, the `OLeanLevel.server` data is
1526+
accessible via `getModuleEntries (level := .server)`. By convention, each level should include all
1527+
data of previous levels.
1528+
1529+
This function is run after elaborating the file and joining all asynchronous threads. It is run
1530+
once for each level when the module system is enabled, otherwise once for `private`.
15171531
-/
1518-
saveEntriesFn : σ → Array α
1532+
exportEntriesFn : Environment → σ → OLeanLevel → Array α
15191533
statsFn : σ → Format
15201534

15211535
instance {α σ} [Inhabited σ] : Inhabited (PersistentEnvExtensionState α σ) :=
@@ -1527,20 +1541,26 @@ instance {α β σ} [Inhabited σ] : Inhabited (PersistentEnvExtension α β σ)
15271541
name := default,
15281542
addImportedFn := fun _ => default,
15291543
addEntryFn := fun s _ => s,
1530-
exportEntriesFn := fun _ => #[],
1531-
saveEntriesFn := fun _ => #[],
1544+
exportEntriesFn := fun _ _ _ => #[],
15321545
statsFn := fun _ => Format.nil
15331546
}
15341547

15351548
namespace PersistentEnvExtension
15361549

15371550
/--
1538-
Returns the data saved by `ext.exportEntriesFn/saveEntriesFn` when `m` was elaborated. See docs on
1539-
the functions for details.
1551+
Returns the data saved by `ext.exportEntriesFn` when `m` was elaborated. See docs on the function for
1552+
details. When using the module system, `level` can be used to select the level of data to retrieve,
1553+
but is limited to the maximum level actually imported: `exported` on the cmdline and `server` in the
1554+
language server.
15401555
-/
15411556
def getModuleEntries {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
1542-
(env : Environment) (m : ModuleIdx) (includeServer := false) : Array α :=
1543-
let exts := if includeServer then env.serverBaseExts else env.base.private.extensions
1557+
(env : Environment) (m : ModuleIdx) (level := OLeanLevel.exported) : Array α :=
1558+
let exts := match level with
1559+
| .exported => env.base.private.extensions
1560+
| .server => match env.serverBaseExts? with
1561+
| some exts => exts
1562+
| none => panic! "cannot use `server` level outside the language server"
1563+
| level => panic! s!"unsupported level {repr level}, expected `exported` or `server`"
15441564
-- safety: as in `getStateUnsafe`
15451565
unsafe (ext.toEnvExtension.getStateImpl exts).importedEntries[m]!
15461566

@@ -1573,19 +1593,36 @@ end PersistentEnvExtension
15731593

15741594
builtin_initialize persistentEnvExtensionsRef : IO.Ref (Array (PersistentEnvExtension EnvExtensionEntry EnvExtensionEntry EnvExtensionState)) ← IO.mkRef #[]
15751595

1576-
structure PersistentEnvExtensionDescr (α β σ : Type) where
1577-
name : Name := by exact decl_name%
1578-
mkInitial : IO σ
1579-
addImportedFn : Array (Array α) → ImportM σ
1580-
addEntryFn : σ → β → σ
1581-
exportEntriesFn : σ → Array α
1582-
saveEntriesFn : σ → Array α := exportEntriesFn
1583-
statsFn : σ → Format := fun _ => Format.nil
1584-
asyncMode : EnvExtension.AsyncMode := .mainOnly
1585-
replay? : Option (ReplayFn σ) := none
1596+
-- Helper structure to enable cyclic default values of `exportEntriesFn` and `exportEntriesFnEx`.
1597+
structure PersistentEnvExtensionDescrCore (α β σ : Type) where
1598+
name : Name := by exact decl_name%
1599+
mkInitial : IO σ
1600+
addImportedFn : Array (Array α) → ImportM σ
1601+
addEntryFn : σ → β → σ
1602+
exportEntriesFnEx : Environment → σ → OLeanLevel → Array α
1603+
statsFn : σ → Format := fun _ => Format.nil
1604+
asyncMode : EnvExtension.AsyncMode := .mainOnly
1605+
replay? : Option (ReplayFn σ) := none
15861606

1587-
attribute [inherit_doc PersistentEnvExtension.exportEntriesFn] PersistentEnvExtensionDescr.exportEntriesFn
1588-
attribute [inherit_doc PersistentEnvExtension.saveEntriesFn] PersistentEnvExtensionDescr.saveEntriesFn
1607+
attribute [inherit_doc PersistentEnvExtension.exportEntriesFn]
1608+
PersistentEnvExtensionDescrCore.exportEntriesFnEx
1609+
1610+
/--
1611+
Auxiliary function to signal to the structure instance elaborator that `default` should be used as
1612+
the default value for a field but only if `_otherField` has been given, which is added as an
1613+
artifical dependency.
1614+
-/
1615+
def useDefaultIfOtherFieldGiven (default : α) (_otherField : β) : α :=
1616+
default
1617+
1618+
structure PersistentEnvExtensionDescr (α β σ : Type) extends PersistentEnvExtensionDescrCore α β σ where
1619+
-- The cyclic default values force the user to specify at least one of the two following fields.
1620+
/--
1621+
Obsolete simpler version of `exportEntriesFnEx`. Its value is ignored if the latter is also
1622+
specified.
1623+
-/
1624+
exportEntriesFn : σ → Array α := useDefaultIfOtherFieldGiven (fun _ => #[]) exportEntriesFnEx
1625+
exportEntriesFnEx := fun _ s _ => exportEntriesFn s
15891626

15901627
unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ] (descr : PersistentEnvExtensionDescr α β σ) : IO (PersistentEnvExtension α β σ) := do
15911628
let pExts ← persistentEnvExtensionsRef.get
@@ -1604,8 +1641,7 @@ unsafe def registerPersistentEnvExtensionUnsafe {α β σ : Type} [Inhabited σ]
16041641
name := descr.name,
16051642
addImportedFn := descr.addImportedFn,
16061643
addEntryFn := descr.addEntryFn,
1607-
exportEntriesFn := descr.exportEntriesFn,
1608-
saveEntriesFn := descr.saveEntriesFn,
1644+
exportEntriesFn := descr.exportEntriesFnEx,
16091645
statsFn := descr.statsFn
16101646
}
16111647
persistentEnvExtensionsRef.modify fun pExts => pExts.push (unsafeCast pExt)
@@ -1661,16 +1697,6 @@ unsafe def Environment.freeRegions (env : Environment) : IO Unit :=
16611697
TODO: statically check for this. -/
16621698
env.header.regions.forM CompactedRegion.free
16631699

1664-
/-- The level of information to save/load. Each level includes all previous ones. -/
1665-
inductive OLeanLevel where
1666-
/-- Information from exported contexts. -/
1667-
| exported
1668-
/-- Environment extension state for the language server. -/
1669-
| server
1670-
/-- Private module data. -/
1671-
| «private»
1672-
deriving DecidableEq
1673-
16741700
def OLeanLevel.adjustFileName (base : System.FilePath) : OLeanLevel → System.FilePath
16751701
| .exported => base
16761702
| .server => base.addExtension "server"
@@ -1688,7 +1714,7 @@ def mkModuleData (env : Environment) (level : OLeanLevel := .private) : IO Modul
16881714
if asyncMode matches .async then
16891715
asyncMode := .sync
16901716
let state := pExt.getState (asyncMode := asyncMode) env
1691-
(pExt.name, if level = .exported then pExt.exportEntriesFn state else pExt.saveEntriesFn state)
1717+
(pExt.name, pExt.exportEntriesFn env state level)
16921718
let kenv := env.toKernelEnv
16931719
let env := env.setExporting (level != .private)
16941720
let constNames := kenv.constants.foldStage2 (fun names name _ => names.push name) #[]
@@ -2040,8 +2066,9 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
20402066
realizedImportedConsts? := none
20412067
}
20422068
env := env.setCheckedSync { env.base.private with extensions := (← setImportedEntries env.base.private.extensions moduleData) }
2043-
let serverData := modules.filterMap (·.serverData? level)
2044-
env := { env with serverBaseExts := (← setImportedEntries env.base.private.extensions serverData) }
2069+
if level ≥ .server then
2070+
let serverData := modules.filterMap (·.serverData? level)
2071+
env := { env with serverBaseExts? := some (← setImportedEntries env.base.private.extensions serverData) }
20452072
if leakEnv then
20462073
/- Mark persistent a first time before `finalizePersistenExtensions`, which
20472074
avoids costly MT markings when e.g. an interpreter closure (which

tests/pkg/module/Module.lean

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Lean
21
import Module.Basic
32
import Module.Imported
43
import Module.ImportedAll
@@ -11,14 +10,7 @@ import Module.NonModule
1110

1211
open Lean
1312

14-
/-! Non-essential metadata should only be accessible at level >= .server -/
15-
16-
#eval show IO Unit from do
17-
let env ← importModules (level := .exported) #[`Module.Basic] {}
18-
assert! env.header.isModule
19-
let _ ← Core.CoreM.toIO (ctx := { fileName := "module.lean", fileMap := default }) (s := { env }) do
20-
assert! (← findDeclarationRanges? ``f).isNone
21-
assert! (getModuleDoc? (← getEnv) `Module.Basic).any (·.size == 0)
13+
/-! Non-essential metadata should be accessible at level >= .server -/
2214

2315
#eval show IO Unit from do
2416
let env ← importModules (level := .server) #[`Module.Basic] {}

0 commit comments

Comments
 (0)