mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
chore: remove save tactic (#7047)
This PR removes the `save` and `checkpoint` tactics that have been superseded by incremental elaboration
This commit is contained in:
committed by
GitHub
parent
f61e2989a2
commit
f7e207a824
@@ -1014,31 +1014,6 @@ example : ∀ x : Nat, x = x := by unhygienic
|
||||
-/
|
||||
macro "unhygienic " t:tacticSeq : tactic => `(tactic| set_option tactic.hygienic false in $t)
|
||||
|
||||
/--
|
||||
`checkpoint tac` acts the same as `tac`, but it caches the input and output of `tac`,
|
||||
and if the file is re-elaborated and the input matches, the tactic is not re-run and
|
||||
its effects are reapplied to the state. This is useful for improving responsiveness
|
||||
when working on a long tactic proof, by wrapping expensive tactics with `checkpoint`.
|
||||
|
||||
See the `save` tactic, which may be more convenient to use.
|
||||
|
||||
(TODO: do this automatically and transparently so that users don't have to use
|
||||
this combinator explicitly.)
|
||||
-/
|
||||
syntax (name := checkpoint) "checkpoint " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`save` is defined to be the same as `skip`, but the elaborator has
|
||||
special handling for occurrences of `save` in tactic scripts and will transform
|
||||
`by tac1; save; tac2` to `by (checkpoint tac1); tac2`, meaning that the effect of `tac1`
|
||||
will be cached and replayed. This is useful for improving responsiveness
|
||||
when working on a long tactic proof, by using `save` after expensive tactics.
|
||||
|
||||
(TODO: do this automatically and transparently so that users don't have to use
|
||||
this combinator explicitly.)
|
||||
-/
|
||||
macro (name := save) "save" : tactic => `(tactic| skip)
|
||||
|
||||
/--
|
||||
The tactic `sleep ms` sleeps for `ms` milliseconds and does nothing.
|
||||
It is used for debugging purposes only.
|
||||
|
||||
@@ -95,7 +95,6 @@ structure Context where
|
||||
macroStack : MacroStack := []
|
||||
currMacroScope : MacroScope := firstFrontendMacroScope
|
||||
ref : Syntax := Syntax.missing
|
||||
tacticCache? : Option (IO.Ref Tactic.Cache)
|
||||
/--
|
||||
Snapshot for incremental reuse and reporting of command elaboration. Currently only used for
|
||||
(mutual) defs and contained tactics, in which case the `DynamicSnapshot` is a
|
||||
@@ -619,8 +618,7 @@ private def mkTermContext (ctx : Context) (s : State) : CommandElabM Term.Contex
|
||||
return {
|
||||
macroStack := ctx.macroStack
|
||||
sectionVars := sectionVars
|
||||
isNoncomputableSection := scope.isNoncomputable
|
||||
tacticCache? := ctx.tacticCache? }
|
||||
isNoncomputableSection := scope.isNoncomputable }
|
||||
|
||||
/--
|
||||
Lift the `TermElabM` monadic action `x` into a `CommandElabM` monadic action.
|
||||
@@ -759,7 +757,6 @@ private def liftCommandElabMCore (cmd : CommandElabM α) (throwOnError : Bool) :
|
||||
currRecDepth := ctx.currRecDepth
|
||||
currMacroScope := ctx.currMacroScope
|
||||
ref := ctx.ref
|
||||
tacticCache? := none
|
||||
snap? := none
|
||||
cancelTk? := ctx.cancelTk?
|
||||
suppressElabErrors := ctx.suppressElabErrors
|
||||
|
||||
@@ -33,7 +33,6 @@ def setCommandState (commandState : Command.State) : FrontendM Unit :=
|
||||
cmdPos := s.cmdPos
|
||||
fileName := ctx.inputCtx.fileName
|
||||
fileMap := ctx.inputCtx.fileMap
|
||||
tacticCache? := none
|
||||
snap? := none
|
||||
cancelTk? := none
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import Lean.Elab.Tactic.Conv
|
||||
import Lean.Elab.Tactic.Delta
|
||||
import Lean.Elab.Tactic.Meta
|
||||
import Lean.Elab.Tactic.Unfold
|
||||
import Lean.Elab.Tactic.Cache
|
||||
import Lean.Elab.Tactic.Calc
|
||||
import Lean.Elab.Tactic.Congr
|
||||
import Lean.Elab.Tactic.Guard
|
||||
|
||||
@@ -135,70 +135,6 @@ where
|
||||
@[builtin_tactic paren, builtin_incremental] def evalParen : Tactic :=
|
||||
Term.withNarrowedArgTacticReuse 1 evalTactic
|
||||
|
||||
def isCheckpointableTactic (arg : Syntax) : TacticM Bool := do
|
||||
-- TODO: make it parametric
|
||||
let kind := arg.getKind
|
||||
return kind == ``Lean.Parser.Tactic.save
|
||||
|
||||
/--
|
||||
Takes a `sepByIndent tactic "; "`, and inserts `checkpoint` blocks for `save` tactics.
|
||||
|
||||
Input:
|
||||
```
|
||||
a
|
||||
b
|
||||
save
|
||||
c
|
||||
d
|
||||
save
|
||||
e
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
checkpoint
|
||||
a
|
||||
b
|
||||
save
|
||||
checkpoint
|
||||
c
|
||||
d
|
||||
save
|
||||
e
|
||||
```
|
||||
-/
|
||||
-- Note that we need to preserve the separators to show the right goals after semicolons.
|
||||
def addCheckpoints (stx : Syntax) : TacticM Syntax := do
|
||||
if !(← stx.getSepArgs.anyM isCheckpointableTactic) then return stx
|
||||
-- do not checkpoint checkpointable tactic by itself to prevent infinite recursion
|
||||
-- TODO: rethink approach if we add non-trivial checkpointable tactics
|
||||
if stx.getNumArgs <= 2 then return stx
|
||||
let mut currentCheckpointBlock := #[]
|
||||
let mut output := #[]
|
||||
-- `+ 1` to account for optional trailing separator
|
||||
for i in [:(stx.getArgs.size + 1) / 2] do
|
||||
let tac := stx[2*i]
|
||||
let sep? := stx.getArgs[2*i+1]?
|
||||
if (← isCheckpointableTactic tac) then
|
||||
let checkpoint : Syntax :=
|
||||
mkNode ``checkpoint #[
|
||||
mkAtomFrom tac "checkpoint",
|
||||
mkNode ``tacticSeq #[
|
||||
mkNode ``tacticSeq1Indented #[
|
||||
-- HACK: null node is not a valid tactic, but prevents infinite loop
|
||||
mkNullNode (currentCheckpointBlock.push (mkNullNode #[tac]))
|
||||
]
|
||||
]
|
||||
]
|
||||
currentCheckpointBlock := #[]
|
||||
output := output.push checkpoint
|
||||
if let some sep := sep? then output := output.push sep
|
||||
else
|
||||
currentCheckpointBlock := currentCheckpointBlock.push tac
|
||||
if let some sep := sep? then currentCheckpointBlock := currentCheckpointBlock.push sep
|
||||
output := output ++ currentCheckpointBlock
|
||||
return stx.setArgs output
|
||||
|
||||
@[builtin_tactic tacticSeq1Indented, builtin_incremental]
|
||||
def evalTacticSeq1Indented : Tactic :=
|
||||
Term.withNarrowedArgTacticReuse (argIdx := 0) evalSepTactics
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Lean.Elab.Tactic.Basic
|
||||
|
||||
namespace Lean.Elab.Tactic
|
||||
open Meta
|
||||
|
||||
private def equivMVarDecl (d₁ d₂ : MetavarDecl) : Bool :=
|
||||
d₁.type == d₂.type
|
||||
-- The following additional check does not seem to be necessary
|
||||
/-
|
||||
&&
|
||||
d₁.lctx.decls.size == d₂.lctx.decls.size &&
|
||||
Id.run do
|
||||
for i in [:d₁.lctx.decls.size] do
|
||||
match d₁.lctx.decls[i], d₂.lctx.decls[i] with
|
||||
| some localDecl₁, some localDecl₂ => if localDecl₁.type != localDecl₂.type then return false
|
||||
| none, none => pure ()
|
||||
| _, _ => return false
|
||||
return true
|
||||
-/
|
||||
|
||||
register_builtin_option tactic.dbg_cache : Bool := {
|
||||
defValue := false
|
||||
group := "tactic"
|
||||
descr := "enable tactic cache debug messages (remark: they are sent to the standard error)"
|
||||
}
|
||||
|
||||
private def dbg_cache (msg : String) : TacticM Unit := do
|
||||
if tactic.dbg_cache.get (← getOptions) then
|
||||
dbg_trace msg
|
||||
|
||||
private def dbg_cache' (cacheRef : IO.Ref Cache) (pos : String.Pos) (mvarId : MVarId) (msg : String) : TacticM Unit := do
|
||||
if tactic.dbg_cache.get (← getOptions) then
|
||||
let {line, column} := (← getFileMap).toPosition pos
|
||||
dbg_trace "{msg}, line: {line}, column: {column}, contains entry: {(← cacheRef.get).pre.find? { mvarId, pos } |>.isSome}"
|
||||
|
||||
private def findCache? (cacheRef : IO.Ref Cache) (mvarId : MVarId) (stx : Syntax) (pos : String.Pos) : TacticM (Option Snapshot) := do
|
||||
let some s := (← cacheRef.get).pre.find? { mvarId, pos } | do dbg_cache "cache key not found"; return none
|
||||
let mvarDecl ← mvarId.getDecl
|
||||
let some mvarDeclOld := s.meta.mctx.findDecl? mvarId | return none
|
||||
if equivMVarDecl mvarDecl mvarDeclOld then
|
||||
if stx == s.stx then
|
||||
return some s
|
||||
else
|
||||
dbg_cache "syntax is different"
|
||||
return none
|
||||
else
|
||||
dbg_cache "cached state is not compatible"
|
||||
return none
|
||||
|
||||
@[builtin_tactic checkpoint] def evalCheckpoint : Tactic := fun stx =>
|
||||
focus do
|
||||
let mvarId ← getMainGoal
|
||||
let some cacheRef := (← readThe Term.Context).tacticCache? | evalTactic stx[1]
|
||||
let some pos := stx.getPos? | evalTactic stx[1]
|
||||
match (← findCache? cacheRef mvarId stx[1] pos) with
|
||||
| some s =>
|
||||
cacheRef.modify fun { pre, post } => { pre, post := post.insert { mvarId, pos } s }
|
||||
set s.core
|
||||
set s.meta
|
||||
set s.term
|
||||
set s.tactic
|
||||
dbg_cache' cacheRef pos mvarId "cache hit"
|
||||
| none =>
|
||||
evalTactic stx[1]
|
||||
let s := {
|
||||
stx := stx[1]
|
||||
core := (← getThe Core.State)
|
||||
meta := (← getThe Meta.State)
|
||||
term := (← getThe Term.State)
|
||||
tactic := (← get)
|
||||
}
|
||||
dbg_cache' cacheRef pos mvarId "cache miss"
|
||||
cacheRef.modify fun { pre, post } => { pre, post := post.insert { mvarId, pos } s }
|
||||
|
||||
end Lean.Elab.Tactic
|
||||
@@ -306,8 +306,6 @@ structure Context where
|
||||
ignoreTCFailures : Bool := false
|
||||
/-- `true` when elaborating patterns. It affects how we elaborate named holes. -/
|
||||
inPattern : Bool := false
|
||||
/-- Cache for the `save` tactic. It is only `some` in the LSP server. -/
|
||||
tacticCache? : Option (IO.Ref Tactic.Cache) := none
|
||||
/--
|
||||
Snapshot for incremental processing of current tactic, if any.
|
||||
|
||||
|
||||
@@ -542,7 +542,6 @@ where
|
||||
elabSnap := default
|
||||
finishedSnap := .pure { diagnostics := .empty, cmdState }
|
||||
reportSnap := default
|
||||
tacticCache := (← IO.mkRef {})
|
||||
nextCmdSnap? := none
|
||||
}
|
||||
return
|
||||
@@ -557,7 +556,6 @@ where
|
||||
-- progress
|
||||
let initRange? := getNiceCommandStartPos? stx |>.map fun pos => ⟨pos, pos⟩
|
||||
let finishedSnap := { range? := initRange?, task := finishedPromise.result! }
|
||||
let tacticCache ← old?.map (·.tacticCache) |>.getDM (IO.mkRef {})
|
||||
|
||||
let minimalSnapshots := internal.cmdlineSnapshots.get cmdState.scopes.head!.opts
|
||||
let next? ← if Parser.isTerminalCommand stx then pure none
|
||||
@@ -571,14 +569,14 @@ where
|
||||
else
|
||||
(stx, parserState)
|
||||
prom.resolve {
|
||||
diagnostics, finishedSnap, tacticCache, nextCmdSnap?
|
||||
diagnostics, finishedSnap, nextCmdSnap?
|
||||
stx := stx', parserState := parserState'
|
||||
elabSnap := { range? := stx.getRange?, task := elabPromise.result! }
|
||||
reportSnap := { range? := initRange?, task := reportPromise.result! }
|
||||
}
|
||||
let cmdState ← doElab stx cmdState beginPos
|
||||
{ old? := old?.map fun old => ⟨old.stx, old.elabSnap⟩, new := elabPromise }
|
||||
finishedPromise tacticCache ctx
|
||||
finishedPromise ctx
|
||||
let traceTask ←
|
||||
if (← isTracingEnabledForCore `Elab.snapshotTree cmdState.scopes.head!.opts) then
|
||||
-- We want to trace all of `CommandParsedSnapshot` but `traceTask` is part of it, so let's
|
||||
@@ -611,24 +609,15 @@ where
|
||||
parseCmd none parserState cmdState next (sync := false) ctx
|
||||
|
||||
doElab (stx : Syntax) (cmdState : Command.State) (beginPos : String.Pos)
|
||||
(snap : SnapshotBundle DynamicSnapshot) (finishedPromise : IO.Promise CommandFinishedSnapshot)
|
||||
(tacticCache : IO.Ref Tactic.Cache) :
|
||||
(snap : SnapshotBundle DynamicSnapshot) (finishedPromise : IO.Promise CommandFinishedSnapshot) :
|
||||
LeanProcessingM Command.State := do
|
||||
let ctx ← read
|
||||
let scope := cmdState.scopes.head!
|
||||
-- reset per-command state
|
||||
let cmdStateRef ← IO.mkRef { cmdState with
|
||||
messages := .empty, traceState := {}, snapshotTasks := #[] }
|
||||
/-
|
||||
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.cmdlineSnapshots.get scope.opts then none else snap
|
||||
cancelTk? := some ctx.newCancelTk
|
||||
}
|
||||
@@ -638,8 +627,6 @@ where
|
||||
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
|
||||
|
||||
@@ -48,8 +48,6 @@ structure CommandParsedSnapshot extends Snapshot where
|
||||
finishedSnap : SnapshotTask CommandFinishedSnapshot
|
||||
/-- Additional, untyped snapshots used for reporting, not reuse. -/
|
||||
reportSnap : SnapshotTask SnapshotTree
|
||||
/-- Cache for `save`; to be replaced with incrementality. -/
|
||||
tacticCache : IO.Ref Tactic.Cache
|
||||
/-- Next command, unless this is a terminal command. -/
|
||||
nextCmdSnap? : Option (SnapshotTask CommandParsedSnapshot)
|
||||
deriving Nonempty
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace Lean.Server.Snapshots
|
||||
|
||||
open Elab
|
||||
|
||||
/- For `Inhabited Snapshot` -/
|
||||
builtin_initialize dummyTacticCache : IO.Ref Tactic.Cache ← IO.mkRef {}
|
||||
|
||||
/-- What Lean knows about the world after the header and each command. -/
|
||||
structure Snapshot where
|
||||
stx : Syntax
|
||||
@@ -56,7 +53,6 @@ def runCommandElabM (snap : Snapshot) (meta : DocumentMeta) (c : CommandElabM α
|
||||
cmdPos := snap.stx.getPos? |>.getD 0,
|
||||
fileName := meta.uri,
|
||||
fileMap := meta.text,
|
||||
tacticCache? := none
|
||||
snap? := none
|
||||
cancelTk? := none
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ def mkCtx [BEq tp] [Hashable tp]
|
||||
let fileMap := coreCtx.fileMap
|
||||
let env := coreState.env
|
||||
let maxRecDepth := coreCtx.maxRecDepth
|
||||
let cmdCtx : Command.Context := { fileName, fileMap, tacticCache? := none }
|
||||
let cmdCtx : Command.Context := { fileName, fileMap }
|
||||
let cmdState : Command.State := { env, maxRecDepth }
|
||||
let addVars (p : LocalContext × LocalInstances × Array (Array val))
|
||||
(q : tp × CoreM Command) :
|
||||
|
||||
Reference in New Issue
Block a user