forked from leanprover/lean4checker
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMain.lean
148 lines (130 loc) · 5.76 KB
/
Main.lean
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
/-
Copyright (c) 2023 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Lean.CoreM
import Lean4Checker.Lean
open Lean
structure Context where
newConstants : HashMap Name ConstantInfo
structure State where
env : Environment
remaining : NameSet := {}
pending : NameSet := {}
abbrev M := ReaderT Context <| StateT State IO
def M.run (env : Environment) (newConstants : HashMap Name ConstantInfo) (act : M α) : IO α :=
StateT.run' (s := { env, remaining := newConstants.keyNameSet }) do
ReaderT.run (r := { newConstants }) do
act
/-- Check if a `Name` still needs processing. If so, move it from `remaining` to `pending`. -/
def isTodo (name : Name) : M Bool := do
let r := (← get).remaining
if r.contains name then
modify fun s => { s with remaining := s.remaining.erase name, pending := s.pending.insert name }
return true
else
return false
/-- Use the current `Environment` to throw a `KernelException`. -/
def throwKernelException (ex : KernelException) : M Unit := do
let ctx := { fileName := "", options := ({} : KVMap), fileMap := default }
let state := { env := (← get).env }
Prod.fst <$> (Lean.Core.CoreM.toIO · ctx state) do Lean.throwKernelException ex
/-- Add a declaration, possibly throwing a `KernelException`. -/
def addDecl (d : Declaration) : M Unit := do
match (← get).env.addDecl d with
| .ok env => modify fun s => { s with env := env }
| .error ex => throwKernelException ex
mutual
/--
Check if a `Name` still needs to be processed (i.e. is in `remaining`).
If so, recursively replay any constants it refers to,
to ensure we add declarations in the right order.
The construct the `Declaration` from its stored `ConstantInfo`,
and add it to the environment.
-/
partial def replayConstant (name : Name) : M Unit := do
if ← isTodo name then
let some ci := (← read).newConstants.find? name | unreachable!
replayConstants ci.getUsedConstants
-- Check that this name is still pending: a mutual block may have taken care of it.
if (← get).pending.contains name then
match ci with
| .defnInfo info =>
IO.println s!"Replaying defn {name}"
addDecl (Declaration.defnDecl info)
| .thmInfo info =>
IO.println s!"Replaying thm {name}"
addDecl (Declaration.thmDecl info)
| .axiomInfo info =>
IO.println s!"Replaying axiom {name}"
addDecl (Declaration.axiomDecl info)
| .opaqueInfo info =>
IO.println s!"Replaying opaque {name}"
addDecl (Declaration.opaqueDecl info)
| .inductInfo info =>
IO.println s!"Replaying inductive {name}"
let lparams := info.levelParams
let nparams := info.numParams
let all ← info.all.mapM fun n => do pure <| ((← read).newConstants.find! n)
for o in all do
modify fun s =>
{ s with remaining := s.remaining.erase o.name, pending := s.pending.erase o.name }
let ctorInfo ← all.mapM fun ci => do
pure (ci, ← ci.inductiveVal!.ctors.mapM fun n => do
pure ((← read).newConstants.find! n))
-- Make sure we are really finished with the constructors.
for (_, ctors) in ctorInfo do
for ctor in ctors do
replayConstants ctor.getUsedConstants
let types : List InductiveType := ctorInfo.map fun ⟨ci, ctors⟩ =>
{ name := ci.name
type := ci.type
ctors := ctors.map fun ci => { name := ci.name, type := ci.type } }
addDecl (Declaration.inductDecl lparams nparams types false)
-- We discard `ctorInfo` and `recInfo` constants. These are added when generating inductives.
| .ctorInfo _ =>
IO.println s!"Skipping constructor {name}"
| .recInfo _ =>
IO.println s!"Skipping recursor {name}"
| .quotInfo _ =>
IO.println s!"Replaying quotient {name}"
addDecl (Declaration.quotDecl)
modify fun s => { s with pending := s.pending.erase name }
/-- Replay a set of constants one at a time. -/
partial def replayConstants (names : NameSet) : M Unit := do
for n in names do replayConstant n
end
/--
Given a module, obtain the environments
* `before`, by importing all its imports but not processing the contents of the module
* `after`, by importing the module
and then run a function `act before after`.
-/
unsafe def diffEnvironments (module : Name) (act : Environment → Environment → IO α) : IO α := do
Lean.withImportModules #[{module}] {} 0 fun after => do
Lean.withImportModules (after.importsOf module) {} 0 fun before =>
act before after
unsafe def replay (module : Name) : IO Unit := do
diffEnvironments module fun before after => do
let newConstants := after.constants.map₁.toList.filter
-- We skip unsafe constants, and also partial constants. Later we may want to handle partial constants.
fun ⟨n, ci⟩ => !before.constants.map₁.contains n && !ci.isUnsafe && !ci.isPartial
M.run before (HashMap.ofList newConstants) do
for (n, _) in newConstants do
replayConstant n
/--
Run as e.g. `lake exe lean4checker Mathlib.Data.Nat.Basic`.
This will replay all the new declarations from this file into the `Environment`
as it was at the beginning of the file, using the kernel to check them.
This is not an external verifier, simply a tool to detect "environment hacking".
-/
unsafe def main (args : List String) : IO UInt32 := do
initSearchPath (← findSysroot)
let module ← match args with
| [mod] => match Syntax.decodeNameLit s!"`{mod}" with
| some m => pure m
| none => throw <| IO.userError s!"Could not resolve module: {mod}"
| _ => throw <| IO.userError "Usage: lake exe lean4checker Mathlib.Data.Nat.Basic"
replay module
return 0