feat: preparation for #7830 (#7955)

This PR adds the tactic implementation for #7830, before changing the
syntax after a stage0 update. It will allow optional RHSs in induction
cases.
This commit is contained in:
Kyle Miller 2025-04-13 23:22:04 -07:00 committed by GitHub
parent 48a9bfb73d
commit de25524dd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -49,36 +49,55 @@ private def altHasExplicitModifier (alt : Syntax) : Bool :=
private def getAltVars (alt : Syntax) : Array Syntax :=
let lhs := getFirstAltLhs alt
lhs[2].getArgs
private def hasAltRHS (alt : Syntax) : Bool :=
if !alt[1].isOfKind nullKind then
-- TODO(kmill) Bootstrapping workaround. Delete case in #7830.
true
else
alt[1].getNumArgs > 0
private def getAltRHS (alt : Syntax) : Syntax :=
alt[2]
if !alt[1].isOfKind nullKind then
-- TODO(kmill) Bootstrapping workaround. Delete case in #7830.
alt[2]
else
alt[1][1]
private def getAltDArrow (alt : Syntax) : Syntax :=
alt[1]
if !alt[1].isOfKind nullKind then
-- TODO(kmill) Bootstrapping workaround. Delete case in #7830.
alt[1]
else
alt[1][0]
-- Return true if `stx` is a term occurring in the RHS of the induction/cases tactic
def isHoleRHS (rhs : Syntax) : Bool :=
rhs.isOfKind ``Parser.Term.syntheticHole || rhs.isOfKind ``Parser.Term.hole
def evalAlt (mvarId : MVarId) (alt : Syntax) (addInfo : TermElabM Unit) : TacticM Unit :=
let rhs := getAltRHS alt
withCaseRef (getAltDArrow alt) rhs do
if isHoleRHS rhs then
addInfo
mvarId.withContext <| withTacticInfoContext rhs do
let mvarDecl ← mvarId.getDecl
let val ← elabTermEnsuringType rhs mvarDecl.type
mvarId.assign val
let gs' ← getMVarsNoDelayed val
tagUntaggedGoals mvarDecl.userName `induction gs'.toList
setGoals <| (← getGoals) ++ gs'.toList
else
let goals ← getGoals
try
def evalAlt (mvarId : MVarId) (alt : Syntax) (addInfo : TermElabM Unit) : TacticM Unit := do
if !hasAltRHS alt then
throwErrorAt alt "(internal error) RHS was not expanded"
else
let rhs := getAltRHS alt
withCaseRef (getAltDArrow alt) rhs do
if isHoleRHS rhs then
addInfo
let goals ← getGoals
setGoals [mvarId]
closeUsingOrAdmit <|
withTacticInfoContext (mkNullNode #[getAltLhses alt, getAltDArrow alt]) <|
(addInfo *> evalTactic rhs)
finally
setGoals goals
mvarId.withContext <| withTacticInfoContext (mkNullNode #[getAltLhses alt, rhs]) do
let mvarDecl ← mvarId.getDecl
let val ← elabTermEnsuringType rhs mvarDecl.type
mvarId.assign val
let gs' ← getMVarsNoDelayed val
tagUntaggedGoals mvarDecl.userName `induction gs'.toList
setGoals <| goals ++ gs'.toList
else
let goals ← getGoals
try
setGoals [mvarId]
closeUsingOrAdmit <|
withTacticInfoContext (mkNullNode #[getAltLhses alt, getAltDArrow alt]) <|
(addInfo *> evalTactic rhs)
finally
setGoals goals
/-!
Helper method for creating an user-defined eliminator/recursor application.
@ -531,12 +550,26 @@ private def withAltsOfOptInductionAlts (optInductionAlts : Syntax)
private def getOptPreTacOfOptInductionAlts (optInductionAlts : Syntax) : Syntax :=
if optInductionAlts.isNone then mkNullNode else optInductionAlts[0][1]
private def isMultiAlt (alt : Syntax) : Bool :=
alt[0].getNumArgs > 1
/--
Returns true if the `Lean.Parser.Tactic.inductionAlt` either has more than one alternative
or has no RHS.
-/
private def shouldExpandAlt (alt : Syntax) : Bool :=
-- TODO(kmill) enable second case in #7830
alt[0].getNumArgs > 1 || (false && 1 < alt.getNumArgs && alt[1].getNumArgs == 0)
/-- Return `some #[alt_1, ..., alt_n]` if `alt` has multiple LHSs. -/
private def expandMultiAlt? (alt : Syntax) : Option (Array Syntax) := Id.run do
if isMultiAlt alt then
/--
Returns `some #[alt_1, ..., alt_n]` if `alt` has multiple LHSs or if `alt` has no RHS.
If there is no RHS, it is filled in with a hole.
-/
private def expandAlt? (alt : Syntax) : Option (Array Syntax) := Id.run do
if shouldExpandAlt alt then
let alt :=
-- TODO(kmill) enable case in #7830
if false && 1 < alt.getNumArgs && alt[1].getNumArgs == 0 then
alt.setArg 1 <| mkNullNode #[mkAtomFrom alt "=>", mkHole alt]
else
alt
some <| alt[0].getArgs.map fun lhs => alt.setArg 0 (mkNullNode #[lhs])
else
none
@ -553,10 +586,10 @@ Remark: the `RHS` of alternatives with multi LHSs is copied.
-/
private def expandInductionAlts? (inductionAlts : Syntax) : Option Syntax := Id.run do
let alts := getAltsOfInductionAlts inductionAlts
if alts.any isMultiAlt then
if alts.any shouldExpandAlt then
let mut altsNew := #[]
for alt in alts do
if let some alt' := expandMultiAlt? alt then
if let some alt' := expandAlt? alt then
altsNew := altsNew ++ alt'
else
altsNew := altsNew.push alt