refactor: add annotation for displaying conv state

This commit is contained in:
Leonardo de Moura 2021-09-02 15:51:38 -07:00
parent a8b434f93d
commit 391366ef24
8 changed files with 32 additions and 31 deletions

View file

@ -78,7 +78,6 @@ structure TacticInfo extends ElabInfo where
goalsBefore : List MVarId
mctxAfter : MetavarContext
goalsAfter : List MVarId
inConv : Bool
deriving Inhabited
structure MacroExpansionInfo where
@ -189,17 +188,17 @@ def FieldInfo.format (ctx : ContextInfo) (info : FieldInfo) : IO Format := do
ctx.runMetaM info.lctx do
return f!"{info.fieldName} : {← Meta.ppExpr (← Meta.inferType info.val)} := {← Meta.ppExpr info.val} @ {formatStxRange ctx info.stx}"
def ContextInfo.ppGoals (ctx : ContextInfo) (goals : List MVarId) (inConv : Bool) : IO Format :=
def ContextInfo.ppGoals (ctx : ContextInfo) (goals : List MVarId) : IO Format :=
if goals.isEmpty then
return "no goals"
else
ctx.runMetaM {} (return Std.Format.prefixJoin "\n" (← goals.mapM (Meta.ppGoal . inConv)))
ctx.runMetaM {} (return Std.Format.prefixJoin "\n" (← goals.mapM (Meta.ppGoal .)))
def TacticInfo.format (ctx : ContextInfo) (info : TacticInfo) : IO Format := do
let ctxB := { ctx with mctx := info.mctxBefore }
let ctxA := { ctx with mctx := info.mctxAfter }
let goalsBefore ← ctxB.ppGoals info.goalsBefore info.inConv
let goalsAfter ← ctxA.ppGoals info.goalsAfter info.inConv
let goalsBefore ← ctxB.ppGoals info.goalsBefore
let goalsAfter ← ctxA.ppGoals info.goalsAfter
return f!"Tactic @ {formatElabInfo ctx info.toElabInfo}\n{info.stx}\nbefore {goalsBefore}\nafter {goalsAfter}"
def MacroExpansionInfo.format (ctx : ContextInfo) (info : MacroExpansionInfo) : IO Format := do

View file

@ -39,8 +39,6 @@ structure Context where
main : MVarId
-- declaration name of the executing elaborator, used by `mkTacticInfo` to persist it in the info tree
elaborator : Name
-- `true` when in `conv` tactic mode. This flag is only used to pretty print the goals showing just the left-hand-side
inConv : Bool := false
structure State where
goals : List MVarId
@ -121,7 +119,6 @@ def mkTacticInfo (mctxBefore : MetavarContext) (goalsBefore : List MVarId) (stx
stx := stx
mctxAfter := (← getMCtx)
goalsAfter := (← getUnsolvedGoals)
inConv := (← read).inConv
}
def mkInitialTacticInfo (stx : Syntax) : TacticM (TacticM Info) := do

View file

