File size: 6,283 Bytes
0115dcf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | import Pantograph.Goal
import Pantograph.Environment
import Lean.Environment
import Lean.Replay
import Lean.Util.ShareCommon
import Std.Data.HashMap
open Lean
/-!
Input/Output functions borrowed from REPL
# Pickling and unpickling objects
By abusing `saveModuleData` and `readModuleData` we can pickle and unpickle objects to disk.
-/
namespace Pantograph
/--
Save an object to disk.
If you need to write multiple objects from within a single declaration,
you will need to provide a unique `key` for each.
-/
def pickle {α : Type} (path : System.FilePath) (x : α) (key : Name := by exact decl_name%) : IO Unit :=
saveModuleData path key (unsafe unsafeCast x)
/--
Load an object from disk.
Note: The returned `CompactedRegion` can be used to free the memory behind the value
of type `α`, using `CompactedRegion.free` (which is only safe once all references to the `α` are
released). Ignoring the `CompactedRegion` results in the data being leaked.
Use `withUnpickle` to call `CompactedRegion.free` automatically.
This function is unsafe because the data being loaded may not actually have type `α`, and this
may cause crashes or other bad behavior.
-/
unsafe def unpickle (α : Type) (path : System.FilePath) : IO (α × CompactedRegion) := do
let (x, region) ← readModuleData path
pure (unsafeCast x, region)
/-- Load an object from disk and run some continuation on it, freeing memory afterwards. -/
unsafe def withUnpickle [Monad m] [MonadLiftT IO m] {α β : Type}
(path : System.FilePath) (f : α → m β) : m β := do
let (x, region) ← unpickle α path
let r ← f x
region.free
pure r
/--
Pickle an `Environment` to disk relative to a background.
We only store:
* the list of imports
* the new constants from `Environment.constants`
and when unpickling, we build a fresh `Environment` from the imports,
and then add the new constants.
-/
@[export pantograph_env_pickle_m]
def environmentPickle (env : Environment) (path : System.FilePath) (background? : Option Environment := .none)
: IO Unit :=
let distilled := distilEnvironment env background?
pickle path (ShareCommon.shareCommon distilled)
def resurrectEnvironment
(distilled : DistilledEnvironment)
(background? : Option Environment := .none)
: IO Environment := do
let (imports, constArray) := distilled
let env ← match background? with
| .none => importModules imports {} 0 (loadExts := true)
| .some env =>
assert! env.imports == imports
pure env
env.replay (Std.HashMap.ofList constArray.toList)
/--
Unpickle an `Environment` from disk.
We construct a fresh `Environment` with the relevant imports,
and then replace the new constants.
The `CompactedRegion` must be free'd after using the environment. Otherwise
there could be memory leaks.
-/
@[export pantograph_env_unpickle_m]
def environmentUnpickle (path : System.FilePath) (background? : Option Environment := .none)
: IO (Environment × CompactedRegion) := unsafe do
let (distilled, region) ← unpickle (Array Import × ConstArray) path
return (← resurrectEnvironment distilled background?, region)
/-- `CoreM`'s state, with information irrelevant to pickling masked out -/
structure CompactCoreState where
-- env : Environment
nextMacroScope : MacroScope := firstFrontendMacroScope + 1
ngen : NameGenerator := {}
auxDeclNGen : DeclNameGenerator := {}
-- traceState : TraceState := {}
-- cache : Cache := {}
-- messages : MessageLog := {}
-- infoState : Elab.InfoState := {}
structure CompactGoalState where
env : DistilledEnvironment
core : CompactCoreState
«meta» : Meta.State
«elab» : Elab.Term.State
tactic : Elab.Tactic.State
root : MVarId
parentMVars : List MVarId
fragments : FragmentMap
/-- Pickles a goal state by taking its diff relative to a background
environment. This function eliminates all `MessageData` from synthetic
metavariables, because these `MessageData` objects frequently carry closures,
which cannot be pickled. If there is no synthetic metavariable, this would not
cause a difference. -/
@[export pantograph_goal_state_pickle_m]
def goalStatePickle (goalState : GoalState) (path : System.FilePath) (background? : Option Environment := .none)
: IO Unit :=
let {
savedState := {
term := {
«meta» := {
core := {
env, nextMacroScope, ngen, auxDeclNGen, ..
},
«meta»,
}
«elab» := «elab»@{
syntheticMVars, ..
},
},
tactic
}
root,
parentMVars,
fragments,
} := goalState
-- Delete `MessageData`s
let syntheticMVars : MVarIdMap _ := syntheticMVars.foldl (init := .empty) λ acc key val =>
let kind := match val.kind with
| .typeClass _ => .typeClass .none
| .coe header? expectedType e f? _ => .coe header? expectedType e f? .none
| k => k
acc.insert key { val with kind }
let compacted : CompactGoalState := {
env := distilEnvironment env background?,
core := { nextMacroScope, ngen, auxDeclNGen },
«meta»,
«elab» := { «elab» with syntheticMVars },
tactic,
root,
parentMVars,
fragments,
}
pickle path (ShareCommon.shareCommon compacted)
/--
Loads a goal state from disk. The background is the environment which the goal
state was cached relative to.
The `CompactedRegion` must be free'd after using the goal state. Otherwise there
will be memory leaks.
-/
@[export pantograph_goal_state_unpickle_m]
def goalStateUnpickle (path : System.FilePath) (background? : Option Environment := .none)
: IO (GoalState × CompactedRegion) := unsafe do
let ({
env,
core,
«meta»,
«elab»,
tactic,
root,
parentMVars,
fragments,
}, region) ← Pantograph.unpickle CompactGoalState path
let env ← resurrectEnvironment env background?
let goalState := {
savedState := {
term := {
«meta» := {
core := {
core with
passedHeartbeats := 0,
env,
},
«meta»,
},
«elab»,
},
tactic,
},
root,
parentMVars,
fragments,
}
return (goalState, region)
end Pantograph
|