Zulip Chat Archive
Stream: general
Topic: Clearing obvious contradictions in cases of goals
l1mey (Sep 10 2025 at 05:20):
For context, poison and bitvec are part of the inductive type iN, which is:
/--
LLVM-style integers with poison value.
-/
inductive iN (bits : Nat) : Type where
| bitvec : BitVec bits → iN bits
| poison : iN bits
I'm currently in the middle of a proof that has a couple obvious contradictions in the cases that simp hasn't cleaned out. Things like heq✝ : poison = bitvec b✝ and heq✝ : poison = bitvec a✝ in the first and second case should cause both of these cases to clear, but right now simp isn't doing much.
theorem comm_nsw {n} (x y : iN n)
: x +nsw y <~> y +nsw x := by
simp [iN_unwrap_poison]
repeat' split; <;> try simp
/-
case h_3
n : Nat
x✝³ x✝² x✝¹ x✝ : iN n
a✝ b✝ : BitVec n
heq✝ : poison = bitvec b✝
⊢ poison <~> iN.addNswBV a✝ b✝
case h_3
n : Nat
x✝⁴ x✝³ x✝² x✝¹ : iN n
a✝ b✝ : BitVec n
heq✝ : poison = bitvec a✝
x✝ : bitvec b✝ = poison → False
⊢ poison <~> iN.addNswBV a✝ b✝
case h_3
n : Nat
x✝¹ x✝ : iN n
a✝ b✝ : BitVec n
⊢ iN.addNswBV a✝ b✝ <~> iN.addNswBV b✝ a✝
-/
I've created this simp lemma and it didn't work, but I never expected it to:
@[simp]
theorem iN_contra {n} {a : BitVec n}
{h : (poison : iN n) = (bitvec a : iN n)} : False := by
cases h
l1mey (Sep 10 2025 at 06:05):
Using a custom tactic, I'm able to extract out the contradictions:
elab "clear_poison_eq" : tactic => do
withMainContext do
let goals ← getGoals
goals.forM $ fun goal => do
let ctx ← getLCtx
ctx.forM (fun (decl : Lean.LocalDecl) => do
-- local decl: name: heq._@.theorems.iN_normal.tilde._hyg.67
-- expr: _uniq.1235
-- type: Eq.{1} (iN _uniq.221) (iN.poison _uniq.221) (iN.bitvec _uniq.221 _uniq.1202)
let declExpr := decl.toExpr
let declType ← Lean.Meta.inferType declExpr
let (fn, args) := declType.getAppFnArgs
let isPoison (e : Expr) : Bool :=
match e.getAppFn.constName? with
| some nm => nm == ``iN.poison
| none => false
match fn, args with
| `Eq, #[_, lhs, rhs] =>
if isPoison lhs || isPoison rhs then
dbg_trace f!"clearing poison eq: {lhs} {rhs}"
let idStx := mkIdent decl.userName
let stx := mkNode ``Lean.Parser.Tactic.cases #[mkNullNode #[mkAtom "cases"], idStx]
try Elab.Tactic.evalTactic stx catch _ => pure ()
| _, _ => pure ()
)
This has output:
clearing poison eq: iN.poison _uniq.221 iN.bitvec _uniq.221 _uniq.1202
clearing poison eq: iN.poison _uniq.221 iN.bitvec _uniq.221 _uniq.1202
clearing poison eq: iN.poison _uniq.221 iN.bitvec _uniq.221 _uniq.1202
But I can't seem to clear the goal using the cases? It prints without doing anything. Help would be very appreciated.
Robin Arnez (Sep 10 2025 at 06:38):
Hmm? simp_all should be able to handle this with reduceCtorEq:
/--
LLVM-style integers with poison value.
-/
inductive iN (bits : Nat) : Type where
| bitvec : BitVec bits → iN bits
| poison : iN bits
example (h : iN.poison = iN.bitvec b) : 1 = 2 := by
simp only [reduceCtorEq] at h
Robin Arnez (Sep 10 2025 at 07:01):
But looking at your metaprogram:
- Don't forget to use docs#Lean.MVarId.withContext! So just use
fun goal => goal.withContext do - The right syntax for
casesis
let null := mkNullNode #[]
let elimTarget := mkNode `Lean.Parser.Tactic.elimTarget #[null, idStx]
let tactic : Syntax := mkNode `Lean.Parser.Tactic.cases #[mkAtom "cases", mkNullNode #[elimTarget], null, null]
I don't want you remember that though, just
let tactic ← `(tactic| cases $idStx:ident)
Also, many tactics can also be found as functions on MVarIds; so you can simply do goal.<tactic> sometimes. cases is one of them!
let #[] ← goal.cases decl.fvarId | throwError "could not solve goals with `cases`"
So here's a corrected version of your tactic:
import Lean
/--
LLVM-style integers with poison value.
-/
inductive iN (bits : Nat) : Type where
| bitvec : BitVec bits → iN bits
| poison : iN bits
open Lean Meta Elab Tactic
elab "clear_poison_eq" : tactic => do
let goals ← getGoals
goals.forM $ fun goal => goal.withContext do
let ctx ← getLCtx
for decl in ctx do
let declType ← whnf decl.type
let isPoison (e : Expr) : Bool :=
match e.getAppFn.constName? with
| some nm => nm == ``iN.poison
| none => false
match_expr declType with
| Eq _ lhs rhs =>
if isPoison lhs || isPoison rhs then
Lean.logInfo m!"clearing poison eq: {lhs} {rhs}"
let #[] ← goal.cases decl.fvarId | throwError "could not solve goals with `cases`"
| _ => pure ()
example (h : iN.poison = iN.bitvec 3#4) : Nat := by
clear_poison_eq
Aaron Liu (Sep 10 2025 at 10:26):
Try using simp_all
Aaron Liu (Sep 10 2025 at 10:26):
or contradiction
Last updated: Dec 20 2025 at 21:32 UTC