|
| 1 | +/- |
| 2 | +Copyright (c) 2025 Lean FRO, LLC. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Joachim Breitner |
| 5 | +-/ |
| 6 | + |
| 7 | +module |
| 8 | +prelude |
| 9 | +public import Lean.Meta.Basic |
| 10 | +import Lean.Meta.Tactic.Delta |
| 11 | +import Lean.Meta.Tactic.Rewrite |
| 12 | +import Lean.Meta.Constructions.SparseCasesOn |
| 13 | +import Lean.Meta.Constructions.SparseCasesOnEq |
| 14 | +import Lean.Meta.HasNotBit |
| 15 | +import Lean.Meta.Tactic.Cases |
| 16 | + |
| 17 | +namespace Lean.Meta |
| 18 | + |
| 19 | +private def rewriteGoalUsingEq (goal : MVarId) (eq : Expr) (symm : Bool := false) : MetaM MVarId := do |
| 20 | + let rewriteResult ← goal.rewrite (←goal.getType) eq symm |
| 21 | + goal.replaceTargetEq rewriteResult.eNew rewriteResult.eqProof |
| 22 | + |
| 23 | +/-- |
| 24 | +Reduces a sparse casesOn applied to a concrete constructor. |
| 25 | +-/ |
| 26 | +public def reduceSparseCasesOn (mvarId : MVarId) : MetaM (Array MVarId) := do |
| 27 | + let some (_, lhs) ← matchEqHEqLHS? (← mvarId.getType) | throwError "Target not an equality" |
| 28 | + lhs.withApp fun f xs => do |
| 29 | + let .const matchDeclName _ := f | throwError "Not a const application" |
| 30 | + let some sparseCasesOnInfo ← getSparseCasesOnInfo matchDeclName | throwError "Not a sparse casesOn application" |
| 31 | + withTraceNode `Meta.Match.matchEqs (msg := (return m!"{exceptEmoji ·} splitSparseCasesOn")) do |
| 32 | + if xs.size < sparseCasesOnInfo.arity then |
| 33 | + throwError "Not enough arguments for sparse casesOn application" |
| 34 | + let majorIdx := sparseCasesOnInfo.majorPos |
| 35 | + let major := xs[majorIdx]! |
| 36 | + let some ctorInfo ← isConstructorApp'? major |
| 37 | + | throwError "Major premise is not a constructor application:{indentExpr major}" |
| 38 | + if sparseCasesOnInfo.insterestingCtors.contains ctorInfo.name then |
| 39 | + let mvarId' ← mvarId.modifyTargetEqLHS fun lhs => |
| 40 | + unfoldDefinition lhs |
| 41 | + return #[mvarId'] |
| 42 | + else |
| 43 | + let sparseCasesOnEqName ← getSparseCasesOnEq matchDeclName |
| 44 | + let eqProof := mkConst sparseCasesOnEqName lhs.getAppFn.constLevels! |
| 45 | + let eqProof := mkAppN eqProof lhs.getAppArgs[:sparseCasesOnInfo.arity] |
| 46 | + let eqProof := mkApp eqProof (← mkHasNotBitProof (mkRawNatLit ctorInfo.cidx) (← sparseCasesOnInfo.insterestingCtors.mapM (fun n => return (← getConstInfoCtor n).cidx))) |
| 47 | + let mvarId' ← rewriteGoalUsingEq mvarId eqProof |
| 48 | + return #[mvarId'] |
| 49 | + |
| 50 | +public def splitSparseCasesOn (mvarId : MVarId) : MetaM (Array MVarId) := do |
| 51 | + let some (_, lhs) ← matchEqHEqLHS? (← mvarId.getType) | throwError "Target not an equality" |
| 52 | + lhs.withApp fun f xs => do |
| 53 | + let .const matchDeclName _ := f | throwError "Not a const application" |
| 54 | + let some sparseCasesOnInfo ← getSparseCasesOnInfo matchDeclName | throwError "Not a sparse casesOn application" |
| 55 | + withTraceNode `Meta.Match.matchEqs (msg := (return m!"{exceptEmoji ·} splitSparseCasesOn")) do |
| 56 | + try |
| 57 | + trace[Meta.Match.matchEqs] "splitSparseCasesOn running on\n{mvarId}" |
| 58 | + if xs.size < sparseCasesOnInfo.arity then |
| 59 | + throwError "Not enough arguments for sparse casesOn application" |
| 60 | + let majorIdx := sparseCasesOnInfo.majorPos |
| 61 | + unless xs[majorIdx]!.isFVar do |
| 62 | + throwError "Major premise is not a free variable:{indentExpr xs[majorIdx]!}" |
| 63 | + let fvarId := xs[majorIdx]!.fvarId! |
| 64 | + let subgoals ← mvarId.cases fvarId (interestingCtors? := sparseCasesOnInfo.insterestingCtors) |
| 65 | + subgoals.mapM fun s => s.mvarId.withContext do |
| 66 | + if s.ctorName.isNone then |
| 67 | + unless s.fields.size = 1 do |
| 68 | + throwError "Unexpected number of fields for catch-all branch: {s.fields}" |
| 69 | + let sparseCasesOnEqName ← getSparseCasesOnEq matchDeclName |
| 70 | + let some (_, lhs) ← matchEqHEqLHS? (← s.mvarId.getType) | throwError "Target not an equality" |
| 71 | + let eqProof := mkConst sparseCasesOnEqName lhs.getAppFn.constLevels! |
| 72 | + let eqProof := mkAppN eqProof lhs.getAppArgs[:sparseCasesOnInfo.arity] |
| 73 | + let eqProof := mkApp eqProof s.fields[0]! |
| 74 | + rewriteGoalUsingEq s.mvarId eqProof |
| 75 | + else |
| 76 | + s.mvarId.modifyTargetEqLHS fun lhs => |
| 77 | + unfoldDefinition lhs |
| 78 | + catch e => |
| 79 | + trace[Meta.Match.matchEqs] "splitSparseCasesOn failed{indentD e.toMessageData}" |
| 80 | + throw e |
0 commit comments