This PR replaces all usages of `[:]` slice notation in `src` with the new `[...]` notation in production code, tests and comments. The underlying implementation of the `Subarray` functions stays the same. Notation cheat sheet: * `*...*` is the doubly-unbounded range. * `*...a` or `*...<a` contains all elements that are less than `a`. * `*...=a` contains all elements that are less than or equal to `a`. * `a...*` contains all elements that are greater than or equal to `a`. * `a...b` or `a...<b` contains all elements that are greater than or equal to `a` and less than `b`. * `a...=b` contains all elements that are greater than or equal to `a` and less than or equal to `b`. * `a<...*` contains all elements that are greater than `a`. * `a<...b` or `a<...<b` contains all elements that are greater than `a` and less than `b`. * `a<...=b` contains all elements that are greater than `a` and less than or equal to `b`. Benchmarks have shown that importing the iterator-backed parts of the polymorphic slice library in `Init` impacts build performance. This PR avoids this problem by separating those parts of the library that do not rely on iterators from those those that do. Whereever the new slice notation is used, only the iterator-independent files are imported.
443 lines
20 KiB
Text
443 lines
20 KiB
Text
/-
|
||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Leonardo de Moura, Joachim Breitner
|
||
-/
|
||
|
||
prelude
|
||
import Lean.Meta.Match
|
||
import Lean.Meta.InferType
|
||
import Lean.Meta.Check
|
||
import Lean.Meta.Tactic.Split
|
||
|
||
namespace Lean.Meta.MatcherApp
|
||
|
||
/-- Auxiliary function for MatcherApp.addArg -/
|
||
private partial def updateAlts (unrefinedArgType : Expr) (typeNew : Expr) (altNumParams : Array Nat) (alts : Array Expr) (refined : Bool) (i : Nat) : MetaM (Array Nat × Array Expr) := do
|
||
if h : i < alts.size then
|
||
let alt := alts[i]
|
||
let numParams := altNumParams[i]!
|
||
let typeNew ← whnfD typeNew
|
||
match typeNew with
|
||
| Expr.forallE _ d b _ =>
|
||
let (alt, refined) ← forallBoundedTelescope d (some numParams) fun xs d => do
|
||
let alt ← try instantiateLambda alt xs catch _ => throwError "unexpected matcher application, insufficient number of parameters in alternative"
|
||
forallBoundedTelescope d (some 1) fun x _ => do
|
||
let alt ← mkLambdaFVars x alt -- x is the new argument we are adding to the alternative
|
||
let refined ← if refined then
|
||
pure refined
|
||
else
|
||
pure <| !(← isDefEq unrefinedArgType (← inferType x[0]!))
|
||
return (← mkLambdaFVars xs alt, refined)
|
||
updateAlts unrefinedArgType (b.instantiate1 alt) (altNumParams.set! i (numParams+1)) (alts.set i alt) refined (i+1)
|
||
| _ => throwError "unexpected type at MatcherApp.addArg"
|
||
else
|
||
if refined then
|
||
return (altNumParams, alts)
|
||
else
|
||
throwError "failed to add argument to matcher application, argument type was not refined by `casesOn`"
|
||
|
||
/--
|
||
Given
|
||
- matcherApp `match_i As (fun xs => motive[xs]) discrs (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining`, and
|
||
- expression `e : B[discrs]`,
|
||
Construct the term
|
||
`match_i As (fun xs => B[xs] -> motive[xs]) discrs (fun ys_1 (y : B[C_1[ys_1]]) => alt_1) ... (fun ys_n (y : B[C_n[ys_n]]) => alt_n) e remaining`.
|
||
|
||
We only abstract discriminants that are fvars. We used to use `kabstract` to abstract all
|
||
discriminants from `B[discrs]`, but that changes the type of the arg in ways that make it no
|
||
longer compatible with the original recursive function (issue #7322).
|
||
|
||
If this is still not great, then we could try to use `kabstract`, but only on the last parameter
|
||
of the `arg` (the termination proof obligation).
|
||
|
||
This method assumes
|
||
- the `matcherApp.motive` is a lambda abstraction where `xs.size == discrs.size`
|
||
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
|
||
|
||
This is used in `Lean.Elab.PreDefinition.WF.Fix` when replacing recursive calls with calls to
|
||
the argument provided by `fix` to refine type of the local variable used for recursive calls,
|
||
which may mention `major`. See there for how to use this function.
|
||
-/
|
||
def addArg (matcherApp : MatcherApp) (e : Expr) : MetaM MatcherApp :=
|
||
lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do
|
||
unless motiveArgs.size == matcherApp.discrs.size do
|
||
-- This error can only happen if someone implemented a transformation that rewrites the motive created by `mkMatcher`.
|
||
throwError "unexpected matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
|
||
let eType ← inferType e
|
||
let eTypeAbst := matcherApp.discrs.size.foldRev (init := eType) fun i _ eTypeAbst =>
|
||
let discr := matcherApp.discrs[i]
|
||
if discr.isFVar then
|
||
let motiveArg := motiveArgs[i]!
|
||
eTypeAbst.replaceFVar discr motiveArg
|
||
else
|
||
eTypeAbst
|
||
let motiveBody ← mkArrow eTypeAbst motiveBody
|
||
let matcherLevels ← match matcherApp.uElimPos? with
|
||
| none => pure matcherApp.matcherLevels
|
||
| some pos =>
|
||
let uElim ← getLevel motiveBody
|
||
pure <| matcherApp.matcherLevels.set! pos uElim
|
||
let motive ← mkLambdaFVars motiveArgs motiveBody
|
||
-- Construct `aux` `match_i As (fun xs => B[xs] → motive[xs]) discrs`, and infer its type `auxType`.
|
||
-- We use `auxType` to infer the type `B[C_i[ys_i]]` of the new argument in each alternative.
|
||
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) matcherApp.params
|
||
let aux := mkApp aux motive
|
||
let aux := mkAppN aux matcherApp.discrs
|
||
unless (← isTypeCorrect aux) do
|
||
throwError "failed to add argument to matcher application, type error when constructing the new motive"
|
||
let auxType ← inferType aux
|
||
let (altNumParams, alts) ← updateAlts eType auxType matcherApp.altNumParams matcherApp.alts false 0
|
||
return { matcherApp with
|
||
matcherLevels := matcherLevels,
|
||
motive := motive,
|
||
alts := alts,
|
||
altNumParams := altNumParams,
|
||
remaining := #[e] ++ matcherApp.remaining
|
||
}
|
||
|
||
/-- Similar to `MatcherApp.addArg`, but returns `none` on failure. -/
|
||
def addArg? (matcherApp : MatcherApp) (e : Expr) : MetaM (Option MatcherApp) :=
|
||
try
|
||
return some (← matcherApp.addArg e)
|
||
catch _ =>
|
||
return none
|
||
|
||
|
||
/-- Given
|
||
- matcherApp `match_i As (fun xs => motive[xs]) discrs (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining`, and
|
||
- a expression `B[discrs]` (which may not be a type, e.g. `n : Nat`),
|
||
returns the expressions `fun ys_1 ... ys_i => B[C_1[ys_1]] ... B[C_n[ys_n]]`,
|
||
|
||
This method assumes
|
||
- the `matcherApp.motive` is a lambda abstraction where `xs.size == discrs.size`
|
||
- each alternative is a lambda abstraction where `ys_i.size == matcherApp.altNumParams[i]`
|
||
|
||
This is similar to `MatcherApp.addArg` when you only have an expression to
|
||
refined, and not a type with a value.
|
||
|
||
This is used in `Lean.Elab.PreDefinition.WF.GuessFix` when constructing the context of recursive
|
||
calls to refine the functions' parameter, which may mention `major`.
|
||
See there for how to use this function.
|
||
-/
|
||
def refineThrough (matcherApp : MatcherApp) (e : Expr) : MetaM (Array Expr) :=
|
||
lambdaTelescope matcherApp.motive fun motiveArgs _motiveBody => do
|
||
unless motiveArgs.size == matcherApp.discrs.size do
|
||
-- This error can only happen if someone implemented a transformation that rewrites the motive created by `mkMatcher`.
|
||
throwError "failed to transfer argument through matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
|
||
|
||
let eAbst ← matcherApp.discrs.size.foldRevM (init := e) fun i _ eAbst => do
|
||
let motiveArg := motiveArgs[i]!
|
||
let discr := matcherApp.discrs[i]
|
||
let eTypeAbst ← kabstract eAbst discr
|
||
return eTypeAbst.instantiate1 motiveArg
|
||
-- Let's create something that’s a `Sort` and mentions `e`
|
||
-- (recall that `e` itself possibly isn't a type),
|
||
-- by writing `e = e`, so that we can use it as a motive
|
||
let eEq ← mkEq eAbst eAbst
|
||
|
||
let matcherLevels ← match matcherApp.uElimPos? with
|
||
| none => pure matcherApp.matcherLevels
|
||
| some pos =>
|
||
pure <| matcherApp.matcherLevels.set! pos levelZero
|
||
let motive ← mkLambdaFVars motiveArgs eEq
|
||
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) matcherApp.params
|
||
let aux := mkApp aux motive
|
||
let aux := mkAppN aux matcherApp.discrs
|
||
unless (← isTypeCorrect aux) do
|
||
throwError "failed to transfer argument through matcher application, type error when constructing the new motive"
|
||
let auxType ← inferType aux
|
||
forallTelescope auxType fun altAuxs _ => do
|
||
let altAuxTys ← altAuxs.mapM (inferType ·)
|
||
(Array.zip matcherApp.altNumParams altAuxTys).mapM fun (altNumParams, altAuxTy) => do
|
||
forallBoundedTelescope altAuxTy altNumParams fun fvs body => do
|
||
unless fvs.size = altNumParams do
|
||
throwError "failed to transfer argument through matcher application, alt type must be telescope with #{altNumParams} arguments"
|
||
-- extract type from our synthetic equality
|
||
let body := body.getArg! 2
|
||
-- and abstract over the parameters of the alternatives, so that we can safely pass the Expr out
|
||
mkLambdaFVars fvs body
|
||
|
||
/-- A non-failing version of `MatcherApp.refineThrough` -/
|
||
def refineThrough? (matcherApp : MatcherApp) (e : Expr) :
|
||
MetaM (Option (Array Expr)) :=
|
||
try
|
||
return some (← matcherApp.refineThrough e)
|
||
catch _ =>
|
||
return none
|
||
|
||
|
||
private def withUserNamesImpl {α} (fvars : Array Expr) (names : Array Name) (k : MetaM α) : MetaM α := do
|
||
let lctx := (Array.zip fvars names).foldl (init := ← (getLCtx)) fun lctx (fvar, name) =>
|
||
lctx.setUserName fvar.fvarId! name
|
||
withLCtx' lctx k
|
||
|
||
/--
|
||
Sets the user name of the FVars in the local context according to the given array of names.
|
||
|
||
If they differ in size the shorter size wins.
|
||
-/
|
||
def withUserNames {n} [MonadControlT MetaM n] [Monad n]
|
||
{α} (fvars : Array Expr) (names : Array Name) (k : n α) : n α := do
|
||
mapMetaM (withUserNamesImpl fvars names) k
|
||
|
||
/-
|
||
`Match.forallAltTelescope` lifted to a monad transformer
|
||
(and only passing those arguments that we care about below)
|
||
-/
|
||
private def forallAltTelescope'
|
||
{n} [Monad n] [MonadControlT MetaM n]
|
||
{α} (origAltType : Expr) (numParams numDiscrEqs : Nat)
|
||
(k : Array Expr → Array Expr → n α) : n α := do
|
||
map2MetaM (fun k =>
|
||
Match.forallAltVarsTelescope origAltType numParams numDiscrEqs
|
||
fun ys args _mask _bodyType => k ys args
|
||
) k
|
||
|
||
/--
|
||
Performs a possibly type-changing transformation to a `MatcherApp`.
|
||
|
||
* `onParams` is run on each parameter and discriminant
|
||
* `onMotive` runs on the body of the motive, and is passed the motive parameters
|
||
(one for each `MatcherApp.discrs`)
|
||
* `onAlt` runs on each alternative, and is passed the expected type of the alternative,
|
||
as inferred from the motive
|
||
* `onRemaining` runs on the remaining arguments (and may change their number)
|
||
|
||
If `useSplitter` is true, the matcher is replaced with the splitter.
|
||
NB: Not all operations on `MatcherApp` can handle one `matcherName` is a splitter.
|
||
|
||
If `addEqualities` is true, then equalities connecting the discriminant to the parameters of the
|
||
alternative (like in `match h : x with …`) are be added, if not already there.
|
||
|
||
This function works even if the type of alternatives do *not* fit the inferred type. This
|
||
allows you to post-process the `MatcherApp` with `MatcherApp.inferMatchType`, which will
|
||
infer a type, given all the alternatives.
|
||
-/
|
||
def transform
|
||
{n} [MonadLiftT MetaM n] [MonadControlT MetaM n] [Monad n] [MonadError n] [MonadEnv n] [MonadLog n]
|
||
[AddMessageContext n] [MonadOptions n]
|
||
(matcherApp : MatcherApp)
|
||
(useSplitter := false)
|
||
(addEqualities : Bool := false)
|
||
(onParams : Expr → n Expr := pure)
|
||
(onMotive : Array Expr → Expr → n Expr := fun _ e => pure e)
|
||
(onAlt : Nat → Expr → Expr → n Expr := fun _ _ e => pure e)
|
||
(onRemaining : Array Expr → n (Array Expr) := pure) :
|
||
n MatcherApp := do
|
||
|
||
-- We also handle CasesOn applications here, and need to treat them specially in a
|
||
-- few places.
|
||
-- TODO: Expand MatcherApp with the necessary fields to make this more uniform
|
||
-- (in particular, include discrEq and whether there is a splitter)
|
||
let isCasesOn := isCasesOnRecursor (← getEnv) matcherApp.matcherName
|
||
|
||
let numDiscrEqs ←
|
||
if isCasesOn then pure 0 else
|
||
match ← getMatcherInfo? matcherApp.matcherName with
|
||
| some info => pure info.getNumDiscrEqs
|
||
| none => throwError "matcher {matcherApp.matcherName} has no MatchInfo found"
|
||
|
||
let params' ← matcherApp.params.mapM onParams
|
||
let discrs' ← matcherApp.discrs.mapM onParams
|
||
|
||
let (motive', uElim, addHEqualities) ← lambdaTelescope matcherApp.motive fun motiveArgs motiveBody => do
|
||
unless motiveArgs.size == matcherApp.discrs.size do
|
||
throwError "unexpected matcher application, motive must be lambda expression with #{matcherApp.discrs.size} arguments"
|
||
let mut motiveBody' ← onMotive motiveArgs motiveBody
|
||
|
||
-- Prepend `(x = e) →` or `(x ≍ e) → ` to the motive when an equality is requested
|
||
-- and not already present, and remember whether we added an Eq or a HEq
|
||
let mut addHEqualities : Array (Option Bool) := #[]
|
||
for arg in motiveArgs, discr in discrs', di in matcherApp.discrInfos do
|
||
if addEqualities && di.hName?.isNone then
|
||
if ← isProof arg then
|
||
addHEqualities := addHEqualities.push none
|
||
else
|
||
let heq ← mkEqHEq discr arg
|
||
motiveBody' ← liftMetaM <| mkArrow heq motiveBody'
|
||
addHEqualities := addHEqualities.push heq.isHEq
|
||
else
|
||
addHEqualities := addHEqualities.push none
|
||
|
||
return (← mkLambdaFVars motiveArgs motiveBody', ← getLevel motiveBody', addHEqualities)
|
||
|
||
let matcherLevels ← match matcherApp.uElimPos? with
|
||
| none => pure matcherApp.matcherLevels
|
||
| some pos => pure <| matcherApp.matcherLevels.set! pos uElim
|
||
|
||
-- We pass `Eq.refl`s for all the equations we added as extra arguments
|
||
-- (and count them along the way)
|
||
let mut remaining' := #[]
|
||
let mut extraEqualities : Nat := 0
|
||
for discr in discrs'.reverse, b in addHEqualities.reverse do
|
||
match b with
|
||
| none => pure ()
|
||
| some is_heq =>
|
||
remaining' := remaining'.push (← (if is_heq then mkHEqRefl else mkEqRefl) discr)
|
||
extraEqualities := extraEqualities + 1
|
||
|
||
if useSplitter && !isCasesOn then
|
||
let aux1 := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) params'
|
||
let aux1 := mkApp aux1 motive'
|
||
let aux1 := mkAppN aux1 discrs'
|
||
unless (← isTypeCorrect aux1) do
|
||
prependError m!"failed to transform matcher, type error when constructing new pre-splitter motive:{indentExpr aux1}\nfailed with" do
|
||
check aux1
|
||
let origAltTypes ← inferArgumentTypesN matcherApp.alts.size aux1
|
||
|
||
-- We replace the matcher with the splitter
|
||
let matchEqns ← Match.getEquationsFor matcherApp.matcherName
|
||
let splitter := matchEqns.splitterName
|
||
|
||
let aux2 := mkAppN (mkConst splitter matcherLevels.toList) params'
|
||
let aux2 := mkApp aux2 motive'
|
||
let aux2 := mkAppN aux2 discrs'
|
||
unless (← isTypeCorrect aux2) do
|
||
prependError m!"failed to transform matcher, type error when constructing splitter motive:{indentExpr aux2}\nfailed with" do
|
||
check aux2
|
||
let altTypes ← inferArgumentTypesN matcherApp.alts.size aux2
|
||
|
||
let mut alts' := #[]
|
||
for altIdx in [:matcherApp.alts.size],
|
||
alt in matcherApp.alts,
|
||
numParams in matcherApp.altNumParams,
|
||
splitterNumParams in matchEqns.splitterAltNumParams,
|
||
origAltType in origAltTypes,
|
||
altType in altTypes do
|
||
let alt' ← forallAltTelescope' origAltType (numParams - numDiscrEqs) 0 fun ys args => do
|
||
let altType ← instantiateForall altType ys
|
||
-- The splitter inserts its extra parameters after the first ys.size parameters, before
|
||
-- the parameters for the numDiscrEqs
|
||
forallBoundedTelescope altType (splitterNumParams - ys.size) fun ys2 altType => do
|
||
forallBoundedTelescope altType numDiscrEqs fun ys3 altType => do
|
||
forallBoundedTelescope altType extraEqualities fun ys4 altType => do
|
||
let alt ← try instantiateLambda alt (args ++ ys3)
|
||
catch _ => throwError "unexpected matcher application, insufficient number of parameters in alternative"
|
||
let alt' ← onAlt altIdx altType alt
|
||
mkLambdaFVars (ys ++ ys2 ++ ys3 ++ ys4) alt'
|
||
alts' := alts'.push alt'
|
||
|
||
remaining' := remaining' ++ (← onRemaining matcherApp.remaining)
|
||
|
||
return { matcherApp with
|
||
matcherName := splitter
|
||
matcherLevels := matcherLevels
|
||
params := params'
|
||
motive := motive'
|
||
discrs := discrs'
|
||
altNumParams := matchEqns.splitterAltNumParams.map (· + extraEqualities)
|
||
alts := alts'
|
||
remaining := remaining'
|
||
}
|
||
else
|
||
let aux := mkAppN (mkConst matcherApp.matcherName matcherLevels.toList) params'
|
||
let aux := mkApp aux motive'
|
||
let aux := mkAppN aux discrs'
|
||
unless (← isTypeCorrect aux) do
|
||
logError m!"failed to transform matcher, type error when constructing new motive:{indentExpr aux}"
|
||
check aux
|
||
let altTypes ← inferArgumentTypesN matcherApp.alts.size aux
|
||
|
||
let mut alts' := #[]
|
||
for altIdx in [:matcherApp.alts.size],
|
||
alt in matcherApp.alts,
|
||
numParams in matcherApp.altNumParams,
|
||
altType in altTypes do
|
||
let alt' ← forallBoundedTelescope altType numParams fun xs altType => do
|
||
forallBoundedTelescope altType extraEqualities fun ys4 altType => do
|
||
-- we should try to preserve the variable names in the alternative
|
||
let names ← lambdaTelescope alt fun xs _ => xs.mapM (·.fvarId!.getUserName)
|
||
withUserNames xs names do
|
||
let alt ← instantiateLambda alt xs
|
||
let alt' ← onAlt altIdx altType alt
|
||
mkLambdaFVars (xs ++ ys4) alt'
|
||
alts' := alts'.push alt'
|
||
|
||
remaining' := remaining' ++ (← onRemaining matcherApp.remaining)
|
||
|
||
return { matcherApp with
|
||
matcherLevels := matcherLevels
|
||
params := params'
|
||
motive := motive'
|
||
discrs := discrs'
|
||
altNumParams := matcherApp.altNumParams.map (· + extraEqualities)
|
||
alts := alts'
|
||
remaining := remaining'
|
||
}
|
||
|
||
|
||
|
||
/--
|
||
Given a `MatcherApp`, replaces the motive with one that is inferred from the actual types of the
|
||
alternatives.
|
||
|
||
For example, given
|
||
```
|
||
(match (motive := Nat → Unit → ?) n with
|
||
0 => 1
|
||
_ => true) ()
|
||
```
|
||
(for any `?`; the motive’s result type be ignored) will give this type
|
||
```
|
||
(match n with
|
||
| 0 => Nat
|
||
| _ => Bool)
|
||
```
|
||
|
||
The given `MatcherApp` must not use a splitter in `matcherName`.
|
||
The resulting expression *will* use the splitter corresponding to `matcherName` (this is necessary
|
||
for the construction).
|
||
|
||
Internally, this needs to reduce the matcher in a given branch; this is done using
|
||
`Split.simpMatchTarget`.
|
||
-/
|
||
def inferMatchType (matcherApp : MatcherApp) : MetaM MatcherApp := do
|
||
-- In matcherApp.motive, replace the (dummy) matcher body with a type
|
||
-- derived from the inferred types of the alternatives
|
||
let nExtra := matcherApp.remaining.size
|
||
matcherApp.transform (useSplitter := true)
|
||
(onMotive := fun motiveArgs body => do
|
||
let extraParams ← arrowDomainsN nExtra body
|
||
let propMotive ← mkLambdaFVars motiveArgs (.sort levelZero)
|
||
let propAlts ← matcherApp.alts.mapM fun termAlt =>
|
||
lambdaTelescope termAlt fun xs termAltBody => do
|
||
-- We have alt parameters and parameters corresponding to the extra args
|
||
let xs1 := xs[*...(xs.size - nExtra)]
|
||
let xs2 := xs[(xs.size - nExtra)...xs.size]
|
||
-- logInfo m!"altIH: {xs} => {altIH}"
|
||
let altType ← inferType termAltBody
|
||
for x in xs2 do
|
||
if altType.hasAnyFVar (· == x.fvarId!) then
|
||
throwError "Type {altType} of alternative {termAlt} still depends on {x}"
|
||
-- logInfo m!"altIH type: {altType}"
|
||
mkLambdaFVars xs1 altType
|
||
let matcherLevels ← match matcherApp.uElimPos? with
|
||
| none => pure matcherApp.matcherLevels
|
||
| some pos => pure <| matcherApp.matcherLevels.set! pos levelOne
|
||
let typeMatcherApp := { matcherApp with
|
||
motive := propMotive
|
||
matcherLevels := matcherLevels
|
||
discrs := motiveArgs
|
||
alts := propAlts
|
||
remaining := #[]
|
||
}
|
||
mkArrowN extraParams typeMatcherApp.toExpr
|
||
)
|
||
(onAlt := fun _altIdx expAltType alt => do
|
||
let altType ← inferType alt
|
||
let eq ← mkEq expAltType altType
|
||
let proof ← mkFreshExprSyntheticOpaqueMVar eq
|
||
let goal := proof.mvarId!
|
||
-- logInfo m!"Goal: {goal}"
|
||
let goal ← Split.simpMatchTarget goal
|
||
-- logInfo m!"Goal after splitting: {goal}"
|
||
try
|
||
goal.refl
|
||
catch _ =>
|
||
logInfo m!"Cannot close goal after splitting: {goal}"
|
||
goal.admit
|
||
mkEqMPR proof alt
|
||
)
|
||
|
||
end Lean.Meta.MatcherApp
|