Zulip Chat Archive

Stream: mathlib4

Topic: split_ifs


Dan Velleman (Jun 17 2024 at 16:25):

Can someone explain these examples:

import Mathlib.tactic

example (u : Nat) (h : u = u) : (if u = u then 0 else 1) = 0 := by
  split_ifs
  rfl
  done

example (u : Nat) : (if u = u then 0 else 1) = 0 := by
  have h : u = u := by rfl
  split_ifs
  rfl; rfl
  done

In the first proof, after the split_ifs the goal is 0 = 0, and rfl completes the proof. In the second proof, after split_ifs there are two 0 = 0 goals, one with the additional hypothesis u = u and one with the additional hypothesis ¬u = u, so one needs two invocations of rfl to complete the proof. Why the difference? The tactic state when split_ifs is invoked appears to be the same in the two examples.

Jireh Loreaux (Jun 17 2024 at 16:44):

This seems like a missing withMainContext in the implementation of split_ifs to me, but I'm not super knowledgable about these things.

Jireh Loreaux (Jun 17 2024 at 16:44):

If you do show_term split_ifs you can see the difference in what it produces.

Ruben Van de Velde (Jun 17 2024 at 16:46):

I think split_ifs looks for hypotheses that match the condition to avoid creating pointless subgoals

Jireh Loreaux (Jun 17 2024 at 16:47):

Right, but it should have found it in the second example, unless it didn't get the new context after the have

Ruben Van de Velde (Jun 17 2024 at 16:47):

Oh sorry, I missed the have

Jireh Loreaux (Jun 17 2024 at 17:36):

@David Renshaw since you ported this tactic, maybe you see the problem here? It's not immediately obvious to me.

Damiano Testa (Jun 17 2024 at 21:08):

Exceedingly often, weird tactic behaviour after have is due to the presence of metadata introduced by the have.

Damiano Testa (Jun 17 2024 at 21:15):

A quick look at the tactic makes me wonder whether there are a couple of whnfR missing in getSplitCandidates, around the getMainTargets, but I'm not at a computer to be able to check.

David Renshaw (Jun 18 2024 at 00:16):

This makes the behavior of the two versions match:

diff --git a/Mathlib/Tactic/SplitIfs.lean b/Mathlib/Tactic/SplitIfs.lean
index d41c91ddab..7a408e2004 100644
--- a/Mathlib/Tactic/SplitIfs.lean
+++ b/Mathlib/Tactic/SplitIfs.lean
@@ -102,9 +102,11 @@ private def getNextName (hNames: IO.Ref (List (TSyntax `Lean.binderIdent))) : Me
 -/
 private def valueKnown (cond : Expr) : TacticM Bool := do
   let hTypes ← (((← getLCtx).getFVarIds.map mkFVar).mapM inferType : MetaM _)
-  let hTypes := hTypes.toList
   let not_cond := mkApp (mkConst `Not) cond
-  return (hTypes.contains cond) || (hTypes.contains not_cond)
+  for ht in hTypes do
+    if ← isDefEq cond ht then return true
+    if ← isDefEq not_cond ht then return true
+  return false

David Renshaw (Jun 18 2024 at 00:17):

(and causes some breakage in mathlib)

David Renshaw (Jun 18 2024 at 00:31):

Just doing instantiateMVars seems to works better...

David Renshaw (Jun 18 2024 at 00:39):

diff --git a/Mathlib/Tactic/SplitIfs.lean b/Mathlib/Tactic/SplitIfs.lean
index d41c91ddab..13d9944826 100644
--- a/Mathlib/Tactic/SplitIfs.lean
+++ b/Mathlib/Tactic/SplitIfs.lean
@@ -101,10 +101,12 @@ private def getNextName (hNames: IO.Ref (List (TSyntax `Lean.binderIdent))) : Me
 /-- Returns `true` if the condition or its negation already appears as a hypothesis.
 -/
 private def valueKnown (cond : Expr) : TacticM Bool := do
-  let hTypes ← (((← getLCtx).getFVarIds.map mkFVar).mapM inferType : MetaM _)
-  let hTypes := hTypes.toList
   let not_cond := mkApp (mkConst `Not) cond
-  return (hTypes.contains cond) || (hTypes.contains not_cond)
+  for h in ← getLocalHyps do
+    let ty ← instantiateMVars (← inferType h)
+    if cond == ty then return true
+    if not_cond == ty then return true
+  return false

David Renshaw (Jun 18 2024 at 01:06):

I opened a pull request with a fix:
#13916


Last updated: May 02 2025 at 03:31 UTC