Skip to content

Commit ddc5830

Browse files
committed
Merge branch 'leanprover-community-master'
2 parents 817938d + 63b4bdb commit ddc5830

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+578
-91
lines changed

REPL/JSON.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace REPL
1616

1717
structure CommandOptions where
1818
allTactics : Option Bool := none
19+
rootGoals : Option Bool := none
1920
/--
2021
Should be "full", "tactics", "original", or "substantive".
2122
Anything else is ignored.
@@ -256,6 +257,7 @@ structure ProofStepResponse where
256257
traces : List String
257258
goalInfos: List GoalInfo := []
258259
mctxAfter : Option MetavarContext.Json
260+
proofStatus : String
259261
deriving ToJson, FromJson
260262

261263
instance : ToJson ProofStepResponse where
@@ -266,7 +268,8 @@ instance : ToJson ProofStepResponse where
266268
Json.nonemptyList "messages" r.messages,
267269
Json.nonemptyList "sorries" r.sorries,
268270
Json.nonemptyList "traces" r.traces,
269-
match r.mctxAfter with | some mctxAfter => [("mctxAfter", toJson mctxAfter)] | none => []
271+
match r.mctxAfter with | some mctxAfter => [("mctxAfter", toJson mctxAfter)] | none => [],
272+
[("proofStatus", r.proofStatus)]
270273
]
271274

272275
/-- Json wrapper for an error. -/

REPL/Lean/Environment.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ and then replace the new constants.
2525
-/
2626
def unpickle (path : FilePath) : IO (Environment × CompactedRegion) := unsafe do
2727
let ((imports, map₂), region) ← _root_.unpickle (Array Import × PHashMap Name ConstantInfo) path
28-
let env ← importModules imports {} 0
28+
let env ← importModules imports {} 0 (loadExts := true)
2929
return (← env.replay (Std.HashMap.ofList map₂.toList), region)
3030

3131
end Lean.Environment

