Skip to content

Commit 82f200d

Browse files
committed
fix: have tauto use Lean.Meta.isProp rather than Lean.Expr.isProp (#5565)
`Lean.Expr.isProp` can't handle metavariables but `Lean.Meta.isProp` can. Bug reported by @negiizhao
1 parent b8fe59a commit 82f200d

File tree

3 files changed

+61
-22
lines changed

3 files changed

+61
-22
lines changed

Mathlib/Order/Circular.lean

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,17 @@ def Preorder.toCircularPreorder (α : Type _) [Preorder α] : CircularPreorder
425425
· exact Or.inr (Or.inr ⟨hca, hab.trans hbd⟩)
426426
sbtw_iff_btw_not_btw {a b c} := by
427427
simp_rw [lt_iff_le_not_le]
428-
have := le_trans a b c
429-
have := le_trans b c a
430-
have := le_trans c a b
431-
tauto
428+
have h1 := le_trans a b c
429+
have h2 := le_trans b c a
430+
have h3 := le_trans c a b
431+
-- Porting note: was `tauto`, but this is a much faster tactic proof
432+
revert h1 h2 h3
433+
generalize (a ≤ b) = p1
434+
generalize (b ≤ a) = p2
435+
generalize (a ≤ c) = p3
436+
generalize (c ≤ a) = p4
437+
generalize (b ≤ c) = p5
438+
by_cases p1 <;> by_cases p2 <;> by_cases p3 <;> by_cases p4 <;> by_cases p5 <;> simp [*]
432439
#align preorder.to_circular_preorder Preorder.toCircularPreorder
433440

434441
/-- The circular partial order obtained from "looping around" a partial order.

Mathlib/Tactic/Tauto.lean

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ initialize registerTraceClass `tauto
2929
def distribNotOnceAt (hypFVar : Expr) (g : MVarId) : MetaM AssertAfterResult := g.withContext do
3030
let .fvar fvarId := hypFVar | throwError "not fvar {hypFVar}"
3131
let h ← fvarId.getDecl
32-
let e : Q(Prop) ← (do guard (← inferType h.type).isProp; pure h.type)
32+
let e : Q(Prop) ← (do guard <| ← Meta.isProp h.type; pure h.type)
3333
let replace (p : Expr) := g.replace h.fvarId p
3434
match e with
3535
| ~q(¬ ($a : Prop) = $b) => do
@@ -92,25 +92,25 @@ Calls `distribNotAt` on the head of `state.fvars` up to `nIters` times, returnin
9292
early on failure.
9393
-/
9494
partial def distribNotAt (nIters : Nat) (state : DistribNotState) : MetaM DistribNotState :=
95-
match nIters, state.fvars with
96-
| 0, _ | _, [] => pure state
97-
| n + 1, fv::fvs => do
98-
try
99-
let result ← distribNotOnceAt fv state.currentGoal
100-
let newFVars := (mkFVar result.fvarId)::(fvs.map (fun x ↦ result.subst.apply x))
101-
distribNotAt n ⟨newFVars, result.mvarId⟩
102-
catch _ => pure state
95+
match nIters, state.fvars with
96+
| 0, _ | _, [] => pure state
97+
| n + 1, fv::fvs => do
98+
try
99+
let result ← distribNotOnceAt fv state.currentGoal
100+
let newFVars := mkFVar result.fvarId :: fvs.map (fun x ↦ result.subst.apply x)
101+
distribNotAt n ⟨newFVars, result.mvarId⟩
102+
catch _ => pure state
103103

104104
/--
105105
For each fvar in `fvars`, calls `distribNotAt` and carries along the resulting
106106
renamings.
107107
-/
108108
partial def distribNotAux (fvars : List Expr) (g : MVarId) : MetaM MVarId :=
109-
match fvars with
110-
| [] => pure g
111-
| _ => do
112-
let result ← distribNotAt 3 ⟨fvars, g⟩
113-
distribNotAux result.fvars.tail! result.currentGoal
109+
match fvars with
110+
| [] => pure g
111+
| _ => do
112+
let result ← distribNotAt 3 ⟨fvars, g⟩
113+
distribNotAux result.fvars.tail! result.currentGoal
114114

115115
/--
116116
Tries to apply de-Morgan-like rules on all hypotheses.
@@ -120,7 +120,7 @@ def distribNot : TacticM Unit := withMainContext do
120120
let mut fvars := []
121121
for h in ← getLCtx do
122122
if !h.isImplementationDetail then
123-
fvars := (mkFVar h.fvarId):: fvars
123+
fvars := mkFVar h.fvarId :: fvars
124124
liftMetaTactic' (distribNotAux fvars)
125125

126126
/-- Config for the `tauto` tactic. Currently empty. TODO: add `closer` option. -/
@@ -131,15 +131,17 @@ declare_config_elab elabConfig Config
131131

132132
/-- Matches propositions where we want to apply the `constructor` tactic
133133
in the core loop of `tauto`. -/
134-
def coreConstructorMatcher (e : Q(Prop)) : MetaM Bool := match e with
134+
def coreConstructorMatcher (e : Q(Prop)) : MetaM Bool :=
135+
match e with
135136
| ~q(_ ∧ _) => pure true
136137
| ~q(_ ↔ _) => pure true
137138
| ~q(True) => pure true
138139
| _ => pure false
139140

140141
/-- Matches propositions where we want to apply the `cases` tactic
141142
in the core loop of `tauto`. -/
142-
def casesMatcher (e : Q(Prop)) : MetaM Bool := match e with
143+
def casesMatcher (e : Q(Prop)) : MetaM Bool :=
144+
match e with
143145
| ~q(_ ∧ _) => pure true
144146
| ~q(_ ∨ _) => pure true
145147
| ~q(Exists _) => pure true
@@ -179,7 +181,8 @@ def tautoCore : TacticM Unit := do
179181

180182
/-- Matches propositions where we want to apply the `constructor` tactic in the
181183
finishing stage of `tauto`. -/
182-
def finishingConstructorMatcher (e : Q(Prop)) : MetaM Bool := match e with
184+
def finishingConstructorMatcher (e : Q(Prop)) : MetaM Bool :=
185+
match e with
183186
| ~q(_ ∧ _) => pure true
184187
| ~q(_ ↔ _) => pure true
185188
| ~q(Exists _) => pure true

test/Tauto.lean

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,35 @@ example (p q r : Prop) (h : ¬ p = q) (h' : r = q) : p ↔ ¬ r := by tauto
6969
example (p : Prop) : p → ¬ (p → ¬ p) := by tauto
7070
example (p : Prop) (em : p ∨ ¬ p) : ¬ (p ↔ ¬ p) := by tauto
7171

72+
-- reported at https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/weird.20tactic.20bug/near/370505747
73+
open Classical in
74+
example (hp : p) (hq : q) : (if p ∧ q then 1 else 0) = 1 := by
75+
-- split_ifs creates a hypothesis with a type that's a metavariable
76+
split_ifs
77+
· rfl
78+
· tauto
79+
80+
example (hp : p) (hq : q) (h : ¬ (p ∧ q)) : False := by
81+
-- causes `h'` to have a type that's a metavariable:
82+
have h' := h
83+
clear h
84+
tauto
85+
86+
example (p : Prop) (h : False) : p := by
87+
-- causes `h'` to have a type that's a metavariable:
88+
have h' := h
89+
clear h
90+
tauto
91+
92+
example (hp : p) (hq : q) : p ∧ q := by
93+
-- causes goal to have a type that's a metavariable:
94+
suffices h : ?foo by exact h
95+
tauto
96+
97+
-- Checking `tauto` can deal with annotations:
98+
example (hp : ¬ p) (hq : ¬ (q ↔ p) := by sorry) : q := by
99+
tauto
100+
72101
example (P : Nat → Prop) (n : Nat) : P n → n = 7 ∨ n = 0 ∨ ¬ (n = 7 ∨ n = 0) ∧ P n :=
73102
by tauto
74103

0 commit comments

Comments
 (0)