Skip to content

Commit c42b68d

Browse files
committed
Proof step verification using kernel. leanprover-community#3
1 parent b9c90bf commit c42b68d

File tree

1 file changed

+64
-75
lines changed

1 file changed

+64
-75
lines changed

REPL/Main.lean

Lines changed: 64 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -247,83 +247,72 @@ def getProofStatus (proofState : ProofSnapshot) : M m String := do
247247
| _ => return "Incomplete: open goals remain"
248248

249249
/--
250-
Returns a list of newly assigned metavariables and their expressions,
251-
comparing the current proof state with an optional old proof state.
252-
If no old state is provided, returns all assigned MVars.
253-
-/
254-
def getNewlyAssignedMVars (proofState : ProofSnapshot) (oldProofState? : Option ProofSnapshot := none) :
255-
M m (List (MVarId × Expr)) := do
256-
-- Get all goals from both states
257-
let allGoals := match oldProofState? with
258-
| none => proofState.tacticState.goals
259-
| some oldState =>
260-
(proofState.tacticState.goals ++ oldState.tacticState.goals).eraseDups
261-
262-
-- Check which have assignments in new state
263-
let (assignments, _) ← proofState.runMetaM do
264-
let mut assignments := []
265-
for goalId in allGoals do
266-
match proofState.metaState.mctx.getExprAssignmentCore? goalId with
267-
| none => continue
268-
| some pf => do
269-
let pf ← instantiateMVars pf
270-
assignments := (goalId, pf) :: assignments
271-
return assignments
272-
return assignments
273-
274-
/--
275-
Verifies that all assigned goals in the proof state have valid assignments that type check.
250+
Verifies that all goals from the old state are properly handled in the new state.
276251
Returns either "OK" or an error message describing the first failing goal.
277252
-/
278-
def verifyGoalAssignment (proofState : ProofSnapshot) (oldProofState? : Option ProofSnapshot := none) :
253+
def verifyGoalAssignment (ctx : ContextInfo) (proofState : ProofSnapshot) (oldProofState? : Option ProofSnapshot := none) :
279254
M m String := do
280-
let mut allOk := true
281-
let mut errorMsg := ""
282-
283-
let assignments ← getNewlyAssignedMVars proofState oldProofState?
284-
-- Print assignments for debugging
285-
-- IO.println s!"Number of assignments: {assignments.length}"
286-
-- for (goalId, pf) in assignments do
287-
-- IO.println s!"Goal {goalId.name} := {pf}"
288-
for (goalId, pf) in assignments do
289-
let (res, _) ← proofState.runMetaM do
290-
-- Check that proof has expected type
291-
let pft ← Meta.inferType pf >>= instantiateMVars
292-
let expectedType ← Meta.inferType (mkMVar goalId) >>= instantiateMVars
293-
unless (← Meta.isDefEq pft expectedType) do
294-
return s!"Error: proof has type {pft} but goal has type {expectedType}"
295-
296-
let pf ← goalId.withContext $ abstractAllLambdaFVars pf
297-
let pft ← Meta.inferType pf >>= instantiateMVars
298-
299-
if pf.hasExprMVar then
300-
return "Error: contains metavariable(s)"
301-
302-
-- Find level parameters
303-
let usedLevels := collectLevelParams {} pft
304-
let usedLevels := collectLevelParams usedLevels pf
305-
306-
let decl := Declaration.defnDecl {
307-
name := Name.anonymous,
308-
type := pft,
309-
value := pf,
310-
levelParams := usedLevels.params.toList,
311-
hints := ReducibilityHints.opaque,
312-
safety := DefinitionSafety.safe
313-
}
314-
315-
try
316-
let _ ← addDecl decl
317-
return "OK"
318-
catch ex =>
319-
return s!"Error: kernel type check failed: {← ex.toMessageData.toString}"
320-
321-
if res != "OK" then
322-
allOk := false
323-
errorMsg := res
324-
break
325-
326-
return if allOk then "OK" else errorMsg
255+
match oldProofState? with
256+
| none => return "OK" -- Nothing to verify
257+
| some oldState => do
258+
let mut allOk := true
259+
let mut errorMsg := ""
260+
261+
for oldGoal in oldState.tacticState.goals do
262+
-- If the goal is still present in the new proofState, we don't need to verify it yet.
263+
if proofState.tacticState.goals.contains oldGoal then
264+
continue
265+
266+
let (res, _) ← proofState.runMetaM do
267+
-- Check if goal is assigned in new state
268+
match proofState.metaState.mctx.getExprAssignmentCore? oldGoal with
269+
| none => return s!"Goal {oldGoal.name} was not solved"
270+
| some pf => do
271+
let pf ← instantiateMVars pf
272+
273+
-- Check that all MVars in the proof are goals in new state
274+
-- let mvars ← Meta.getMVars pf
275+
let (_, mvars) ← ctx.runMetaM proofState.metaContext.lctx ((Meta.collectMVars pf).run {})
276+
IO.println s!"Goal {oldGoal.name} = {pf} ({mvars.result.map (·.name)})"
277+
for mvar in mvars.result do
278+
-- If the metavariable in the assignment is a new goal, it's fine.
279+
unless proofState.tacticState.goals.contains mvar do
280+
return s!"Goal {oldGoal.name} assignment contains metavariables"
281+
282+
-- Check that proof has expected type
283+
let pft ← Meta.inferType pf >>= instantiateMVars
284+
let expectedType ← Meta.inferType (mkMVar oldGoal) >>= instantiateMVars
285+
unless (← Meta.isDefEq pft expectedType) do
286+
return s!"Error: proof has type {pft} but goal has type {expectedType}"
287+
288+
let pf ← oldGoal.withContext $ abstractAllLambdaFVars pf
289+
let pft ← Meta.inferType pf >>= instantiateMVars
290+
291+
-- Find level parameters
292+
let usedLevels := collectLevelParams {} pft
293+
let usedLevels := collectLevelParams usedLevels pf
294+
295+
let decl := Declaration.defnDecl {
296+
name := Name.anonymous,
297+
type := pft,
298+
value := pf,
299+
levelParams := usedLevels.params.toList,
300+
hints := ReducibilityHints.opaque,
301+
safety := DefinitionSafety.safe
302+
}
303+
304+
try
305+
let _ ← addDecl decl
306+
return "OK"
307+
catch ex =>
308+
return s!"Error: kernel type check failed: {← ex.toMessageData.toString}"
309+
310+
if res != "OK" then
311+
allOk := false
312+
errorMsg := res
313+
break
314+
315+
return if allOk then "OK" else errorMsg
327316

328317
/-- Record a `ProofSnapshot` and generate a JSON response for it. -/
329318
def createProofStepReponse (proofState : ProofSnapshot) (old? : Option ProofSnapshot := none) :
@@ -356,7 +345,7 @@ def createProofStepReponse (proofState : ProofSnapshot) (old? : Option ProofSnap
356345
goalInfos
357346
mctxAfter := mctxAfterJson
358347
proofStatus := (← getProofStatus proofState)
359-
stepVerification := (← verifyGoalAssignment proofState old?) }
348+
stepVerification := (← verifyGoalAssignment ctx proofState old?) }
360349

361350
/-- Pickle a `CommandSnapshot`, generating a JSON response. -/
362351
def pickleCommandSnapshot (n : PickleEnvironment) : M m (CommandResponse ⊕ Error) := do

0 commit comments

Comments
 (0)