@ -14,7 +14,7 @@ open Meta
def mkConvGoalFor (lhs : Expr) : MetaM (Expr × Expr) := do
let lhsType ← inferType lhs
let rhs ← mkFreshExprMVar lhsType
let targetNew ← mkEq lhs rhs
let targetNew := mkLHSGoal (← mkEq lhs rhs)
let newGoal ← mkFreshExprSyntheticOpaqueMVar targetNew
return (rhs, newGoal)
@ -23,7 +23,7 @@ def convert (lhs : Expr) (conv : TacticM Unit) : TacticM (Expr × Expr) := do
let savedGoals ← getGoals
try
setGoals [newGoal.mvarId!]
withReader (fun ctx => { ctx with inConv := true }) conv
conv
pruneSolvedGoals
for mvarId in (← getGoals) do
try
@ -52,7 +52,7 @@ def getRhs : TacticM Expr :=
/-- `⊢ lhs = rhs` ~~> `⊢ lhs' = rhs` using `h : lhs = lhs'`. -/
def updateLhs (lhs' : Expr) (h : Expr) : TacticM Unit := do
let rhs ← getRhs
let newGoal ← mkFreshExprSyntheticOpaqueMVar (← mkEq lhs' rhs)
let newGoal ← mkFreshExprSyntheticOpaqueMVar (mkLHSGoal (← mkEq lhs' rhs))
assignExprMVar (← getMainGoal) (← mkEqTrans h newGoal)
replaceMainGoal [newGoal.mvarId!]
@ -60,7 +60,7 @@ def updateLhs (lhs' : Expr) (h : Expr) : TacticM Unit := do
def changeLhs (lhs' : Expr) : TacticM Unit := do
let rhs ← getRhs
liftMetaTactic1 fun mvarId => do
replaceTargetDefEq mvarId (← mkEq lhs' rhs)
replaceTargetDefEq mvarId (mkLHSGoal (← mkEq lhs' rhs))
@[builtinTactic Lean.Parser.Tactic.Conv.skip] def evalSkip : Tactic := fun stx => do
liftMetaTactic1 fun mvarId => do

View file

@ -1038,6 +1038,22 @@ def annotation? (kind : Name) (e : Expr) : Option Expr :=
| Expr.mdata d b _ => if d.size == 1 && d.getBool kind false then some b else none
| _ => none
/--
Annotate `e` with the LHS annotation. The delaborator displays
expressions of the form `lhs = rhs` as `lhs` when they have this annotation.
-/
def mkLHSGoal (e : Expr) : Expr :=
mkAnnotation `_lhsGoal e
def isLHSGoal? (e : Expr) : Option Expr :=
match annotation? `_lhsGoal e with
| none => none
| some e =>
if e.isAppOfArity `Eq 3 then
some e.appFn!.appArg!
else
none
def mkFreshFVarId {m : Type → Type} [Monad m] [MonadNameGenerator m] : m FVarId :=
mkFreshId

View file

@ -144,20 +144,7 @@ end ToHide
private def addLine (fmt : Format) : Format :=
if fmt.isNil then fmt else fmt ++ Format.line
/--
Return the target type for the given goal.
If `inConv == true` and target type is an equality, then return the left-hand-side only. -/
def getGoalTarget (mvarDecl : MetavarDecl) (inConv : Bool) : MetaM Expr := do
let target ← instantiateMVars mvarDecl.type
if inConv then
if let some (_, lhs, _) ← matchEq? target then
instantiateMVars lhs
else
return target
else
return target
def ppGoal (mvarId : MVarId) (inConv := false) : MetaM Format := do
def ppGoal (mvarId : MVarId) : MetaM Format := do
match (← getMCtx).findDecl? mvarId with
| none => pure "unknown goal"
| some mvarDecl => do
@ -214,7 +201,7 @@ def ppGoal (mvarId : MVarId) (inConv := false) : MetaM Format := do
ppVars varNames prevType? fmt localDecl
let fmt ← pushPending varNames type? fmt
let fmt := addLine fmt
let typeFmt ← ppExpr (← getGoalTarget mvarDecl inConv)
let typeFmt ← ppExpr (← instantiateMVars mvarDecl.type)
let fmt := fmt ++ "⊢ " ++ Format.nest indent typeFmt
match mvarDecl.userName with
| Name.anonymous => pure fmt

View file

@ -446,6 +446,8 @@ def delabMData : Delab := do
`(.($s)) -- We only include the inaccessible annotation when we are delaborating patterns
else
return s
else if let some _ := isLHSGoal? (← getExpr) then
withMDataExpr <| withAppFn <| withAppArg <| delab
else
withMDataOptions delab

View file

@ -148,7 +148,7 @@ def getInteractiveGoals (p : Lsp.PlainGoalParams) : RequestM (RequestTask (Optio
let goals ← List.join <$> rs.mapM fun { ctxInfo := ci, tacticInfo := ti, useAfter := useAfter } =>
let ci := if useAfter then { ci with mctx := ti.mctxAfter } else { ci with mctx := ti.mctxBefore }
let goals := if useAfter then ti.goalsAfter else ti.goalsBefore
ci.runMetaM {} <| goals.mapM (fun g => Meta.withPPInaccessibleNames (Widget.goalToInteractive g ti.inConv))
ci.runMetaM {} <| goals.mapM (fun g => Meta.withPPInaccessibleNames (Widget.goalToInteractive g))
return some { goals := goals.toArray }
return none
@ -182,7 +182,7 @@ partial def getInteractiveTermGoal (p : Lsp.PlainTermGoalParams)
-- for binders, hide the last hypothesis (the binder itself)
let lctx' := if ti.isBinder then i.lctx.pop else i.lctx
let goal ← ci.runMetaM lctx' do
Meta.withPPInaccessibleNames <| Widget.goalToInteractive (← Meta.mkFreshExprMVar ty).mvarId! (inConv := false)
Meta.withPPInaccessibleNames <| Widget.goalToInteractive (← Meta.mkFreshExprMVar ty).mvarId!
let range := if let some r := i.range? then r.toLspRange text else ⟨p.position, p.position⟩
return some { goal with range }
return none

View file

@ -66,7 +66,7 @@ structure InteractiveGoals where
open Meta in
/-- A variant of `Meta.ppGoal` which preserves subexpression information for interactivity. -/
def goalToInteractive (mvarId : MVarId) (inConv : Bool) : MetaM InteractiveGoal := do
def goalToInteractive (mvarId : MVarId) : MetaM InteractiveGoal := do
let some mvarDecl ← (← getMCtx).findDecl? mvarId
| throwError "unknown goal {mvarId}"
let ppAuxDecls := pp.auxDecls.get (← getOptions)
@ -118,7 +118,7 @@ def goalToInteractive (mvarId : MVarId) (inConv : Bool) : MetaM InteractiveGoal
else
ppVars varNames prevType? hyps localDecl
let hyps ← pushPending varNames type? hyps
let goalTp ← getGoalTarget mvarDecl inConv
let goalTp ← instantiateMVars mvarDecl.type
let goalFmt ← exprToInteractive goalTp
let userName? := match mvarDecl.userName with
| Name.anonymous => none