| 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) |
|
|
| / |
| 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) |
|
|
| / |
| structure CompactCoreState where |
| |
| nextMacroScope : MacroScope := firstFrontendMacroScope + 1 |
| ngen : NameGenerator := {} |
| auxDeclNGen : DeclNameGenerator := {} |
| |
| |
| |
| |
|
|
| 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 |
|
|
| / |
| 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 |
| |
| 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 |
|
|