Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: snapshot trees and language processors #3014

Merged
merged 69 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
ceeaff7
feat: start integrating new snapshot types into cmdline driver
Kha Oct 12, 2023
d0fd3d7
feat: switch `Language` to bundled, integrate into cmdline driver in …
Kha Oct 13, 2023
152d594
fix: do not duplicate output
Kha Oct 13, 2023
a68d1c6
fix: more output fixes
Kha Oct 19, 2023
f2f6016
foo
Kha Nov 3, 2023
986d1c1
fix: `run_task`/`deactivate_task` race condition on `m_imp->m_closure`
Kha Nov 9, 2023
d2bf996
fix: re-read HTTP header when skipping notification in `Ipc.readRespo…
Kha Nov 10, 2023
23da22e
fix: debounce server status reporting
Kha Nov 10, 2023
7932c0d
fix: create snapshot for header in legacy adapter
Kha Nov 11, 2023
00e976c
fix: memoize `cmdSnaps` legacy adapter so `getFinishedPrefix` works a…
Kha Nov 11, 2023
7c0ad9c
fix: `waitForDiagnostics` must wait on reporter task
Kha Nov 11, 2023
89383cf
doc: Language
Kha Nov 13, 2023
58c19d7
chore: rely on auto cancellation of reporter
Kha Nov 25, 2023
22aa0dc
doc: more
Kha Nov 27, 2023
1d547d3
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Nov 29, 2023
0ce8fee
import at most once per worker process
Kha Nov 29, 2023
4c9b70b
fix test outputs
Kha Nov 29, 2023
0282d1c
File worker: synchronize output messages
Kha Nov 30, 2023
8fff2e2
fix: incremental parsing
Kha Dec 1, 2023
ca75ce2
polish Lean.Language.Lean
Kha Dec 1, 2023
44cb1cc
better naming
Kha Dec 2, 2023
057184b
feat: synchronous execution of task continuations
Kha Dec 2, 2023
0688e22
synchronous fast forwarding in Lean processor
Kha Dec 2, 2023
d0e9409
Bring back trust level
Kha Dec 2, 2023
85a151b
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Dec 2, 2023
3930e40
Try a shortcut
Kha Dec 2, 2023
3b14598
Remove redundant fields
Kha Dec 2, 2023
daf6fde
doc
Kha Dec 2, 2023
c6dd2b1
code owner
Kha Dec 2, 2023
f89de7f
doc
Kha Dec 2, 2023
edefad1
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Dec 8, 2023
5550623
Fix explicit cancellation
Kha Dec 12, 2023
91e7e28
Adjust comments
Kha Dec 13, 2023
33a9647
Cache interactive diagnostics properly
Kha Dec 13, 2023
63a853e
fix: clear progress notifications from `lake` in the end
Kha Dec 13, 2023
9c49e49
fix: header errors should be fatal
Kha Dec 14, 2023
67c0599
Header diagnostics should cover the full file
Kha Dec 14, 2023
e0e7faf
server reporting delay
Kha Dec 14, 2023
cef0d59
fix: header errors should be fatal
Kha Dec 14, 2023
5122ee7
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Dec 14, 2023
dc97d75
fix nondeterministic test output from new debouncing
Kha Dec 14, 2023
893ea99
trigger CI
Kha Dec 14, 2023
078cef0
trigger CI again
Kha Dec 15, 2023
561f260
remove unused Option.forM, already in std4
Kha Dec 15, 2023
6ec85ec
perf: keep alive old single-threaded cmdline driver for a little longer
Kha Dec 18, 2023
b50c9e2
fix: don't report the same info tree twice
Kha Dec 21, 2023
0480045
refactor: previous commit
Kha Dec 21, 2023
29b6612
fix: report all info trees at `ileanInfoFinal`
Kha Dec 21, 2023
5de7e2c
refactor: move interactive diagnostics caching from `Snapshot` to fil…
Kha Dec 23, 2023
8b96499
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Jan 25, 2024
8bd6612
clean up Lean.Server changes
Kha Feb 1, 2024
a0a65b6
refactor: remove unique ID indirection for interactive diagnostics ca…
Kha Feb 13, 2024
25886d2
Merge branch 'master' of https://github.com/leanprover/lean4 into het…
Kha Feb 20, 2024
58d5b58
Remove `Language` interface for now, to be refined in `hash-lang`
Kha Feb 20, 2024
4665f41
Move `setup-file` logic back from language processor into file worker
Kha Feb 21, 2024
8830667
Run `lake setup-file` at most once per worker process, like before
Kha Feb 22, 2024
91a0fee
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Feb 29, 2024
2c99cf5
Fix cancellation in reportSnapshots
Kha Feb 29, 2024
44e5163
refactor: lazy initialization of request caches
Kha Mar 1, 2024
ad6b225
fix: traces on partial syntax
Kha Mar 1, 2024
2745d55
address some comments
Kha Mar 6, 2024
491b125
remove ElabTaskError.aborted
Kha Mar 7, 2024
c64fe35
address more comments
Kha Mar 7, 2024
34ff994
address more comments
Kha Mar 7, 2024
27c90ba
address even more comments
Kha Mar 7, 2024
959fa55
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Mar 7, 2024
f7db589
Replace silly critical section code with `modifyGet`
Kha Mar 8, 2024
c26033c
Address review comments
Kha Mar 8, 2024
723910a
Merge remote-tracking branch 'upstream/master' into het-snaps
Kha Mar 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/src/Lean/Data/Lsp/ @mhuisi
/src/Lean/Elab/Deriving/ @semorrison
/src/Lean/Elab/Tactic/ @semorrison
/src/Lean/Language/ @Kha
Kha marked this conversation as resolved.
Show resolved Hide resolved
/src/Lean/Meta/Tactic/ @leodemoura
/src/Lean/Parser/ @Kha
/src/Lean/PrettyPrinter/ @Kha
Expand Down
2 changes: 1 addition & 1 deletion src/Init/Data/Channel.lean
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Sends a message on an `Channel`.

This function does not block.
-/
def Channel.send (v : α) (ch : Channel α) : BaseIO Unit :=
def Channel.send (ch : Channel α) (v : α) : BaseIO Unit :=
ch.atomically do
let st ← get
if st.closed then return
Expand Down
7 changes: 7 additions & 0 deletions src/Init/Data/Option/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ def toMonad [Monad m] [Alternative m] : Option α → m α
| none, _ => none
| some a, b => b a

/-- Runs `f` on `o`'s value, if any, and returns its result, or else returns `none`. -/
@[inline] protected def bindM [Monad m] (f : α → m (Option β)) (o : Option α) : m (Option β) := do
Kha marked this conversation as resolved.
Show resolved Hide resolved
if let some a := o then
return (← f a)
else
return none

@[inline] protected def mapM [Monad m] (f : α → m β) (o : Option α) : m (Option β) := do
if let some a := o then
return some (← f a)
Expand Down
3 changes: 3 additions & 0 deletions src/Lean/Data/JsonRpc.lean
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ structure ResponseError (α : Type u) where
instance [ToJson α] : CoeOut (ResponseError α) Message :=
⟨fun r => Message.responseError r.id r.code r.message (r.data?.map toJson)⟩

instance : CoeOut (ResponseError Unit) Message :=
⟨fun r => Message.responseError r.id r.code r.message none⟩

instance : Coe String RequestID := ⟨RequestID.str⟩
instance : Coe JsonNumber RequestID := ⟨RequestID.num⟩

Expand Down
1 change: 1 addition & 0 deletions src/Lean/Data/Lsp/Internal.lean
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ structure RefInfo.Location where
range : Lsp.Range
/-- Parent declaration of the reference. `none` if the reference is itself a declaration. -/
parentDecl? : Option RefInfo.ParentDecl
deriving Inhabited

/-- Definition site and usage sites of a reference. Obtained from `Lean.Server.RefInfo`. -/
structure RefInfo where
Expand Down
20 changes: 13 additions & 7 deletions src/Lean/Data/Lsp/Ipc.lean
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,32 @@ partial def readResponseAs (expectedID : RequestID) (α) [FromJson α] :
def waitForExit : IpcM UInt32 := do
(←read).wait

/-- Waits for the worker to emit all diagnostics for the current document version
and returns them as a list. -/
/--
Waits for the worker to emit all diagnostic notifications for the current document version and
returns the last notification, if any.

We used to return all notifications but with debouncing in the server, this would not be
deterministic anymore as what messages are dropped depends on wall-clock timing.
-/
partial def collectDiagnostics (waitForDiagnosticsId : RequestID := 0) (target : DocumentUri) (version : Nat)
: IpcM (List (Notification PublishDiagnosticsParams)) := do
: IpcM (Option (Notification PublishDiagnosticsParams)) := do
writeRequest ⟨waitForDiagnosticsId, "textDocument/waitForDiagnostics", WaitForDiagnosticsParams.mk target version⟩
let rec loop : IpcM (List (Notification PublishDiagnosticsParams)) := do
loop
where
loop := do
match (←readMessage) with
| Message.response id _ =>
if id == waitForDiagnosticsId then return []
if id == waitForDiagnosticsId then return none
else loop
| Message.responseError id _ msg _ =>
if id == waitForDiagnosticsId then
throw $ userError s!"Waiting for diagnostics failed: {msg}"
else loop
| Message.notification "textDocument/publishDiagnostics" (some param) =>
match fromJson? (toJson param) with
| Except.ok diagnosticParam => return ⟨"textDocument/publishDiagnostics", diagnosticParam⟩ :: (←loop)
| Except.ok diagnosticParam => return (← loop).getD ⟨"textDocument/publishDiagnostics", diagnosticParam⟩
| Except.error inner => throw $ userError s!"Cannot decode publishDiagnostics parameters\n{inner}"
| _ => loop
loop

def runWith (lean : System.FilePath) (args : Array String := #[]) (test : IpcM α) : IO α := do
let proc ← Process.spawn {
Expand Down
4 changes: 2 additions & 2 deletions src/Lean/DeclarationRange.lean
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ def addDeclarationRanges [MonadEnv m] (declName : Name) (declRanges : Declaratio
def findDeclarationRangesCore? [Monad m] [MonadEnv m] (declName : Name) : m (Option DeclarationRanges) :=
return declRangeExt.find? (← getEnv) declName

def findDeclarationRanges? [Monad m] [MonadEnv m] [MonadLiftT IO m] (declName : Name) : m (Option DeclarationRanges) := do
def findDeclarationRanges? [Monad m] [MonadEnv m] [MonadLiftT BaseIO m] (declName : Name) : m (Option DeclarationRanges) := do
let env ← getEnv
let ranges ← if isAuxRecursor env declName || isNoConfusion env declName || (← isRec declName) then
findDeclarationRangesCore? declName.getPrefix
else
findDeclarationRangesCore? declName
match ranges with
| none => return (← builtinDeclRanges.get (m := IO)).find? declName
| none => return (← builtinDeclRanges.get (m := BaseIO)).find? declName
| some _ => return ranges

end Lean
13 changes: 2 additions & 11 deletions src/Lean/Elab/Command.lean
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ private def addTraceAsMessagesCore (ctx : Context) (log : MessageLog) (traceStat
let mut log := log
let traces' := traces.toArray.qsort fun ((a, _), _) ((b, _), _) => a < b
for ((pos, endPos), traceMsg) in traces' do
log := log.add <| mkMessageCore ctx.fileName ctx.fileMap (.joinSep traceMsg.toList "\n") .information pos endPos
let data := .tagged `_traceMsg <| .joinSep traceMsg.toList "\n"
log := log.add <| mkMessageCore ctx.fileName ctx.fileMap data .information pos endPos
return log

