Skip to content

Commit f7eeee0

Browse files
Khawkrozowski
authored andcommitted
refactor: make syntax covering snapshot tasks more precise on the top level (leanprover#8744)
1 parent bad120b commit f7eeee0

File tree

7 files changed

+89
-43
lines changed

7 files changed

+89
-43
lines changed

src/Lean/Elab/Frontend.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ where
112112
|>.foldl (· ++ ·) {}
113113
-- In contrast to messages, we should collect info trees only from the top-level command
114114
-- snapshots as they subsume any info trees reported incrementally by their children.
115-
let trees := commands.map (·.infoTreeSnap.get.infoTree?) |>.filterMap id |>.toPArray'
115+
let trees := commands.map (·.elabSnap.infoTreeSnap.get.infoTree?) |>.filterMap id |>.toPArray'
116116
return {
117-
commandState := { snap.resultSnap.get.cmdState with messages, infoState.trees := trees }
117+
commandState := { snap.elabSnap.resultSnap.get.cmdState with messages, infoState.trees := trees }
118118
parserState := snap.parserState
119119
cmdPos := snap.parserState.pos
120120
commands := commands.map (·.stx)

src/Lean/Language/Basic.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ partial def SnapshotTask.cancelRec [ToSnapshotTree α] (t : SnapshotTask α) : B
218218

219219
/-- Snapshot type without child nodes. -/
220220
structure SnapshotLeaf extends Snapshot
221-
deriving Nonempty, TypeName
221+
deriving Inhabited, TypeName
222222

223223
instance : ToSnapshotTree SnapshotLeaf where
224224
toSnapshotTree s := SnapshotTree.mk s.toSnapshot #[]

src/Lean/Language/Lean.lean

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -392,21 +392,27 @@ where
392392
return {
393393
ictx
394394
stx := newStx
395-
diagnostics := old.diagnostics
395+
diagnostics := .empty
396+
metaSnap := .finished newStx {
397+
diagnostics := old.diagnostics
398+
}
396399
result? := some {
397400
parserState := newParserState
398-
processedSnap := (← oldSuccess.processedSnap.bindIO (stx? := newStx)
401+
processedSnap := (← oldSuccess.processedSnap.bindIO
399402
(cancelTk? := none) (reportingRange? := progressRange?) (sync := true) fun oldProcessed => do
400403
if let some oldProcSuccess := oldProcessed.result? then
401404
-- also wait on old command parse snapshot as parsing is cheap and may allow for
402405
-- elaboration reuse
403-
oldProcSuccess.firstCmdSnap.bindIO (sync := true) (stx? := newStx)
406+
oldProcSuccess.firstCmdSnap.bindIO (sync := true)
404407
(cancelTk? := none) (reportingRange? := progressRange?) fun oldCmd => do
405408
let prom ← IO.Promise.new
406409
let cancelTk ← IO.CancelToken.new
407410
parseCmd oldCmd newParserState oldProcSuccess.cmdState prom (sync := true) cancelTk ctx
408-
return .finished newStx {
409-
diagnostics := oldProcessed.diagnostics
411+
return .finished none {
412+
diagnostics := .empty
413+
metaSnap := .finished newStx {
414+
diagnostics := oldProcessed.diagnostics
415+
}
410416
result? := some {
411417
cmdState := oldProcSuccess.cmdState
412418
firstCmdSnap := { stx? := none, task := prom.result!, cancelTk? := cancelTk } } }
@@ -425,13 +431,16 @@ where
425431
-- ...go immediately to next snapshot
426432
return (← unchanged old old.stx oldSuccess.parserState)
427433

428-
withHeaderExceptions ({ · with ictx, stx := .missing, result? := none }) do
434+
withHeaderExceptions ({ · with ictx, stx := .missing, result? := none, metaSnap := default }) do
429435
-- parsing the header should be cheap enough to do synchronously
430436
let (stx, parserState, msgLog) ← Parser.parseHeader ictx
431437
if msgLog.hasErrors then
432438
return {
433439
ictx, stx
434-
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
440+
diagnostics := .empty
441+
metaSnap := .finished stx {
442+
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
443+
}
435444
result? := none
436445
}
437446

@@ -452,7 +461,10 @@ where
452461
old.result?.forM (·.processedSnap.cancelRec)
453462
return {
454463
ictx, stx
455-
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
464+
diagnostics := .empty
465+
metaSnap := .finished stx {
466+
diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
467+
}
456468
result? := some {
457469
parserState
458470
processedSnap := (← processHeader ⟨trimmedStx⟩ parserState)
@@ -462,9 +474,9 @@ where
462474
processHeader (stx : HeaderSyntax) (parserState : Parser.ModuleParserState) :
463475
LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do
464476
let ctx ← read
465-
SnapshotTask.ofIO stx none (some ⟨0, ctx.input.endPos⟩) <|
477+
SnapshotTask.ofIO none none (some ⟨0, ctx.input.endPos⟩) <|
466478
ReaderT.run (r := ctx) <| -- re-enter reader in new task
467-
withHeaderExceptions (α := HeaderProcessedSnapshot) ({ · with result? := none }) do
479+
withHeaderExceptions (α := HeaderProcessedSnapshot) ({ · with result? := none, metaSnap := default }) do
468480
let setup ← match (← setupImports stx) with
469481
| .ok setup => pure setup
470482
| .error snap => return snap
@@ -483,7 +495,7 @@ where
483495
let stopTime := (← IO.monoNanosNow).toFloat / 1000000000
484496
let diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog)
485497
if msgLog.hasErrors then
486-
return { diagnostics, result? := none }
498+
return { diagnostics, result? := none, metaSnap := default }
487499

488500
let mut traceState := default
489501
if trace.profiler.output.get? setup.opts |>.isSome then
@@ -521,8 +533,11 @@ where
521533
let cancelTk ← IO.CancelToken.new
522534
parseCmd none parserState cmdState prom (sync := true) cancelTk ctx
523535
return {
524-
diagnostics
525-
infoTree? := cmdState.infoState.trees[0]!
536+
diagnostics := .empty
537+
metaSnap := .finished stx {
538+
diagnostics
539+
infoTree? := cmdState.infoState.trees[0]!
540+
}
526541
result? := some {
527542
cmdState
528543
firstCmdSnap := { stx? := none, task := prom.result!, cancelTk? := cancelTk }
@@ -543,7 +558,7 @@ where
543558
let newProm ← IO.Promise.new
544559
let cancelTk ← IO.CancelToken.new
545560
-- can reuse range, syntax unchanged
546-
BaseIO.chainTask (sync := true) old.resultSnap.task fun oldResult =>
561+
BaseIO.chainTask (sync := true) old.elabSnap.resultSnap.task fun oldResult =>
547562
-- also wait on old command parse snapshot as parsing is cheap and may allow for
548563
-- elaboration reuse
549564
BaseIO.chainTask (sync := true) oldNext.task fun oldNext => do
@@ -601,10 +616,13 @@ where
601616
-- is not `Inhabited`
602617
prom.resolve <| {
603618
diagnostics := .empty, stx := .missing, parserState
604-
elabSnap := default
605-
resultSnap := .finished none { diagnostics := .empty, cmdState }
606-
infoTreeSnap := .finished none { diagnostics := .empty }
607-
reportSnap := default
619+
elabSnap := {
620+
diagnostics := .empty
621+
elabSnap := default
622+
resultSnap := .finished none { diagnostics := .empty, cmdState }
623+
infoTreeSnap := .finished none { diagnostics := .empty }
624+
reportSnap := default
625+
}
608626
nextCmdSnap? := none
609627
}
610628
return
@@ -641,13 +659,16 @@ where
641659
prom.resolve {
642660
diagnostics, nextCmdSnap?
643661
stx := stx', parserState := parserState'
644-
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some elabCmdCancelTk }
645-
resultSnap := { stx? := stx', reportingRange? := initRange?, task := resultPromise.result!, cancelTk? := none }
646-
infoTreeSnap := { stx? := stx', reportingRange? := initRange?, task := finishedPromise.result!, cancelTk? := none }
647-
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result!, cancelTk? := none }
662+
elabSnap := {
663+
diagnostics := .empty
664+
elabSnap := { stx? := stx', task := elabPromise.result!, cancelTk? := some elabCmdCancelTk }
665+
resultSnap := { stx? := stx', reportingRange? := initRange?, task := resultPromise.result!, cancelTk? := none }
666+
infoTreeSnap := { stx? := stx', reportingRange? := initRange?, task := finishedPromise.result!, cancelTk? := none }
667+
reportSnap := { stx? := none, reportingRange? := initRange?, task := reportPromise.result!, cancelTk? := none }
668+
}
648669
}
649670
let cmdState ← doElab stx cmdState beginPos
650-
{ old? := old?.map fun old => ⟨old.stx, old.elabSnap⟩, new := elabPromise }
671+
{ old? := old?.map fun old => ⟨old.stx, old.elabSnap.elabSnap⟩, new := elabPromise }
651672
elabCmdCancelTk ctx
652673

653674
let mut reportedCmdState := cmdState
@@ -777,6 +798,6 @@ where goCmd snap :=
777798
if let some next := snap.nextCmdSnap? then
778799
goCmd next.get
779800
else
780-
snap.resultSnap.get.cmdState
801+
snap.elabSnap.resultSnap.get.cmdState
781802

782803
end Lean

src/Lean/Language/Lean/Types.lean

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,11 @@ deriving Nonempty
3636
instance : ToSnapshotTree CommandResultSnapshot where
3737
toSnapshotTree s := ⟨s.toSnapshot, #[]⟩
3838

39-
/-- State after a command has been parsed. -/
40-
structure CommandParsedSnapshot extends Snapshot where
41-
/-- Syntax tree of the command. -/
42-
stx : Syntax
43-
/-- Resulting parser state. -/
44-
parserState : Parser.ModuleParserState
39+
/--
40+
State before a command is elaborated. This is separate from `CommandParsedSnapshot` so that all
41+
snapshots belonging to a command are grouped below a task with the command's syntax tree.
42+
-/
43+
structure CommandElaboratingSnapshot extends Snapshot where
4544
/--
4645
Snapshot for incremental reporting and reuse during elaboration, type dependent on specific
4746
elaborator.
@@ -55,16 +54,30 @@ structure CommandParsedSnapshot extends Snapshot where
5554
infoTreeSnap : SnapshotTask SnapshotLeaf
5655
/-- Additional, untyped snapshots used for reporting, not reuse. -/
5756
reportSnap : SnapshotTask SnapshotTree
58-
/-- Next command, unless this is a terminal command. -/
59-
nextCmdSnap? : Option (SnapshotTask CommandParsedSnapshot)
6057
deriving Nonempty
61-
partial instance : ToSnapshotTree CommandParsedSnapshot where
58+
instance : ToSnapshotTree CommandElaboratingSnapshot where
6259
toSnapshotTree := go where
6360
go s := ⟨s.toSnapshot,
6461
#[s.elabSnap.map (sync := true) toSnapshotTree,
6562
s.resultSnap.map (sync := true) toSnapshotTree,
6663
s.infoTreeSnap.map (sync := true) toSnapshotTree,
67-
s.reportSnap] |>
64+
s.reportSnap]⟩
65+
66+
/-- State after a command has been parsed. -/
67+
structure CommandParsedSnapshot extends Snapshot where
68+
/-- Syntax tree of the command. -/
69+
stx : Syntax
70+
/-- Resulting parser state. -/
71+
parserState : Parser.ModuleParserState
72+
/-- State before the command is elaborated. This snapshot is always fulfilled immediately. -/
73+
elabSnap : CommandElaboratingSnapshot
74+
/-- Next command, unless this is a terminal command. -/
75+
nextCmdSnap? : Option (SnapshotTask CommandParsedSnapshot)
76+
deriving Nonempty
77+
partial instance : ToSnapshotTree CommandParsedSnapshot where
78+
toSnapshotTree := go where
79+
go s := ⟨s.toSnapshot,
80+
#[.finished s.stx (toSnapshotTree s.elabSnap)] |>
6881
pushOpt (s.nextCmdSnap?.map (·.map (sync := true) go))⟩
6982

7083
/-- State after successful importing. -/
@@ -76,11 +89,16 @@ structure HeaderProcessedState where
7689

7790
/-- State after the module header has been processed including imports. -/
7891
structure HeaderProcessedSnapshot extends Snapshot where
92+
/--
93+
Holds produced diagnostics and info tree. Separate snapshot so that it can be tagged with the
94+
header syntax, which should not be done for this snapshot containing `firstCmdSnap`.
95+
-/
96+
metaSnap : SnapshotTask SnapshotLeaf
7997
/-- State after successful importing. -/
8098
result? : Option HeaderProcessedState
8199
isFatal := result?.isNone
82100
instance : ToSnapshotTree HeaderProcessedSnapshot where
83-
toSnapshotTree s := ⟨s.toSnapshot, #[] |>
101+
toSnapshotTree s := ⟨s.toSnapshot, #[s.metaSnap.map (sync := true) toSnapshotTree] |>
84102
pushOpt (s.result?.map (·.firstCmdSnap.map (sync := true) toSnapshotTree))⟩
85103

86104
/-- State after successfully parsing the module header. -/
@@ -92,6 +110,11 @@ structure HeaderParsedState where
92110

93111
/-- State after the module header has been parsed. -/
94112
structure HeaderParsedSnapshot extends Snapshot where
113+
/--
114+
Holds produced diagnostics. Separate snapshot so that it can be tagged with the header syntax,
115+
which should not be done for this snapshot containing `firstCmdSnap`.
116+
-/
117+
metaSnap : SnapshotTask SnapshotLeaf
95118
/-- Parser input context supplied by the driver, stored here for incremental parsing. -/
96119
ictx : Parser.InputContext
97120
/-- Resulting syntax tree. -/
@@ -101,8 +124,8 @@ structure HeaderParsedSnapshot extends Snapshot where
101124
isFatal := result?.isNone
102125

103126
instance : ToSnapshotTree HeaderParsedSnapshot where
104-
toSnapshotTree s := ⟨s.toSnapshot,
105-
#[] |> pushOpt (s.result?.map (·.processedSnap.map (sync := true) toSnapshotTree))⟩
127+
toSnapshotTree s := ⟨s.toSnapshot, #[s.metaSnap.map (sync := true) toSnapshotTree] |>
128+
pushOpt (s.result?.map (·.processedSnap.map (sync := true) toSnapshotTree))⟩
106129

107130
/-- Shortcut accessor to the final header state, if successful. -/
108131
def HeaderParsedSnapshot.processedResult (snap : HeaderParsedSnapshot) :

src/Lean/Server/FileWorker.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ def setupImports
382382
unless (← IO.checkCanceled) do
383383
IO.Process.exit 2 -- signal restart request to watchdog
384384
-- should not be visible to user as task is already canceled
385-
return .error { diagnostics := .empty, result? := none }
385+
return .error { diagnostics := .empty, result? := none, metaSnap := default }
386386

387387
chanOut.sync.send <| mkInitialIleanInfoUpdateNotification doc <| collectImports stx
388388
let imports := Elab.headerToImports stx
@@ -404,11 +404,13 @@ def setupImports
404404
"Imports are out of date and must be rebuilt; \
405405
use the \"Restart File\" command in your editor.")
406406
result? := none
407+
metaSnap := default
407408
}
408409
| .error msg =>
409410
return .error {
410411
diagnostics := (← diagnosticsOfHeaderError msg)
411412
result? := none
413+
metaSnap := default
412414
}
413415
| _ => pure ()
414416

src/Lean/Server/FileWorker/Utils.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private partial def mkCmdSnaps (initSnap : Language.Lean.InitialSnapshot) :
2828
} <| .delayed <| headerSuccess.firstCmdSnap.task.asServerTask.bindCheap go
2929
where
3030
go cmdParsed :=
31-
cmdParsed.resultSnap.task.asServerTask.mapCheap fun result =>
31+
cmdParsed.elabSnap.resultSnap.task.asServerTask.mapCheap fun result =>
3232
.ok <| .cons {
3333
stx := cmdParsed.stx
3434
mpState := cmdParsed.parserState

src/Lean/Server/Requests.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def findCmdDataAtPos
396396
findCmdParsedSnap doc hoverPos |>.bindCheap fun
397397
| some cmdParsed => toSnapshotTree cmdParsed |>.findInfoTreeAtPos doc.meta.text hoverPos includeStop |>.bindCheap fun
398398
| some infoTree => .pure <| some (cmdParsed.stx, infoTree)
399-
| none => cmdParsed.infoTreeSnap.task.asServerTask.mapCheap fun s =>
399+
| none => cmdParsed.elabSnap.infoTreeSnap.task.asServerTask.mapCheap fun s =>
400400
assert! s.infoTree?.isSome
401401
some (cmdParsed.stx, s.infoTree?.get!)
402402
| none => .pure none

0 commit comments

Comments
 (0)