diff --git a/src/Lean/Elab/Frontend.lean b/src/Lean/Elab/Frontend.lean index 8f29ee4d5c..a15d6a0c43 100644 --- a/src/Lean/Elab/Frontend.lean +++ b/src/Lean/Elab/Frontend.lean @@ -81,10 +81,6 @@ end Frontend open Frontend -def IO.processCommands (inputCtx : Parser.InputContext) (parserState : Parser.ModuleParserState) (commandState : Command.State) : IO State := do - let (_, s) ← (Frontend.processCommands.run { inputCtx := inputCtx }).run { commandState := commandState, parserState := parserState, cmdPos := parserState.pos } - pure s - structure IncrementalState extends State where inputCtx : Parser.InputContext initialSnap : Language.Lean.CommandParsedSnapshot @@ -92,12 +88,10 @@ deriving Nonempty open Language in /-- -Variant of `IO.processCommands` that uses the new Lean language processor implementation for -potential incremental reuse. Pass in result of a previous invocation done with the same state -(but usually different input context) to allow for reuse. +Variant of `IO.processCommands` that allows for potential incremental reuse. Pass in the result of a +previous invocation done with the same state (but usually different input context) to allow for +reuse. -/ --- `IO.processCommands` can be reimplemented on top of this as soon as the additional tasks speed up --- things instead of slowing them down partial def IO.processCommandsIncrementally (inputCtx : Parser.InputContext) (parserState : Parser.ModuleParserState) (commandState : Command.State) (old? : Option IncrementalState) : @@ -110,7 +104,7 @@ where let snap := t.get let commands := commands.push snap.data.stx if let some next := snap.nextCmdSnap? then - go initialSnap next commands + go initialSnap next.task commands else -- Opting into reuse also enables incremental reporting, so make sure to collect messages from -- all snapshots @@ -126,53 +120,17 @@ where inputCtx, initialSnap, commands } +def IO.processCommands (inputCtx : Parser.InputContext) (parserState : Parser.ModuleParserState) + (commandState : Command.State) : IO State := do + let st ← IO.processCommandsIncrementally inputCtx parserState commandState none + return st.toState + def process (input : String) (env : Environment) (opts : Options) (fileName : Option String := none) : IO (Environment × MessageLog) := do let fileName := fileName.getD "" let inputCtx := Parser.mkInputContext input fileName let s ← IO.processCommands inputCtx { : Parser.ModuleParserState } (Command.mkState env {} opts) pure (s.commandState.env, s.commandState.messages) -/-- -Parses values of options registered during import and left by the C++ frontend as strings, fails if -any option names remain unknown. --/ -def reparseOptions (opts : Options) : IO Options := do - let mut opts := opts - let decls ← getOptionDecls - for (name, val) in opts do - let .ofString val := val - | continue -- Already parsed by C++ - -- Options can be prefixed with `weak` in order to turn off the error when the option is not - -- defined - let weak := name.getRoot == `weak - if weak then - opts := opts.erase name - let name := name.replacePrefix `weak Name.anonymous - let some decl := decls.find? name - | unless weak do - throw <| .userError s!"invalid -D parameter, unknown configuration option '{name}' - -If the option is defined in this library, use '-D{`weak ++ name}' to set it conditionally" - - match decl.defValue with - | .ofBool _ => - match val with - | "true" => opts := opts.insert name true - | "false" => opts := opts.insert name false - | _ => - throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \ - it must be true/false" - | .ofNat _ => - let some val := val.toNat? - | throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \ - it must be a natural number" - opts := opts.insert name val - | .ofString _ => opts := opts.insert name val - | _ => throw <| .userError s!"invalid -D parameter, configuration option '{name}' \ - cannot be set in the command line, use set_option command" - - return opts - @[export lean_run_frontend] def runFrontend (input : String) @@ -185,64 +143,31 @@ def runFrontend : IO (Environment × Bool) := do let startTime := (← IO.monoNanosNow).toFloat / 1000000000 let inputCtx := Parser.mkInputContext input fileName - if 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 - -- now that imports have been loaded, check options again - let opts ← reparseOptions opts - let env := env.setMainModule mainModuleName - let mut commandState := Command.mkState env messages opts - let elabStartTime := (← IO.monoNanosNow).toFloat / 1000000000 - - 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 - Language.reportMessages s.commandState.messages opts jsonOutput - - 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 - - if let some out := trace.profiler.output.get? opts then - let traceState := s.commandState.traceState - -- importing does not happen in an elaboration monad, add now - let traceState := { traceState with - traces := #[{ - ref := .missing, - msg := .trace { cls := `Import, startTime, stopTime := elabStartTime } - (.ofFormat "importing") #[] - }].toPArray' ++ traceState.traces - } - let profile ← Firefox.Profile.export mainModuleName.toString startTime traceState opts - IO.FS.writeFile ⟨out⟩ <| Json.compress <| toJson profile - - return (s.commandState.env, !s.commandState.messages.hasErrors) - + let opts := Language.Lean.internal.minimalSnapshots.set opts true let ctx := { inputCtx with } let processor := Language.Lean.process let snap ← processor (fun _ => pure <| .ok { mainModuleName, opts, trustLevel }) none ctx let snaps := Language.toSnapshotTree snap snaps.runAndReport opts jsonOutput + if let some ileanFileName := ileanFileName? then 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 - 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) + let some cmdState := Language.Lean.waitForFinalCmdState? snap + | return (← mkEmptyEnvironment, false) + + if let some out := trace.profiler.output.get? opts then + let traceState := cmdState.traceState + let profile ← Firefox.Profile.export mainModuleName.toString startTime traceState opts + IO.FS.writeFile ⟨out⟩ <| Json.compress <| toJson profile + + let hasErrors := snaps.getAll.any (·.diagnostics.msgLog.hasErrors) + pure (cmdState.env, !hasErrors) end Lean.Elab diff --git a/src/Lean/Language/Lean.lean b/src/Lean/Language/Lean.lean index 994f8dd098..78dc1ceefb 100644 --- a/src/Lean/Language/Lean.lean +++ b/src/Lean/Language/Lean.lean @@ -234,6 +234,54 @@ structure SetupImportsResult where /-- Kernel trust level. -/ trustLevel : UInt32 := 0 +/-- Performance option used by cmdline driver. -/ +register_builtin_option internal.minimalSnapshots : Bool := { + defValue := false + descr := "reduce information stored in snapshots to the minimum necessary for the cmdline \ +driver: diagnostics per command and final full snapshot" +} + +/-- +Parses values of options registered during import and left by the C++ frontend as strings, fails if +any option names remain unknown. +-/ +def reparseOptions (opts : Options) : IO Options := do + let mut opts := opts + let decls ← getOptionDecls + for (name, val) in opts do + let .ofString val := val + | continue -- Already parsed by C++ + -- Options can be prefixed with `weak` in order to turn off the error when the option is not + -- defined + let weak := name.getRoot == `weak + if weak then + opts := opts.erase name + let name := name.replacePrefix `weak Name.anonymous + let some decl := decls.find? name + | unless weak do + throw <| .userError s!"invalid -D parameter, unknown configuration option '{name}' + +If the option is defined in this library, use '-D{`weak ++ name}' to set it conditionally" + + match decl.defValue with + | .ofBool _ => + match val with + | "true" => opts := opts.insert name true + | "false" => opts := opts.insert name false + | _ => + throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \ + it must be true/false" + | .ofNat _ => + let some val := val.toNat? + | throw <| .userError s!"invalid -D parameter, invalid configuration option '{val}' value, \ + it must be a natural number" + opts := opts.insert name val + | .ofString _ => opts := opts.insert name val + | _ => throw <| .userError s!"invalid -D parameter, configuration option '{name}' \ + cannot be set in the command line, use set_option command" + + return opts + /-- Entry point of the Lean language processor. @@ -279,9 +327,11 @@ where if let some oldProcSuccess := oldProcessed.result? then -- also wait on old command parse snapshot as parsing is cheap and may allow for -- elaboration reuse - oldProcSuccess.firstCmdSnap.bindIO (sync := true) fun oldCmd => + oldProcSuccess.firstCmdSnap.bindIO (sync := true) fun oldCmd => do + let prom ← IO.Promise.new + let _ ← IO.asTask (parseCmd oldCmd newParserState oldProcSuccess.cmdState oldProcSuccess.cmdState.env prom ctx) return .pure { oldProcessed with result? := some { oldProcSuccess with - firstCmdSnap := (← parseCmd oldCmd newParserState oldProcSuccess.cmdState ctx) } } + firstCmdSnap := { range? := none, task := prom.result } } } else return .pure oldProcessed) } } else return old @@ -343,42 +393,68 @@ where let setup ← match (← setupImports stx) with | .ok setup => pure setup | .error snap => return snap + + let startTime := (← IO.monoNanosNow).toFloat / 1000000000 -- allows `headerEnv` to be leaked, which would live until the end of the process anyway let (headerEnv, msgLog) ← Elab.processHeader (leakEnv := true) stx setup.opts .empty ctx.toInputContext setup.trustLevel + let stopTime := (← IO.monoNanosNow).toFloat / 1000000000 let diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog) if msgLog.hasErrors then return { diagnostics, result? := none } let headerEnv := headerEnv.setMainModule setup.mainModuleName - let cmdState := Elab.Command.mkState headerEnv msgLog setup.opts - let cmdState := { cmdState with infoState := { - enabled := true - trees := #[Elab.InfoTree.context (.commandCtx { - env := headerEnv - fileMap := ctx.fileMap - ngen := { namePrefix := `_import } - }) (Elab.InfoTree.node - (Elab.Info.ofCommandInfo { elaborator := `header, stx }) - (stx[1].getArgs.toList.map (fun importStx => - Elab.InfoTree.node (Elab.Info.ofCommandInfo { - elaborator := `import - stx := importStx - }) #[].toPArray' - )).toPArray' - )].toPArray' - }} + let mut traceState := default + if trace.profiler.output.get? setup.opts |>.isSome then + traceState := { + traces := #[{ + ref := .missing, + msg := .trace { cls := `Import, startTime, stopTime } + (.ofFormat "importing") #[] + : TraceElem + }].toPArray' + } + -- now that imports have been loaded, check options again + let opts ← reparseOptions setup.opts + let cmdState := Elab.Command.mkState headerEnv msgLog opts + let cmdState := { cmdState with + infoState := { + enabled := true + trees := #[Elab.InfoTree.context (.commandCtx { + env := headerEnv + fileMap := ctx.fileMap + ngen := { namePrefix := `_import } + }) (Elab.InfoTree.node + (Elab.Info.ofCommandInfo { elaborator := `header, stx }) + (stx[1].getArgs.toList.map (fun importStx => + Elab.InfoTree.node (Elab.Info.ofCommandInfo { + elaborator := `import + stx := importStx + }) #[].toPArray' + )).toPArray' + )].toPArray' + } + traceState + } + let prom ← IO.Promise.new + -- The speedup of these `markPersistent`s is negligible but they help in making unexpected + -- `inc_ref_cold`s more visible + let parserState := Runtime.markPersistent parserState + let cmdState := Runtime.markPersistent cmdState + let ctx := Runtime.markPersistent ctx + let _ ← IO.asTask (parseCmd none parserState cmdState cmdState.env prom ctx) return { diagnostics infoTree? := cmdState.infoState.trees[0]! result? := some { cmdState - firstCmdSnap := (← parseCmd none parserState cmdState) + firstCmdSnap := { range? := none, task := prom.result } } } parseCmd (old? : Option CommandParsedSnapshot) (parserState : Parser.ModuleParserState) - (cmdState : Command.State) : LeanProcessingM (SnapshotTask CommandParsedSnapshot) := do + (cmdState : Command.State) (initEnv : Environment) (prom : IO.Promise CommandParsedSnapshot) : + LeanProcessingM Unit := do let ctx ← read -- check for cancellation, most likely during elaboration of previous command, before starting @@ -387,82 +463,100 @@ where -- this is a bit ugly as we don't want to adjust our API with `Option`s just for cancellation -- (as no-one should look at this result in that case) but anything containing `Environment` -- is not `Inhabited` - return .pure <| .mk (nextCmdSnap? := none) { + prom.resolve <| .mk (nextCmdSnap? := none) { diagnostics := .empty, stx := .missing, parserState elabSnap := .pure <| .ofTyped { diagnostics := .empty : SnapshotLeaf } finishedSnap := .pure { diagnostics := .empty, cmdState } tacticCache := (← IO.mkRef {}) } + return - let unchanged old newParserState : BaseIO CommandParsedSnapshot := + let unchanged old newParserState : BaseIO Unit := -- when syntax is unchanged, reuse command processing task as is -- NOTE: even if the syntax tree is functionally unchanged, the new parser state may still -- have changed because of trailing whitespace and comments etc., so it is passed separately -- from `old` - if let some oldNext := old.nextCmdSnap? then - return .mk (data := old.data) - (nextCmdSnap? := (← old.data.finishedSnap.bindIO (sync := true) fun oldFinished => - -- also wait on old command parse snapshot as parsing is cheap and may allow for - -- elaboration reuse - oldNext.bindIO (sync := true) fun oldNext => do - parseCmd oldNext newParserState oldFinished.cmdState ctx)) - else return old -- terminal command, we're done! + if let some oldNext := old.nextCmdSnap? then do + let newProm ← IO.Promise.new + let _ ← old.data.finishedSnap.bindIO fun oldFinished => + -- also wait on old command parse snapshot as parsing is cheap and may allow for + -- elaboration reuse + oldNext.bindIO (sync := true) fun oldNext => do + parseCmd oldNext newParserState oldFinished.cmdState initEnv newProm ctx + return .pure () + prom.resolve <| .mk (data := old.data) (nextCmdSnap? := some { range? := none, task := newProm.result }) + else prom.resolve old -- terminal command, we're done! -- fast path, do not even start new task for this snapshot if let some old := old? then if let some nextCom ← old.nextCmdSnap?.bindM (·.get?) then if (← isBeforeEditPos nextCom.data.parserState.pos) then - return .pure (← unchanged old old.data.parserState) + return (← unchanged old old.data.parserState) - SnapshotTask.ofIO (some ⟨parserState.pos, ctx.input.endPos⟩) do - let beginPos := parserState.pos - let scope := cmdState.scopes.head! - let pmctx := { - env := cmdState.env, options := scope.opts, currNamespace := scope.currNamespace - openDecls := scope.openDecls - } - let (stx, parserState, msgLog) := Parser.parseCommand ctx.toInputContext pmctx parserState - .empty + let beginPos := parserState.pos + let scope := cmdState.scopes.head! + let pmctx := { + env := cmdState.env, options := scope.opts, currNamespace := scope.currNamespace + openDecls := scope.openDecls + } + let (stx, parserState, msgLog) := + profileit "parsing" scope.opts fun _ => + Parser.parseCommand ctx.toInputContext pmctx parserState .empty - -- semi-fast path - if let some old := old? then - -- NOTE: as `parserState.pos` includes trailing whitespace, this forces reprocessing even if - -- only that whitespace changes, which is wasteful but still necessary because it may - -- influence the range of error messages such as from a trailing `exact` - if stx.eqWithInfo old.data.stx then - -- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged` - return (← unchanged old parserState) - -- on first change, make sure to cancel old invocation - -- TODO: pass token into incrementality-aware elaborators to improve reuse of still-valid, - -- still-running elaboration steps? - if let some tk := ctx.oldCancelTk? then - tk.set + -- semi-fast path + if let some old := old? then + -- NOTE: as `parserState.pos` includes trailing whitespace, this forces reprocessing even if + -- only that whitespace changes, which is wasteful but still necessary because it may + -- influence the range of error messages such as from a trailing `exact` + if stx.eqWithInfo old.data.stx then + -- Here we must make sure to pass the *new* parser state; see NOTE in `unchanged` + return (← unchanged old parserState) + -- on first change, make sure to cancel old invocation + -- TODO: pass token into incrementality-aware elaborators to improve reuse of still-valid, + -- still-running elaboration steps? + if let some tk := ctx.oldCancelTk? then + tk.set - -- definitely resolved in `doElab` task - let elabPromise ← IO.Promise.new - let tacticCache ← old?.map (·.data.tacticCache) |>.getDM (IO.mkRef {}) - let finishedSnap ← - doElab stx cmdState beginPos - { old? := old?.map fun old => ⟨old.data.stx, old.data.elabSnap⟩, new := elabPromise } - tacticCache - ctx - - let next? ← if Parser.isTerminalCommand stx then pure none - -- for now, wait on "command finished" snapshot before parsing next command - else some <$> finishedSnap.bindIO fun finished => - parseCmd none parserState finished.cmdState ctx - return .mk (nextCmdSnap? := next?) { - diagnostics := (← Snapshot.Diagnostics.ofMessageLog msgLog) - stx - parserState - elabSnap := { range? := stx.getRange?, task := elabPromise.result } - finishedSnap + -- definitely resolved in `doElab` task + let elabPromise ← IO.Promise.new + let tacticCache ← old?.map (·.data.tacticCache) |>.getDM (IO.mkRef {}) + let finishedSnap ← + doElab stx cmdState beginPos + { old? := old?.map fun old => ⟨old.data.stx, old.data.elabSnap⟩, new := elabPromise } tacticCache + ctx + + let minimalSnapshots := internal.minimalSnapshots.get cmdState.scopes.head!.opts + let next? ← if Parser.isTerminalCommand stx then pure none + -- for now, wait on "command finished" snapshot before parsing next command + else some <$> IO.Promise.new + let diagnostics ← Snapshot.Diagnostics.ofMessageLog msgLog + let data := if minimalSnapshots && !Parser.isTerminalCommand stx then { + diagnostics + stx := .missing + parserState := {} + elabSnap := { range? := stx.getRange?, task := elabPromise.result } + finishedSnap := .pure { + diagnostics := finishedSnap.diagnostics + infoTree? := none + cmdState := { + env := initEnv + maxRecDepth := 0 + } } + tacticCache + } else { + diagnostics, stx, parserState, tacticCache + elabSnap := { range? := stx.getRange?, task := elabPromise.result } + finishedSnap := .pure finishedSnap + } + prom.resolve <| .mk (nextCmdSnap? := next?.map ({ range? := some ⟨parserState.pos, ctx.input.endPos⟩, task := ·.result })) data + if let some next := next? then + parseCmd none parserState finishedSnap.cmdState initEnv next ctx doElab (stx : Syntax) (cmdState : Command.State) (beginPos : String.Pos) (snap : SnapshotBundle DynamicSnapshot) (tacticCache : IO.Ref Tactic.Cache) : - LeanProcessingM (SnapshotTask CommandFinishedSnapshot) := do + LeanProcessingM CommandFinishedSnapshot := do let ctx ← read -- (Try to) use last line of command as range for final snapshot task. This ensures we do not -- retract the progress bar to a previous position in case the command support incremental @@ -471,48 +565,46 @@ where -- `parseCmd` and containing the entire range of the command will determine the reported -- progress and be resolved effectively at the same time as this snapshot task, so `tailPos` is -- irrelevant in this case. - let tailPos := stx.getTailPos? |>.getD beginPos - SnapshotTask.ofIO (some ⟨tailPos, tailPos⟩) do - let scope := cmdState.scopes.head! - let cmdStateRef ← IO.mkRef { cmdState with messages := .empty } - /- - The same snapshot may be executed by different tasks. So, to make sure `elabCommandTopLevel` - has exclusive access to the cache, we create a fresh reference here. Before this change, the - following `tacticCache.modify` would reset the tactic post cache while another snapshot was - still using it. - -/ - let tacticCacheNew ← IO.mkRef (← tacticCache.get) - let cmdCtx : Elab.Command.Context := { ctx with - cmdPos := beginPos - tacticCache? := some tacticCacheNew - snap? := some snap - cancelTk? := some ctx.newCancelTk - } - let (output, _) ← - IO.FS.withIsolatedStreams (isolateStderr := stderrAsMessages.get scope.opts) do - liftM (m := BaseIO) do - withLoggingExceptions - (getResetInfoTrees *> Elab.Command.elabCommandTopLevel stx) - cmdCtx cmdStateRef - let postNew := (← tacticCacheNew.get).post - tacticCache.modify fun _ => { pre := postNew, post := {} } - let cmdState ← cmdStateRef.get - let mut messages := cmdState.messages - if !output.isEmpty then - messages := messages.add { - fileName := ctx.fileName - severity := MessageSeverity.information - pos := ctx.fileMap.toPosition beginPos - data := output - } - let cmdState := { cmdState with messages } - -- definitely resolve eventually - snap.new.resolve <| .ofTyped { diagnostics := .empty : SnapshotLeaf } - return { - diagnostics := (← Snapshot.Diagnostics.ofMessageLog cmdState.messages) - infoTree? := some cmdState.infoState.trees[0]! - cmdState + let scope := cmdState.scopes.head! + let cmdStateRef ← IO.mkRef { cmdState with messages := .empty } + /- + The same snapshot may be executed by different tasks. So, to make sure `elabCommandTopLevel` + has exclusive access to the cache, we create a fresh reference here. Before this change, the + following `tacticCache.modify` would reset the tactic post cache while another snapshot was + still using it. + -/ + let tacticCacheNew ← IO.mkRef (← tacticCache.get) + let cmdCtx : Elab.Command.Context := { ctx with + cmdPos := beginPos + tacticCache? := some tacticCacheNew + snap? := if internal.minimalSnapshots.get scope.opts then none else snap + cancelTk? := some ctx.newCancelTk + } + let (output, _) ← + IO.FS.withIsolatedStreams (isolateStderr := stderrAsMessages.get scope.opts) do + liftM (m := BaseIO) do + withLoggingExceptions + (getResetInfoTrees *> Elab.Command.elabCommandTopLevel stx) + cmdCtx cmdStateRef + let postNew := (← tacticCacheNew.get).post + tacticCache.modify fun _ => { pre := postNew, post := {} } + let cmdState ← cmdStateRef.get + let mut messages := cmdState.messages + if !output.isEmpty then + messages := messages.add { + fileName := ctx.fileName + severity := MessageSeverity.information + pos := ctx.fileMap.toPosition beginPos + data := output } + let cmdState := { cmdState with messages } + -- definitely resolve eventually + snap.new.resolve <| .ofTyped { diagnostics := .empty : SnapshotLeaf } + return { + diagnostics := (← Snapshot.Diagnostics.ofMessageLog cmdState.messages) + infoTree? := some cmdState.infoState.trees[0]! + cmdState + } /-- Convenience function for tool uses of the language processor that skips header handling. @@ -520,14 +612,15 @@ Convenience function for tool uses of the language processor that skips header h def processCommands (inputCtx : Parser.InputContext) (parserState : Parser.ModuleParserState) (commandState : Command.State) (old? : Option (Parser.InputContext × CommandParsedSnapshot) := none) : - BaseIO (SnapshotTask CommandParsedSnapshot) := do - process.parseCmd (old?.map (·.2)) parserState commandState + BaseIO (Task CommandParsedSnapshot) := do + let prom ← IO.Promise.new + process.parseCmd (old?.map (·.2)) parserState commandState commandState.env prom |>.run (old?.map (·.1)) |>.run { inputCtx with } + return prom.result - -/-- Waits for and returns final environment, if importing was successful. -/ -partial def waitForFinalEnv? (snap : InitialSnapshot) : Option Environment := do +/-- Waits for and returns final command state, if importing was successful. -/ +partial def waitForFinalCmdState? (snap : InitialSnapshot) : Option Command.State := do let snap ← snap.result? let snap ← snap.processedSnap.get.result? goCmd snap.firstCmdSnap.get @@ -535,6 +628,6 @@ where goCmd snap := if let some next := snap.nextCmdSnap? then goCmd next.get else - snap.data.finishedSnap.get.cmdState.env + snap.data.finishedSnap.get.cmdState end Lean diff --git a/tests/lean/2505.lean.expected.out b/tests/lean/2505.lean.expected.out index 6610d40a35..98a7adec30 100644 --- a/tests/lean/2505.lean.expected.out +++ b/tests/lean/2505.lean.expected.out @@ -1,3 +1,3 @@ +2505.lean:14:0-14:7: warning: declaration uses 'sorry' target : A (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) target' : A (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) -2505.lean:14:0-14:7: warning: declaration uses 'sorry' diff --git a/tests/lean/4089.lean.expected.out b/tests/lean/4089.lean.expected.out index 9152f7bfa4..18c8b0d2e9 100644 --- a/tests/lean/4089.lean.expected.out +++ b/tests/lean/4089.lean.expected.out @@ -8,6 +8,7 @@ def f (x_1 : obj) : obj := let x_5 : obj := reset[2] x_1; let x_4 : obj := reuse x_5 in ctor_0[Prod.mk] x_3 x_2; ret x_4 + [reset_reuse] def Sigma.toProd._rarg (x_1 : obj) : obj := case x_1 : obj of @@ -20,6 +21,7 @@ def Sigma.toProd._rarg (x_1 : obj) : obj := def Sigma.toProd (x_1 : ◾) (x_2 : ◾) : obj := let x_3 : obj := pap Sigma.toProd._rarg; ret x_3 + [reset_reuse] def foo (x_1 : obj) : obj := case x_1 : obj of @@ -35,4 +37,4 @@ def foo (x_1 : obj) : obj := let x_5 : obj := proj[0] x_3; let x_6 : obj := foo x_4; let x_7 : obj := reuse x_9 in ctor_1[List.cons] x_5 x_6; - ret x_7 \ No newline at end of file + ret x_7 diff --git a/tests/lean/4240.lean.expected.out b/tests/lean/4240.lean.expected.out index 174456c8f3..30636e3d30 100644 --- a/tests/lean/4240.lean.expected.out +++ b/tests/lean/4240.lean.expected.out @@ -23,4 +23,4 @@ def isSomeWithInstanceNat._boxed (x_1 : obj) : obj := let x_2 : u8 := isSomeWithInstanceNat x_1; dec x_1; let x_3 : obj := box x_2; - ret x_3 \ No newline at end of file + ret x_3 diff --git a/tests/lean/Process.lean.expected.out b/tests/lean/Process.lean.expected.out index 600e4f2544..5d186fa1f4 100644 --- a/tests/lean/Process.lean.expected.out +++ b/tests/lean/Process.lean.expected.out @@ -1,6 +1,5 @@ -hi! -:1:0: warning: using 'exit' to interrupt Lean 1 +hi! 0 "ho!\n" "hu!\n" @@ -9,6 +8,7 @@ flush of broken pipe failed 100000 0 0 +:1:0: warning: using 'exit' to interrupt Lean 0 0 none diff --git a/tests/lean/computedFieldsCode.lean.expected.out b/tests/lean/computedFieldsCode.lean.expected.out index 10f711f905..4c2e4c9e17 100644 --- a/tests/lean/computedFieldsCode.lean.expected.out +++ b/tests/lean/computedFieldsCode.lean.expected.out @@ -106,6 +106,7 @@ def Exp.hash._override._boxed (x_1 : obj) : obj := dec x_1; let x_3 : obj := box x_2; ret x_3 + [result] def f._closed_1 : obj := let x_1 : u32 := 10; @@ -124,6 +125,7 @@ def f._closed_3 : u64 := def f : u64 := let x_1 : u64 := f._closed_3; ret x_1 + [result] def g (x_1 : @& obj) : u8 := case x_1 : obj of @@ -138,6 +140,7 @@ def g._boxed (x_1 : obj) : obj := dec x_1; let x_3 : obj := box x_2; ret x_3 + [result] def hash' (x_1 : @& obj) : obj := case x_1 : obj of @@ -161,6 +164,7 @@ def hash'._boxed (x_1 : obj) : obj := let x_2 : obj := hash' x_1; dec x_1; ret x_2 + [result] def getAppFn (x_1 : @& obj) : obj := case x_1 : obj of @@ -175,6 +179,7 @@ def getAppFn._boxed (x_1 : obj) : obj := let x_2 : obj := getAppFn x_1; dec x_1; ret x_2 + [result] def Exp.f (x_1 : @& obj) : obj := let x_2 : obj := getAppFn x_1; @@ -182,4 +187,4 @@ def Exp.f (x_1 : @& obj) : obj := def Exp.f._boxed (x_1 : obj) : obj := let x_2 : obj := Exp.f x_1; dec x_1; - ret x_2 \ No newline at end of file + ret x_2 diff --git a/tests/lean/csimpAttr.lean.expected.out b/tests/lean/csimpAttr.lean.expected.out index d69d1215be..a491585b8c 100644 --- a/tests/lean/csimpAttr.lean.expected.out +++ b/tests/lean/csimpAttr.lean.expected.out @@ -1,6 +1,7 @@ +csimpAttr.lean:7:2-7:7: error: invalid 'csimp' theorem, only constant replacement theorems (e.g., `@f = @g`) are currently supported. [init] def f (x_1 : obj) : obj := let x_2 : obj := Nat.add x_1 x_1; let x_3 : obj := Nat.add x_2 x_2; - ret x_3csimpAttr.lean:7:2-7:7: error: invalid 'csimp' theorem, only constant replacement theorems (e.g., `@f = @g`) are currently supported. + ret x_3 diff --git a/tests/lean/csimpAttrAppend.lean.expected.out b/tests/lean/csimpAttrAppend.lean.expected.out index 0b72623159..2e7eac5952 100644 --- a/tests/lean/csimpAttrAppend.lean.expected.out +++ b/tests/lean/csimpAttrAppend.lean.expected.out @@ -3,4 +3,4 @@ def f (x_1 : obj) (x_2 : obj) (x_3 : obj) : obj := let x_4 : obj := List.appendTR._rarg x_1 x_2; let x_5 : obj := List.appendTR._rarg x_4 x_3; - ret x_5 \ No newline at end of file + ret x_5 diff --git a/tests/lean/dbgMacros.lean.expected.out b/tests/lean/dbgMacros.lean.expected.out index 9b70d1b860..54d6f8756c 100644 --- a/tests/lean/dbgMacros.lean.expected.out +++ b/tests/lean/dbgMacros.lean.expected.out @@ -1,10 +1,10 @@ PANIC at f dbgMacros:2:14: unexpected zero -PANIC at g dbgMacros:10:14: unreachable code has been reached -PANIC at h dbgMacros:16:0: assertion violation: x != 0 0 9 +PANIC at g dbgMacros:10:14: unreachable code has been reached 0 0 +PANIC at h dbgMacros:16:0: assertion violation: x != 0 0 f2, x: 10 11 diff --git a/tests/lean/doubleReset.lean.expected.out b/tests/lean/doubleReset.lean.expected.out index fce8062bf3..25937761cd 100644 --- a/tests/lean/doubleReset.lean.expected.out +++ b/tests/lean/doubleReset.lean.expected.out @@ -34,4 +34,4 @@ def applyProjectionRules._rarg (x_1 : obj) (x_2 : obj) : obj := ret x_5 def applyProjectionRules (x_1 : ◾) (x_2 : ◾) (x_3 : ◾) : obj := let x_4 : obj := pap applyProjectionRules._rarg; - ret x_4 \ No newline at end of file + ret x_4 diff --git a/tests/lean/etaReducedMvarAssignments.lean.expected.out b/tests/lean/etaReducedMvarAssignments.lean.expected.out index 7c743ec28e..b85fc4033b 100644 --- a/tests/lean/etaReducedMvarAssignments.lean.expected.out +++ b/tests/lean/etaReducedMvarAssignments.lean.expected.out @@ -1,4 +1,4 @@ -Pi.hasLe : LE ((i : ι) → α i) -[Meta.isDefEq.assign] ✅ ?m i := α i - [Meta.isDefEq.assign.beforeMkLambda] ?m [i] := α i - [Meta.isDefEq.assign.checkTypes] ✅ (?m : ι → Type ?u) := (α : ι → Type v) +Pi.hasLe : LE ((i : ι) → α i) +[Meta.isDefEq.assign] ✅ ?m i := α i + [Meta.isDefEq.assign.beforeMkLambda] ?m [i] := α i + [Meta.isDefEq.assign.checkTypes] ✅ (?m : ι → Type ?u) := (α : ι → Type v) diff --git a/tests/lean/listLength.lean.expected.out b/tests/lean/listLength.lean.expected.out index 311ee817f5..ae2f89a73c 100644 --- a/tests/lean/listLength.lean.expected.out +++ b/tests/lean/listLength.lean.expected.out @@ -5,4 +5,4 @@ def f (x_1 : obj) : obj := let x_3 : obj := List.lengthTRAux._rarg x_1 x_2; let x_4 : obj := 2; let x_5 : obj := Nat.mul x_4 x_3; - ret x_5 \ No newline at end of file + ret x_5 diff --git a/tests/lean/lit_values.lean.expected.out b/tests/lean/lit_values.lean.expected.out index ee860cc74b..20056b7967 100644 --- a/tests/lean/lit_values.lean.expected.out +++ b/tests/lean/lit_values.lean.expected.out @@ -3,10 +3,10 @@ (some 2) (some -2) (some a) +some "hello" (some (⟨5, 3⟩)) (some (⟨12, 0x003#12⟩)) (some 2) (some 2) (some 2) (some 2) -some "hello" diff --git a/tests/lean/unboxStruct.lean.expected.out b/tests/lean/unboxStruct.lean.expected.out index 8d0db228ff..feffd37563 100644 --- a/tests/lean/unboxStruct.lean.expected.out +++ b/tests/lean/unboxStruct.lean.expected.out @@ -7,4 +7,4 @@ def test2._boxed (x_1 : obj) (x_2 : obj) : obj := let x_3 : u32 := unbox x_1; dec x_1; let x_4 : obj := test2 x_3 x_2; - ret x_4 \ No newline at end of file + ret x_4 diff --git a/tests/lean/updateExprIssue.lean.expected.out b/tests/lean/updateExprIssue.lean.expected.out index 10537d1e32..5e550af5df 100644 --- a/tests/lean/updateExprIssue.lean.expected.out +++ b/tests/lean/updateExprIssue.lean.expected.out @@ -1,4 +1,5 @@ + [init] def sefFn (x_1 : obj) (x_2 : obj) : obj := case x_1 : obj of