REPL/Lean/InfoTree.lean

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,45 @@ partial def findAllInfo (t : InfoTree) (ctx? : Option ContextInfo) (p : Info →
165165
info ++ rest
166166
| _ => []
167167

168+
/-- Analogue of `findAllInfo` but specialized to tactics. It additionally returns root goals. -/
169+
partial def findAllInfoTactics (t : InfoTree) (ctx? : Option ContextInfo) (rootGoals : List MVarId := []) :
170+
List (Info × Option ContextInfo × List MVarId) :=
171+
match t with
172+
| .context ctx t => t.findAllInfoTactics (ctx.mergeIntoOuter? ctx?) rootGoals
173+
| .node info ts =>
174+
let tInfo := match info with
175+
| .ofTacticInfo i =>
176+
if info.isOriginal && i.isSubstantive then
177+
let rootGoals := if rootGoals.isEmpty then i.goalsBefore else rootGoals
178+
[(info, ctx?, rootGoals)]
179+
else
180+
[]
181+
| _ => []
182+
tInfo ++ ts.toList.flatMap (fun t => t.findAllInfoTactics ctx? rootGoals)
183+
| _ => []
184+
168185
/-- Return all `TacticInfo` nodes in an `InfoTree` with "original" syntax,
169186
each equipped with its relevant `ContextInfo`. -/
170-
def findTacticNodes (t : InfoTree) : List (TacticInfo × ContextInfo) :=
171-
let infos := t.findAllInfo none fun i => match i with
172-
| .ofTacticInfo i' => i.isOriginal && i'.isSubstantive
173-
| _ => false
187+
def findTacticNodes (t : InfoTree) : List (TacticInfo × ContextInfo × List MVarId) :=
188+
let infos := t.findAllInfoTactics none
174189
infos.filterMap fun p => match p with
175-
| (.ofTacticInfo i, some ctx) => (i, ctx)
190+
| (.ofTacticInfo i, some ctx, rootGoals) => (i, ctx, rootGoals)
176191
| _ => none
177192

193+
/-- Returns the root goals for a given `InfoTree`. -/
194+
partial def findRootGoals (t : InfoTree) (ctx? : Option ContextInfo := none) :
195+
List (TacticInfo × ContextInfo × List MVarId) :=
196+
match t with
197+
| .context ctx t => t.findRootGoals (ctx.mergeIntoOuter? ctx?)
198+
| .node info ts =>
199+
match info with
200+
| .ofTacticInfo i =>
201+
match ctx? with
202+
| some ctx => [(i, ctx, i.goalsBefore)]
203+
| _ => []
204+
| _ => ts.toList.flatMap (fun t => t.findRootGoals ctx?)
205+
| _ => []
206+
178207
/-- Return all `TacticInfo` nodes in an `InfoTree`
179208
corresponding to explicit invocations of the `sorry` tactic,
180209
each equipped with its relevant `ContextInfo`. -/
@@ -217,17 +246,24 @@ def sorries (t : InfoTree) : List (ContextInfo × SorryType × Position × Posit
217246
(t.findSorryTermNodes.map fun ⟨i, ctx⟩ =>
218247
(ctx, .term i.lctx i.expectedType?, stxRange ctx.fileMap i.stx))
219248

220-
def tactics (t : InfoTree) : List (ContextInfo × Syntax × List MVarId × Position × Position × Array Name) :=
249+
def tactics (t : InfoTree) : List (ContextInfo × Syntax × List MVarId × List MVarId × Position × Position × Array Name) :=
221250
-- HACK: creating a child ngen
222-
t.findTacticNodes.map fun ⟨i, ctx⟩ =>
251+
t.findTacticNodes.map fun ⟨i, ctx, rootGoals⟩ =>
223252
let range := stxRange ctx.fileMap i.stx
224253
( { ctx with mctx := i.mctxBefore, ngen := ctx.ngen.mkChild.1 },
225254
i.stx,
255+
rootGoals,
226256
i.goalsBefore,
227257
range.fst,
228258
range.snd,
229259
i.getUsedConstantsAsSet.toArray )
230260

261+
def rootGoals (t : InfoTree) : List (ContextInfo × List MVarId × Position) :=
262+
t.findRootGoals.map fun ⟨i, ctx, rootGoals⟩ =>
263+
let range := stxRange ctx.fileMap i.stx
264+
( { ctx with mctx := i.mctxBefore, ngen := ctx.ngen.mkChild.1 },
265+
rootGoals,
266+
range.fst )
231267

232268
end Lean.Elab.InfoTree
233269

@@ -282,7 +318,7 @@ namespace Lean.Elab.InfoTree
282318
Finds all tactic invocations in an `InfoTree`,
283319
ignoring structuring tactics (e.g. `by`, `;`, multiline tactics, parenthesized tactics).
284320
-/
285-
def substantiveTactics (t : InfoTree) : List (TacticInfo × ContextInfo) :=
321+
def substantiveTactics (t : InfoTree) : List (TacticInfo × ContextInfo × List MVarId) :=
286322
t.findTacticNodes.filter fun i => i.1.isSubstantive
287323

288324
end Lean.Elab.InfoTree

REPL/Main.lean

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,25 +95,26 @@ def recordProofSnapshot (proofState : ProofSnapshot) : M m Nat := do
9595
modify fun s => { s with proofStates := s.proofStates.push proofState }
9696
return id
9797

98-
def sorries (trees : List InfoTree) (env? : Option Environment) : M m (List Sorry) :=
98+
def sorries (trees : List InfoTree) (env? : Option Environment) (rootGoals? : Option (List MVarId))
99+
: M m (List Sorry) :=
99100
trees.flatMap InfoTree.sorries |>.filter (fun t => match t.2.1 with
100101
| .term _ none => false
101102
| _ => true ) |>.mapM
102103
fun ⟨ctx, g, pos, endPos⟩ => do
103104
let (goal, proofState) ← match g with
104105
| .tactic g => do
105-
let s ← ProofSnapshot.create ctx none env? [g]
106+
let s ← ProofSnapshot.create ctx none env? [g] rootGoals?
106107
pure ("\n".intercalate <| (← s.ppGoals).map fun s => s!"{s}", some s)
107108
| .term lctx (some t) => do
108-
let s ← ProofSnapshot.create ctx lctx env? [] [t]
109+
let s ← ProofSnapshot.create ctx lctx env? [] rootGoals? [t]
109110
pure ("\n".intercalate <| (← s.ppGoals).map fun s => s!"{s}", some s)
110111
| .term _ none => unreachable!
111112
let proofStateId ← proofState.mapM recordProofSnapshot
112113
let goalInfo : Option GoalInfo ← match proofState with
113114
| some proofState => do
114115
match proofState.tacticState.goals[0]? with
115116
| some goalId => do
116-
-- TODO: this does not work when it's just `sorry` instead of `by sorry`
117+
-- TODO: this does not work when it's just `sorry` instead of `by sorry` - allow printGoalInfo to return none
117118
let info ← printGoalInfo ctx goalId
118119
pure (some info)
119120
| none => pure none
@@ -128,8 +129,8 @@ def ppTactic (ctx : ContextInfo) (stx : Syntax) : IO Format :=
128129

129130
def tactics (trees : List InfoTree) : M m (List Tactic) :=
130131
trees.flatMap InfoTree.tactics |>.mapM
131-
fun ⟨ctx, stx, goals, pos, endPos, ns⟩ => do
132-
let proofState := some (← ProofSnapshot.create ctx none none goals)
132+
fun ⟨ctx, stx, rootGoals, goals, pos, endPos, ns⟩ => do
133+
let proofState := some (← ProofSnapshot.create ctx none none goals rootGoals)
133134
let goals := s!"{(← ctx.ppGoals goals)}".trim
134135
let tactic := Format.pretty (← ppTactic ctx stx)
135136
let proofStateId ← proofState.mapM recordProofSnapshot
@@ -142,6 +143,59 @@ def proofTrees (infoTrees : List InfoTree) : M m (List (List ProofStepInfo)) :=
142143
| .some proofTree => return proofTree.steps
143144
| .none => return []
144145

146+
def collectRootGoalsAsSorries (trees : List InfoTree) : M m (List Sorry) := do
147+
trees.flatMap InfoTree.rootGoals |>.mapM
148+
fun ⟨ctx, goals, pos⟩ => do
149+
let proofState := some (← ProofSnapshot.create ctx none none goals goals)
150+
let goals := s!"{(← ctx.ppGoals goals)}".trim
151+
let proofStateId ← proofState.mapM recordProofSnapshot
152+
return Sorry.of goals none pos pos proofStateId
153+
154+
/--
155+
Evaluates the current status of a proof, returning a string description.
156+
Main states include:
157+
- "Completed": Proof is complete and type checks successfully
158+
- "Incomplete": When goals remain, or proof contains sorry/metavariables
159+
- "Error": When kernel type checking errors occur
160+
161+
Inspired by LeanDojo REPL's status tracking.
162+
-/
163+
def getProofStatus (proofState : ProofSnapshot) : M m String := do
164+
match proofState.tacticState.goals with
165+
| [] =>
166+
let res := proofState.runMetaM do
167+
match proofState.rootGoals with
168+
| [goalId] =>
169+
match proofState.metaState.mctx.getExprAssignmentCore? goalId with
170+
| none => return "Error: Goal not assigned"
171+
| some pf => do
172+
let pf ← instantiateMVars pf
173+
let pft ← Meta.inferType pf >>= instantiateMVars
174+
if pf.hasSorry then
175+
return "Incomplete: contains sorry"
176+
if pf.hasExprMVar then
177+
return "Incomplete: contains metavariable(s)"
178+
179+
let decl := Declaration.defnDecl ({
180+
name := Name.anonymous,
181+
type := pft,
182+
value := pf,
183+
levelParams := (collectLevelParams {} pft).params.toList,
184+
hints := ReducibilityHints.opaque,
185+
safety := DefinitionSafety.safe
186+
})
187+
188+
try
189+
let _ ← addDecl decl
190+
catch ex =>
191+
return s!"Error: kernel type check failed: {← ex.toMessageData.toString}"
192+
return "Completed"
193+
194+
| _ => return "Not verified: more than one initial goal"
195+
return (← res).fst
196+
197+
| _ => return "Incomplete: open goals remain"
198+
145199
/-- Record a `ProofSnapshot` and generate a JSON response for it. -/
146200
def createProofStepReponse (proofState : ProofSnapshot) (old? : Option ProofSnapshot := none) :
147201
M m ProofStepResponse := do
@@ -157,7 +211,7 @@ def createProofStepReponse (proofState : ProofSnapshot) (old? : Option ProofSnap
157211
| none => pure trees
158212
-- For debugging purposes, sometimes we print out the trees here:
159213
-- trees.forM fun t => do IO.println (← t.format)
160-
let sorries ← sorries trees none
214+
let sorries ← sorries trees none (some proofState.rootGoals)
161215
let proofStateId ← recordProofSnapshot proofState
162216
let (ctx, _) ← proofState.runMetaM do return { ← CommandContextInfo.save with }
163217
let goalInfos ← proofState.tacticState.goals.mapM (fun mvarId => do
@@ -172,7 +226,7 @@ def createProofStepReponse (proofState : ProofSnapshot) (old? : Option ProofSnap
172226
traces
173227
goalInfos
174228
mctxAfter := mctxAfterJson
175-
}
229+
proofStatus := (← getProofStatus proofState) }
176230

177231
/-- Pickle a `CommandSnapshot`, generating a JSON response. -/
178232
def pickleCommandSnapshot (n : PickleEnvironment) : M m (CommandResponse ⊕ Error) := do
@@ -234,7 +288,10 @@ def runCommand (s : Command) : M IO (CommandResponse ⊕ Error) := do
234288
let messages ← messages.mapM fun m => Message.of m
235289
-- For debugging purposes, sometimes we print out the trees here:
236290
-- trees.forM fun t => do IO.println (← t.format)
237-
let sorries ← sorries trees (initialCmdState?.map (·.env))
291+
let sorries ← sorries trees (initialCmdState?.map (·.env)) none
292+
let sorries ← match s.rootGoals with
293+
| some true => pure (sorries ++ (← collectRootGoalsAsSorries trees))
294+
| _ => pure sorries
238295
let tactics ← match s.allTactics with
239296
| some true => tactics trees
240297
| _ => pure []

REPL/Snapshots.lean

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def unpickle (path : FilePath) : IO (CommandSnapshot × CompactedRegion) := unsa
8383
let ((imports, map₂, cmdState, cmdContext), region) ←
8484
_root_.unpickle (Array Import × PHashMap Name ConstantInfo × CompactableCommandSnapshot ×
8585
Command.Context) path
86-
let env ← (← importModules imports {} 0).replay (Std.HashMap.ofList map₂.toList)
86+
let env ← (← importModules imports {} 0 (loadExts := true)).replay (Std.HashMap.ofList map₂.toList)
8787
let p' : CommandSnapshot :=
8888
{ cmdState := { cmdState with env }
8989
cmdContext }
@@ -108,6 +108,7 @@ structure ProofSnapshot where
108108
termContext : Term.Context
109109
tacticState : Tactic.State
110110
tacticContext : Tactic.Context
111+
rootGoals : List MVarId
111112

112113
namespace ProofSnapshot
113114

@@ -191,7 +192,8 @@ For convenience, we also allow a list of `Expr`s, and these are appended to the
191192
as fresh metavariables with the given types.
192193
-/
193194
def create (ctx : ContextInfo) (lctx? : Option LocalContext) (env? : Option Environment)
194-
(goals : List MVarId) (types : List Expr := []) : IO ProofSnapshot := do
195+
(goals : List MVarId) (rootGoals? : Option (List MVarId)) (types : List Expr := [])
196+
: IO ProofSnapshot := do
195197
ctx.runMetaM (lctx?.getD {}) do
196198
let goals := goals ++ (← types.mapM fun t => Expr.mvarId! <$> Meta.mkFreshExprMVar (some t))
197199
let s ← getThe Core.State
@@ -206,7 +208,10 @@ def create (ctx : ContextInfo) (lctx? : Option LocalContext) (env? : Option Envi
206208
termState := {}
207209
termContext := {}
208210
tacticState := { goals }
209-
tacticContext := { elaborator := .anonymous } }
211+
tacticContext := { elaborator := .anonymous }
212+
rootGoals := match rootGoals? with
213+
| none => goals
214+
| some gs => gs }
210215

211216
open Lean.Core in
212217
/-- A copy of `Core.State` with the `Environment`, caches, and logging omitted. -/
@@ -270,22 +275,23 @@ def pickle (p : ProofSnapshot) (path : FilePath) : IO Unit := do
270275
p'.termState,
271276
({ p'.termContext with } : CompactableTermContext),
272277
p'.tacticState,
273-
p'.tacticContext)
278+
p'.tacticContext,
279+
p'.rootGoals)
274280

275281
/--
276282
Unpickle a `ProofSnapshot`.
277283
-/
278284
def unpickle (path : FilePath) (cmd? : Option CommandSnapshot) :
279285
IO (ProofSnapshot × CompactedRegion) := unsafe do
280286
let ((imports, map₂, coreState, coreContext, metaState, metaContext, termState, termContext,
281-
tacticState, tacticContext), region) ←
287+
tacticState, tacticContext, rootGoals), region) ←
282288
_root_.unpickle (Array Import × PHashMap Name ConstantInfo × CompactableCoreState ×
283289
Core.Context × Meta.State × CompactableMetaContext × Term.State × CompactableTermContext ×
284-
Tactic.State × Tactic.Context) path
290+
Tactic.State × Tactic.Context × List MVarId) path
285291
let env ← match cmd? with
286292
| none =>
287293
enableInitializersExecution
288-
(← importModules imports {} 0).replay (Std.HashMap.ofList map₂.toList)
294+
(← importModules imports {} 0 (loadExts := true)).replay (Std.HashMap.ofList map₂.toList)
289295
| some cmd =>
290296
cmd.cmdState.env.replay (Std.HashMap.ofList map₂.toList)
291297
let p' : ProofSnapshot :=
@@ -296,7 +302,8 @@ def unpickle (path : FilePath) (cmd? : Option CommandSnapshot) :
296302
termState
297303
termContext := { termContext with }
298304
tacticState
299-
tacticContext }
305+
tacticContext
306+
rootGoals }
300307
let (_, p'') ← p'.runCoreM do
301308
for o in ← getOpenDecls do
302309
if let .simple ns _ := o then

lean-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
leanprover/lean4:v4.18.0
1+
leanprover/lean4:v4.19.0-rc2

0 commit comments

Comments
 (0)