@@ -9,6 +9,110 @@ import Lean.Meta.Tactic.Cases
9
9
import Lean.Meta.Tactic.Simp.Main
10
10
11
11
namespace Lean.Meta
12
+
13
+ inductive SplitKind where
14
+ | ite | match | both
15
+
16
+ def SplitKind.considerIte : SplitKind → Bool
17
+ | .ite | .both => true
18
+ | _ => false
19
+
20
+ def SplitKind.considerMatch : SplitKind → Bool
21
+ | .match | .both => true
22
+ | _ => false
23
+
24
+ namespace FindSplitImpl
25
+
26
+ structure Context where
27
+ exceptionSet : ExprSet := {}
28
+ kind : SplitKind := .both
29
+
30
+ unsafe abbrev FindM := ReaderT Context $ StateT (PtrSet Expr) MetaM
31
+
32
+ private def isCandidate (env : Environment) (ctx : Context) (e : Expr) : Bool := Id.run do
33
+ if ctx.exceptionSet.contains e then
34
+ return false
35
+ if ctx.kind.considerIte && (e.isIte || e.isDIte) then
36
+ return !(e.getArg! 1 5 ).hasLooseBVars
37
+ if ctx.kind.considerMatch then
38
+ if let some info := isMatcherAppCore? env e then
39
+ let args := e.getAppArgs
40
+ for i in [info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
41
+ if args[i]!.hasLooseBVars then
42
+ return false
43
+ return true
44
+ return false
45
+
46
+ @[inline] unsafe def checkVisited (e : Expr) : OptionT FindM Unit := do
47
+ if (← get).contains e then
48
+ failure
49
+ modify fun s => s.insert e
50
+
51
+ unsafe def visit (e : Expr) : OptionT FindM Expr := do
52
+ checkVisited e
53
+ if isCandidate (← getEnv) (← read) e then
54
+ return e
55
+ else
56
+ -- We do not look for split candidates in proofs.
57
+ unless e.hasLooseBVars do
58
+ if (← isProof e) then
59
+ failure
60
+ match e with
61
+ | .lam _ _ b _ | .proj _ _ b -- We do not look for split candidates in the binder of lambdas.
62
+ | .mdata _ b => visit b
63
+ | .forallE _ d b _ => visit d <|> visit b -- We want to look for candidates at `A → B`
64
+ | .letE _ _ v b _ => visit v <|> visit b
65
+ | .app .. => visitApp? e
66
+ | _ => failure
67
+ where
68
+ visitApp? (e : Expr) : FindM (Option Expr) :=
69
+ e.withApp fun f args => do
70
+ let info ← getFunInfo f
71
+ for u : i in [0 :args.size] do
72
+ let arg := args[i]
73
+ if h : i < info.paramInfo.size then
74
+ let info := info.paramInfo[i]
75
+ unless info.isProp do
76
+ if info.isExplicit then
77
+ let some found ← visit arg | pure ()
78
+ return found
79
+ else
80
+ let some found ← visit arg | pure ()
81
+ return found
82
+ visit f
83
+
84
+ end FindSplitImpl
85
+
86
+ /-- Return an `if-then-else` or `match-expr` to split. -/
87
+ partial def findSplit? (e : Expr) (kind : SplitKind := .both) (exceptionSet : ExprSet := {}) : MetaM (Option Expr) := do
88
+ go (← instantiateMVars e)
89
+ where
90
+ go (e : Expr) : MetaM (Option Expr) := do
91
+ if let some target ← find? e then
92
+ if e.isIte || e.isDIte then
93
+ let cond := target.getArg! 1 5
94
+ -- Try to find a nested `if` in `cond`
95
+ return (← go cond).getD target
96
+ else
97
+ return some target
98
+ else
99
+ return none
100
+
101
+ find? (e : Expr) : MetaM (Option Expr) := do
102
+ let some candidate ← unsafe FindSplitImpl.visit e { kind, exceptionSet } |>.run' mkPtrSet
103
+ | return none
104
+ trace[split.debug] "candidate:{indentExpr candidate}"
105
+ return some candidate
106
+
107
+ /-- Return the condition and decidable instance of an `if` expression to case split. -/
108
+ private partial def findIfToSplit? (e : Expr) : MetaM (Option (Expr × Expr)) := do
109
+ if let some iteApp ← findSplit? e .ite then
110
+ let cond := iteApp.getArg! 1 5
111
+ let dec := iteApp.getArg! 2 5
112
+ return (cond, dec)
113
+ else
114
+ return none
115
+
12
116
namespace SplitIf
13
117
14
118
builtin_initialize ext : LazyInitExtension MetaM Simp.Context ←
@@ -21,7 +125,7 @@ builtin_initialize ext : LazyInitExtension MetaM Simp.Context ←
21
125
return {
22
126
simpTheorems := #[s]
23
127
congrTheorems := (← getSimpCongrTheorems)
24
- config := { Simp.neutralConfig with dsimp := false , implicitDefEqProofs := true }
128
+ config := { Simp.neutralConfig with dsimp := false }
25
129
}
26
130
27
131
/--
@@ -68,19 +172,9 @@ private def discharge? (numIndices : Nat) (useDecide : Bool) : Simp.Discharge :=
68
172
def mkDischarge? (useDecide := false ) : MetaM Simp.Discharge :=
69
173
return discharge? (← getLCtx).numIndices useDecide
70
174
71
- /-- Return the condition and decidable instance of an `if` expression to case split. -/
72
- private partial def findIfToSplit? (e : Expr) : Option (Expr × Expr) :=
73
- if let some iteApp := e.find? fun e => (e.isIte || e.isDIte) && !(e.getArg! 1 5 ).hasLooseBVars then
74
- let cond := iteApp.getArg! 1 5
75
- let dec := iteApp.getArg! 2 5
76
- -- Try to find a nested `if` in `cond`
77
- findIfToSplit? cond |>.getD (cond, dec)
78
- else
79
- none
80
-
81
- def splitIfAt? (mvarId : MVarId) (e : Expr) (hName? : Option Name) : MetaM (Option (ByCasesSubgoal × ByCasesSubgoal)) := do
175
+ def splitIfAt? (mvarId : MVarId) (e : Expr) (hName? : Option Name) : MetaM (Option (ByCasesSubgoal × ByCasesSubgoal)) := mvarId.withContext do
82
176
let e ← instantiateMVars e
83
- if let some (cond, decInst) := findIfToSplit? e then
177
+ if let some (cond, decInst) ← findIfToSplit? e then
84
178
let hName ← match hName? with
85
179
| none => mkFreshUserName `h
86
180
| some hName => pure hName
0 commit comments