private def addTraceAsMessages : CommandElabM Unit := do
Expand Down Expand Up @@ -268,11 +269,6 @@ instance : MonadRecDepth CommandElabM where
getRecDepth := return (← read).currRecDepth
getMaxRecDepth := return (← get).maxRecDepth

register_builtin_option showPartialSyntaxErrors : Bool := {
defValue := false
descr := "show elaboration errors from partial syntax trees (i.e. after parser recovery)"
}

builtin_initialize registerTraceClass `Elab.command

partial def elabCommand (stx : Syntax) : CommandElabM Unit := do
Expand Down Expand Up @@ -321,11 +317,6 @@ def elabCommandTopLevel (stx : Syntax) : CommandElabM Unit := withRef stx do pro
-- note the order: first process current messages & info trees, then add back old messages & trees,
-- then convert new traces to messages
let mut msgs := (← get).messages
-- `stx.hasMissing` should imply `initMsgs.hasErrors`, but the latter should be cheaper to check in general
if !showPartialSyntaxErrors.get (← getOptions) && initMsgs.hasErrors && stx.hasMissing then
-- discard elaboration errors, except for a few important and unlikely misleading ones, on parse error
msgs := ⟨msgs.msgs.filter fun msg =>
msg.data.hasTag (fun tag => tag == `Elab.synthPlaceholder || tag == `Tactic.unsolvedGoals || (`_traceMsg).isSuffixOf tag)⟩
for tree in (← getInfoTrees) do
trace[Elab.info] (← tree.format)
modify fun st => { st with
Expand Down
75 changes: 53 additions & 22 deletions src/Lean/Elab/Frontend.lean
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Lean.Elab.Import
import Lean.Elab.Command
import Lean.Language.Lean
import Lean.Util.Profile
import Lean.Server.References

Expand Down Expand Up @@ -40,7 +39,19 @@ def setCommandState (commandState : Command.State) : FrontendM Unit :=

def elabCommandAtFrontend (stx : Syntax) : FrontendM Unit := do
runCommandElabM do
let initMsgs ← modifyGet fun st => (st.messages, { st with messages := {} })
Command.elabCommandTopLevel stx
let mut msgs := (← get).messages
-- `stx.hasMissing` should imply `initMsgs.hasErrors`, but the latter should be cheaper to check
-- in general
if !Language.Lean.showPartialSyntaxErrors.get (← getOptions) && initMsgs.hasErrors &&
stx.hasMissing then
-- discard elaboration errors, except for a few important and unlikely misleading ones, on
-- parse error
msgs := ⟨msgs.msgs.filter fun msg =>
msg.data.hasTag (fun tag => tag == `Elab.synthPlaceholder ||
tag == `Tactic.unsolvedGoals || (`_traceMsg).isSuffixOf tag)⟩
modify ({ · with messages := initMsgs ++ msgs })

def updateCmdPos : FrontendM Unit := do
modify fun s => { s with cmdPos := s.parserState.pos }
Expand Down Expand Up @@ -86,12 +97,8 @@ def process (input : String) (env : Environment) (opts : Options) (fileName : Op
pure (s.commandState.env, s.commandState.messages)

builtin_initialize
registerOption `printMessageEndPos { defValue := false, descr := "print end position of each message in addition to start position" }
registerTraceClass `Elab.info

def getPrintMessageEndPos (opts : Options) : Bool :=
opts.getBool `printMessageEndPos false

@[export lean_run_frontend]
def runFrontend
(input : String)
Expand All @@ -102,26 +109,50 @@ def runFrontend
(ileanFileName? : Option String := none)
: IO (Environment × Bool) := do
let inputCtx := Parser.mkInputContext input fileName
let (header, parserState, messages) ← Parser.parseHeader inputCtx
-- allow `env` to be leaked, which would live until the end of the process anyway
let (env, messages) ← processHeader (leakEnv := true) header opts messages inputCtx trustLevel
let env := env.setMainModule mainModuleName
let mut commandState := Command.mkState env messages opts

if ileanFileName?.isSome then
-- Collect InfoTrees so we can later extract and export their info to the ilean file
commandState := { commandState with infoState.enabled := true }

let s ← IO.processCommands inputCtx parserState commandState
for msg in s.commandState.messages.toList do
IO.print (← msg.toString (includeEndPos := getPrintMessageEndPos opts))

-- TODO: replace with `#lang` processing
if /- Lean #lang? -/ true then
-- Temporarily keep alive old cmdline driver for the Lean language so that we don't pay the
-- overhead of passing the environment between snapshots until we actually make good use of it
-- outside the server
let (header, parserState, messages) ← Parser.parseHeader inputCtx
-- allow `env` to be leaked, which would live until the end of the process anyway
let (env, messages) ← processHeader (leakEnv := true) header opts messages inputCtx trustLevel
let env := env.setMainModule mainModuleName
let mut commandState := Command.mkState env messages opts

if ileanFileName?.isSome then
-- Collect InfoTrees so we can later extract and export their info to the ilean file
commandState := { commandState with infoState.enabled := true }

let s ← IO.processCommands inputCtx parserState commandState
for msg in s.commandState.messages.toList do
IO.print (← msg.toString (includeEndPos := Language.printMessageEndPos.get opts))

if let some ileanFileName := ileanFileName? then
let trees := s.commandState.infoState.trees.toArray
let references ←
Lean.Server.findModuleRefs inputCtx.fileMap trees (localVars := false) |>.toLspModuleRefs
let ilean := { module := mainModuleName, references : Lean.Server.Ilean }
IO.FS.writeFile ileanFileName $ Json.compress $ toJson ilean

return (s.commandState.env, !s.commandState.messages.hasErrors)

let ctx := { inputCtx with mainModuleName, opts, trustLevel }
let processor := Language.Lean.process
let snap ← processor none ctx
let snaps := Language.toSnapshotTree snap
snaps.runAndReport opts
if let some ileanFileName := ileanFileName? then
let trees := s.commandState.infoState.trees.toArray
let trees := snaps.getAll.concatMap (match ·.infoTree? with | some t => #[t] | _ => #[])
let references := Lean.Server.findModuleRefs inputCtx.fileMap trees (localVars := false)
let ilean := { module := mainModuleName, references := ← references.toLspModuleRefs : Lean.Server.Ilean }
IO.FS.writeFile ileanFileName $ Json.compress $ toJson ilean

pure (s.commandState.env, !s.commandState.messages.hasErrors)
let hasErrors := snaps.getAll.any (·.diagnostics.msgLog.hasErrors)
-- TODO: remove default when reworking cmdline interface in Lean; currently the only case
-- where we use the environment despite errors in the file is `--stats`
let env := Language.Lean.waitForFinalEnv? snap |>.getD (← mkEmptyEnvironment)
pure (env, !hasErrors)


end Lean.Elab
10 changes: 6 additions & 4 deletions src/Lean/Elab/InfoTree/Main.lean
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,18 @@ partial def InfoTree.substitute (tree : InfoTree) (assignment : PersistentHashMa
| none => hole id
| some tree => substitute tree assignment

def ContextInfo.runMetaM (info : ContextInfo) (lctx : LocalContext) (x : MetaM α) : IO α := do
let x := x.run { lctx := lctx } { mctx := info.mctx }
/-- Embeds a `CoreM` action in `IO` by supplying the information stored in `info`. -/
def ContextInfo.runCoreM (info : ContextInfo) (x : CoreM α) : IO α := do
/-
We must execute `x` using the `ngen` stored in `info`. Otherwise, we may create `MVarId`s and `FVarId`s that
have been used in `lctx` and `info.mctx`.
-/
let ((a, _), _) ←
(·.1) <$>
x.toIO { options := info.options, currNamespace := info.currNamespace, openDecls := info.openDecls, fileName := "<InfoTree>", fileMap := default }
{ env := info.env, ngen := info.ngen }
return a

def ContextInfo.runMetaM (info : ContextInfo) (lctx : LocalContext) (x : MetaM α) : IO α := do
(·.1) <$> info.runCoreM (x.run { lctx := lctx } { mctx := info.mctx })
Kha marked this conversation as resolved.
Show resolved Hide resolved

def ContextInfo.toPPContext (info : ContextInfo) (lctx : LocalContext) : PPContext :=
{ env := info.env, mctx := info.mctx, lctx := lctx,
Expand Down
Loading
Loading