fix: cbv handling of ite/dite/decide (#12816)

This PR solves three distinct issues with the handling of
`ite`/`dite`,`decide`.

1) We prevent the simprocs from picking up `noncomputable`, `Classical`
instances, such as `Classical.propDecidable`, when simplifying the
proposition in `ite`/`dite`/`decide`.

2) We fix a type mismatch occurring when the condition/proposition is
unchanged but the `Decidable` instance is simplified.

3) If we rewrite the proposition from `c` to `c'` and the evaluation of
the original instance `Decidable c` gets stuck we try fallback path of
of obtaining `Decidable c'` instance and evaluating it. This matters
when the instance is evaluated via `cbv_eval` lemmas.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Wojciech Różowski 2026-03-06 16:18:39 +00:00 committed by GitHub
parent 68ea28c24f
commit 54f188160c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 180 additions and 40 deletions

View file

@ -33,15 +33,27 @@ theorem ite_cond_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α)
theorem ite_true {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α) {ht : c} : @ite α c inst a b = a := by theorem ite_true {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α) {ht : c} : @ite α c inst a b = a := by
simp [*] simp [*]
theorem ite_true_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α) (c' : Prop) (h : c = c') {ht : c'} : @ite α c inst a b = a := by
simp [*]
theorem ite_false {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α) {ht : ¬ c} : @ite α c inst a b = b := by theorem ite_false {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α) {ht : ¬ c} : @ite α c inst a b = b := by
simp [*] simp [*]
theorem ite_false_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a b : α) (c' : Prop) (h : c = c') {ht : ¬ c'} : @ite α c inst a b = b := by
simp [*]
theorem dite_true {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) {ht : c} : @dite α c inst a b = a ht := by theorem dite_true {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) {ht : c} : @dite α c inst a b = a ht := by
simp [*] simp [*]
theorem dite_true_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) (c' : Prop) (h : c = c') {ht : c'} : @dite α c inst a b = a (h.mpr_prop ht) := by
subst h; simp [*]
theorem dite_false {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) {ht : ¬ c} : @dite α c inst a b = b ht := by theorem dite_false {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) {ht : ¬ c} : @dite α c inst a b = b ht := by
simp [*] simp [*]
theorem dite_false_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) (c' : Prop) (h : c = c') {ht : ¬ c'} : @dite α c inst a b = b (h.mpr_not ht) := by
subst h; simp [*]
theorem dite_cond_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α) theorem dite_cond_congr {α : Sort u} (c : Prop) {inst : Decidable c} (a : c → α) (b : ¬ c → α)
(c' : Prop) {inst' : Decidable c'} (h : c = c') (c' : Prop) {inst' : Decidable c'} (h : c = c')
: @dite α c inst a b = @dite α c' inst' (fun h' => a (h.mpr_prop h')) (fun h' => b (h.mpr_not h')) := by : @dite α c inst a b = @dite α c' inst' (fun h' => a (h.mpr_prop h')) (fun h' => b (h.mpr_not h')) := by

View file

@ -19,6 +19,8 @@ import Lean.Meta.AppBuilder
import Init.Sym.Lemmas import Init.Sym.Lemmas
import Lean.Meta.Tactic.Cbv.TheoremsLookup import Lean.Meta.Tactic.Cbv.TheoremsLookup
import Lean.Meta.Tactic.Cbv.Opaque import Lean.Meta.Tactic.Cbv.Opaque
import Lean.Meta.Tactic.Cbv.CbvEvalExt
import Lean.Compiler.NoncomputableAttr
/-! /-!
# Control Flow Handling for Cbv # Control Flow Handling for Cbv
@ -35,20 +37,47 @@ corresponding branch.
namespace Lean.Meta.Sym.Simp namespace Lean.Meta.Sym.Simp
open Internal open Internal
def isCbvNoncomputable (p : Name) : CoreM Bool := do
let evalLemmas ← Tactic.Cbv.getCbvEvalLemmas p
return evalLemmas.isNone && Lean.isNoncomputable (← getEnv) p
/--
Attemps to synthesize `Decidable p` instance and guards against picking up a `noncomputable` instance
-/
def trySynthComputableInstance (p : Expr) : SymM <| Option Expr := do
let .some inst' ← trySynthInstance (mkApp (mkConst ``Decidable) p) | return .none
if (← inst'.getUsedConstants.anyM (isCbvNoncomputable ·)) then return .none
shareCommon inst'
/-- Reduce `ite` by matching the `Decidable` instance for `isTrue`/`isFalse`. -/ /-- Reduce `ite` by matching the `Decidable` instance for `isTrue`/`isFalse`. -/
def simpIteDecidable (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM Result := do def matchIteDecidable (f α c inst a b instToMatch : Expr) (fallback : SimpM Result) : SimpM Result := do
match_expr inst with match_expr instToMatch with
| Decidable.isTrue _ hp => | Decidable.isTrue _ hp =>
return .step a <| mkApp6 (mkConst ``Sym.ite_true f.constLevels!) α c inst a b hp return .step a <| mkApp6 (mkConst ``Sym.ite_true f.constLevels!) α c inst a b hp
| Decidable.isFalse _ hnp => | Decidable.isFalse _ hnp =>
return .step b <| mkApp6 (mkConst ``Sym.ite_false f.constLevels!) α c inst a b hnp return .step b <| mkApp6 (mkConst ``Sym.ite_false f.constLevels!) α c inst a b hnp
| _ => fallback | _ => fallback
/-- Like `matchIteDecidable`, but for the congruence case where `c` was simplified to `c'` with proof `h`. -/
def matchIteDecidableCongr (f α c inst a b c' h inst' : Expr) (fallback : SimpM Result) : SimpM Result := do
match_expr inst' with
| Decidable.isTrue _ hp =>
return .step a <| mkApp8 (mkConst ``Sym.ite_true_congr f.constLevels!) α c inst a b c' h hp
| Decidable.isFalse _ hnp =>
return .step b <| mkApp8 (mkConst ``Sym.ite_false_congr f.constLevels!) α c inst a b c' h hnp
| _ => fallback
/-- Simplify the `Decidable` instance, then try `simpIteDecidable`. -/ /-- Simplify the `Decidable` instance, then try `simpIteDecidable`. -/
def simpIteDecidableWithFallback (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM Result := do def simpAndMatchIteDecidable (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM Result := do
match (← simp inst) with match (← simp inst) with
| .rfl _ => simpIteDecidable f α c inst a b fallback | .rfl _ => matchIteDecidable f α c inst a b inst fallback
| .step inst' _ _ => simpIteDecidable f α c inst' a b fallback | .step inst' _ _ => matchIteDecidable f α c inst a b inst' fallback
/-- Like `simpAndMatchIteDecidable`, but for the congruence case where `c` was simplified to `c'`. -/
def simpAndMatchIteDecidableCongr (f α c inst a b c' h inst' : Expr) (fallback : SimpM Result) : SimpM Result := do
match (← simp inst') with
| .rfl _ => matchIteDecidableCongr f α c inst a b c' h inst' fallback
| .step inst'' _ _ => matchIteDecidableCongr f α c inst a b c' h inst'' fallback
/-- Like `simpIte` but also evaluates `Decidable.decide` when the condition does not /-- Like `simpIte` but also evaluates `Decidable.decide` when the condition does not
reduce to `True`/`False` directly. -/ reduce to `True`/`False` directly. -/
@ -64,23 +93,27 @@ public def simpIteCbv : Simproc := fun e => do
else if (← isFalseExpr c) then else if (← isFalseExpr c) then
return .step b <| mkApp3 (mkConst ``ite_false f.constLevels!) α a b return .step b <| mkApp3 (mkConst ``ite_false f.constLevels!) α a b
else else
simpIteDecidableWithFallback f α c inst a b <| return .rfl (done := true) simpAndMatchIteDecidable f α c inst a b do return .rfl (done := true)
| .step c' h _ => | .step c' h _ =>
if (← isTrueExpr c') then if (← isTrueExpr c') then
return .step a <| mkApp (e.replaceFn ``ite_cond_eq_true) h return .step a <| mkApp (e.replaceFn ``ite_cond_eq_true) h
else if (← isFalseExpr c') then else if (← isFalseExpr c') then
return .step b <| mkApp (e.replaceFn ``ite_cond_eq_false) h return .step b <| mkApp (e.replaceFn ``ite_cond_eq_false) h
else else
simpIteDecidableWithFallback f α c inst a b <| do -- If we got stuck with simplifying `p` then let's try evaluating the original isntance
simpAndMatchIteDecidable f α c inst a b do
-- If we get stuck here, maybe the problem is that we need to look at `Decidable c'`
let inst' := mkApp4 (mkConst ``decidable_of_decidable_of_eq) c c' inst h let inst' := mkApp4 (mkConst ``decidable_of_decidable_of_eq) c c' inst h
let e' := e.getBoundedAppFn 4 simpAndMatchIteDecidableCongr f α c inst a b c' h inst' do
let e' ← mkAppS₄ e' c' inst' a b -- If we fail, then we just rewrite `c` to `c'`
let h' := mkApp3 (e.replaceFn ``Sym.ite_cond_congr) c' inst' h let e' := e.getBoundedAppFn 4
return .step e' h' (done := true) let e' ← mkAppS₄ e' c' inst' a b
let h' := mkApp3 (e.replaceFn ``Sym.ite_cond_congr) c' inst' h
return .step e' h' (done := true)
/-- Reduce `dite` by matching the `Decidable` instance for `isTrue`/`isFalse`. -/ /-- Reduce `dite` by matching the `Decidable` instance for `isTrue`/`isFalse`. -/
def simpDIteDecidable (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM Result := do def matchDIteDecidable (f α c inst a b instToMatch : Expr) (fallback : SimpM Result) : SimpM Result := do
match_expr inst with match_expr instToMatch with
| Decidable.isTrue _ hp => | Decidable.isTrue _ hp =>
let a' ← share <| a.betaRev #[hp] let a' ← share <| a.betaRev #[hp]
return .step a' <| mkApp6 (mkConst ``Sym.dite_true f.constLevels!) α c inst a b hp return .step a' <| mkApp6 (mkConst ``Sym.dite_true f.constLevels!) α c inst a b hp
@ -89,11 +122,30 @@ def simpDIteDecidable (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM
return .step b' <| mkApp6 (mkConst ``Sym.dite_false f.constLevels!) α c inst a b hnp return .step b' <| mkApp6 (mkConst ``Sym.dite_false f.constLevels!) α c inst a b hnp
| _ => fallback | _ => fallback
/-- Like `matchDIteDecidable`, but for the congruence case where `c` was simplified to `c'` with proof `h`. -/
def matchDIteDecidableCongr (f α c inst a b c' h inst' : Expr) (fallback : SimpM Result) : SimpM Result := do
match_expr inst' with
| Decidable.isTrue _ hp =>
let hp' := mkApp4 (mkConst ``Eq.mpr_prop) c c' h hp
let a' ← share <| a.betaRev #[hp']
return .step a' <| mkApp8 (mkConst ``Sym.dite_true_congr f.constLevels!) α c inst a b c' h hp
| Decidable.isFalse _ hnp =>
let hnp' := mkApp4 (mkConst ``Eq.mpr_not) c c' h hnp
let b' ← share <| b.betaRev #[hnp']
return .step b' <| mkApp8 (mkConst ``Sym.dite_false_congr f.constLevels!) α c inst a b c' h hnp
| _ => fallback
/-- Simplify the `Decidable` instance, then try `simpDIteDecidable`. -/ /-- Simplify the `Decidable` instance, then try `simpDIteDecidable`. -/
def simpDIteDecidableWithFallback (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM Result := do def simpAndMatchDIteDecidable (f α c inst a b : Expr) (fallback : SimpM Result) : SimpM Result := do
match (← simp inst) with match (← simp inst) with
| .rfl _ => simpDIteDecidable f α c inst a b fallback | .rfl _ => matchDIteDecidable f α c inst a b inst fallback
| .step inst' _ _ => simpDIteDecidable f α c inst' a b fallback | .step inst' _ _ => matchDIteDecidable f α c inst a b inst' fallback
/-- Like `simpAndMatchDIteDecidable`, but for the congruence case where `c` was simplified to `c'`. -/
def simpAndMatchDIteDecidableCongr (f α c inst a b c' h inst' : Expr) (fallback : SimpM Result) : SimpM Result := do
match (← simp inst') with
| .rfl _ => matchDIteDecidableCongr f α c inst a b c' h inst' fallback
| .step inst'' _ _ => matchDIteDecidableCongr f α c inst a b c' h inst'' fallback
/-- Like `simpDIte` but also evaluates `Decidable.decide` when the condition does not /-- Like `simpDIte` but also evaluates `Decidable.decide` when the condition does not
reduce to `True`/`False` directly. -/ reduce to `True`/`False` directly. -/
@ -111,7 +163,7 @@ public def simpDIteCbv : Simproc := fun e => do
let b' ← share <| b.betaRev #[mkConst ``not_false] let b' ← share <| b.betaRev #[mkConst ``not_false]
return .step b' <| mkApp3 (mkConst ``dite_false f.constLevels!) α a b return .step b' <| mkApp3 (mkConst ``dite_false f.constLevels!) α a b
else else
simpDIteDecidableWithFallback f α c inst a b <| return .rfl (done := true) simpAndMatchDIteDecidable f α c inst a b do return .rfl (done := true)
| .step c' h _ => | .step c' h _ =>
if (← isTrueExpr c') then if (← isTrueExpr c') then
let h' ← shareCommon <| mkOfEqTrueCore c h let h' ← shareCommon <| mkOfEqTrueCore c h
@ -122,15 +174,18 @@ public def simpDIteCbv : Simproc := fun e => do
let b ← share <| b.betaRev #[h'] let b ← share <| b.betaRev #[h']
return .step b <| mkApp (e.replaceFn ``dite_cond_eq_false) h return .step b <| mkApp (e.replaceFn ``dite_cond_eq_false) h
else else
simpDIteDecidableWithFallback f α c inst a b <| do -- If we get stuck after simplifying `p` to `p'`, then we try to evaluate the original instance
simpAndMatchDIteDecidable f α c inst a b do
-- Otherwise, we make `Decidable c'` instance and try to evaluate it instead
let inst' := mkApp4 (mkConst ``decidable_of_decidable_of_eq) c c' inst h let inst' := mkApp4 (mkConst ``decidable_of_decidable_of_eq) c c' inst h
let e' := e.getBoundedAppFn 4 simpAndMatchDIteDecidableCongr f α c inst a b c' h inst' do
let h ← shareCommon h let e' := e.getBoundedAppFn 4
let a ← share <| mkLambda `h .default c' (a.betaRev #[mkApp4 (mkConst ``Eq.mpr_prop) c c' h (mkBVar 0)]) let h ← shareCommon h
let b ← share <| mkLambda `h .default (mkNot c') (b.betaRev #[mkApp4 (mkConst ``Eq.mpr_not) c c' h (mkBVar 0)]) let a ← share <| mkLambda `h .default c' (a.betaRev #[mkApp4 (mkConst ``Eq.mpr_prop) c c' h (mkBVar 0)])
let e' ← mkAppS₄ e' c' inst' a b let b ← share <| mkLambda `h .default (mkNot c') (b.betaRev #[mkApp4 (mkConst ``Eq.mpr_not) c c' h (mkBVar 0)])
let h' := mkApp3 (e.replaceFn ``Sym.dite_cond_congr) c' inst' h let e' ← mkAppS₄ e' c' inst' a b
return .step e' h' (done := true) let h' := mkApp3 (e.replaceFn ``Sym.dite_cond_congr) c' inst' h
return .step e' h' (done := true)
/-- Short-circuit evaluation of `Or`. Simplifies only the left argument; /-- Short-circuit evaluation of `Or`. Simplifies only the left argument;
if it reduces to `True`, returns `True` immediately without evaluating the right side. -/ if it reduces to `True`, returns `True` immediately without evaluating the right side. -/
@ -173,16 +228,16 @@ public def simpAnd : Simproc := fun e => do
return .rfl return .rfl
/-- Reduce `decide` by matching the `Decidable` instance for `isTrue`/`isFalse`. -/ /-- Reduce `decide` by matching the `Decidable` instance for `isTrue`/`isFalse`. -/
def simpDecideByInst (p inst : Expr) : SimpM Result := do def matchDecideDecidable (p inst instToMatch : Expr) (fallback : SimpM Result) : SimpM Result := do
match_expr inst with match_expr instToMatch with
| Decidable.isTrue _ hp => | Decidable.isTrue _ hp =>
return .step (← getBoolTrueExpr) <| mkApp3 (mkConst ``Sym.decide_isTrue) p inst hp return .step (← getBoolTrueExpr) <| mkApp3 (mkConst ``Sym.decide_isTrue) p inst hp
| Decidable.isFalse _ hnp => | Decidable.isFalse _ hnp =>
return .step (← getBoolFalseExpr) <| mkApp3 (mkConst ``Sym.decide_isFalse) p inst hnp return .step (← getBoolFalseExpr) <| mkApp3 (mkConst ``Sym.decide_isFalse) p inst hnp
| _ => return .rfl (done := true) | _ => fallback
/-- Like `simpDecideByInst`, but for the case where `p` was simplified to `p'` with proof `h`. -/ /-- Like `simpDecideByInst`, but for the case where `p` was simplified to `p'` with proof `h`. -/
def simpDecideByInstCongr (p p' h inst inst' : Expr) (fallback : SimpM Result) : SimpM Result := do def matchDecideDecidableCongr (p p' h inst inst' : Expr) (fallback : SimpM Result) : SimpM Result := do
match_expr inst' with match_expr inst' with
| Decidable.isTrue _ hp => | Decidable.isTrue _ hp =>
return .step (← getBoolTrueExpr) <| mkApp5 (mkConst ``Sym.decide_isTrue_congr) p p' h inst hp return .step (← getBoolTrueExpr) <| mkApp5 (mkConst ``Sym.decide_isTrue_congr) p p' h inst hp
@ -191,16 +246,16 @@ def simpDecideByInstCongr (p p' h inst inst' : Expr) (fallback : SimpM Result) :
| _ => fallback | _ => fallback
/-- Simplify the `Decidable` instance, then try `simpDecideByInst`. -/ /-- Simplify the `Decidable` instance, then try `simpDecideByInst`. -/
def simpDecideByInstWithFallback (p inst : Expr) : SimpM Result := do def simpAndMatchDecideDecidable (p inst : Expr) (fallback : SimpM Result) : SimpM Result := do
match (← simp inst) with match (← simp inst) with
| .rfl _ => simpDecideByInst p inst | .rfl _ => matchDecideDecidable p inst inst fallback
| .step inst' _ _ => simpDecideByInst p inst' | .step inst' _ _ => matchDecideDecidable p inst inst' fallback
/-- Like `simpDecideByInstWithFallback`, but for the case where `p` was simplified to `p'`. -/ /-- Like `simpDecideByInstWithFallback`, but for the case where `p` was simplified to `p'`. -/
def simpDecideByInstWithFallbackCongr (p p' h inst inst' : Expr) (fallback : SimpM Result) : SimpM Result := do def simpAndMatchDecideDecidableCongr (p p' h inst inst' : Expr) (fallback : SimpM Result) : SimpM Result := do
match (← simp inst') with match (← simp inst') with
| .rfl _ => simpDecideByInstCongr p p' h inst inst' fallback | .rfl _ => matchDecideDecidableCongr p p' h inst inst' fallback
| .step inst'' _ _ => simpDecideByInstCongr p p' h inst inst'' fallback | .step inst'' _ _ => matchDecideDecidableCongr p p' h inst inst'' fallback
/-- Simplify `Decidable.decide` by simplifying the proposition and reducing the instance. /-- Simplify `Decidable.decide` by simplifying the proposition and reducing the instance.
@ -221,20 +276,20 @@ public def simpDecideCbv : Simproc := fun e => do
else if (← isFalseExpr p) then else if (← isFalseExpr p) then
return .step (← getBoolFalseExpr) (mkApp (mkConst ``decide_false) inst) return .step (← getBoolFalseExpr) (mkApp (mkConst ``decide_false) inst)
else else
simpDecideByInstWithFallback p inst simpAndMatchDecideDecidable p inst do return .rfl (done := true)
| .step p' hp _ => | .step p' hp _ =>
if (← isTrueExpr p') then if (← isTrueExpr p') then
return .step (← getBoolTrueExpr) <| mkApp3 (mkConst ``Sym.decide_prop_eq_true) p inst hp return .step (← getBoolTrueExpr) <| mkApp3 (mkConst ``Sym.decide_prop_eq_true) p inst hp
else if (← isFalseExpr p') then else if (← isFalseExpr p') then
return .step (← getBoolFalseExpr) <| mkApp3 (mkConst ``Sym.decide_prop_eq_false) p inst hp return .step (← getBoolFalseExpr) <| mkApp3 (mkConst ``Sym.decide_prop_eq_false) p inst hp
else else
let .some inst' ← trySynthInstance (mkApp (mkConst ``Decidable) p') | return .rfl let inst' ← trySynthComputableInstance p'
let inst' ← shareCommon inst' let inst' := inst'.getD <| mkApp4 (mkConst ``decidable_of_decidable_of_eq) p p' inst hp
simpDecideByInstWithFallbackCongr p p' hp inst inst' (do simpAndMatchDecideDecidableCongr p p' hp inst inst' do
let res := (mkConst ``Decidable.decide) let res := (mkConst ``Decidable.decide)
let res ← shareCommon res let res ← shareCommon res
let res ← mkAppS₂ res p' inst' let res ← mkAppS₂ res p' inst'
return .step res (mkApp5 (mkConst ``Decidable.decide.congr_simp) p p' hp inst inst') (done := true)) return .step res (mkApp5 (mkConst ``Decidable.decide.congr_simp) p p' hp inst inst') (done := true)
end Lean.Meta.Sym.Simp end Lean.Meta.Sym.Simp

View file

@ -0,0 +1,38 @@
set_option cbv.warning false
/-!
# A regression test against `cbv` picking up `Classical.propDecidable`,
when re-synthesizing instances.
When `cbv` encounters `decide P`, it simplifies the proposition `P`. If `P`
unfolds (e.g. `IsEven 2` → `∃ k, 2 * k = 2`), `simpDecideCbv` tries to
synthetize `Decidable` instance for the *unfolded* form. With `open Classical`,
this was picking up `Classical.propDecidable` (which uses `choice`), replacing
the original computable instance with one that cannot be evaluated.
The code now contains a guard ensuring that the instance is not made of constants
marked as `noncomputable`.
-/
-- A predicate wrapping an existential — has a computable `DecidablePred` instance,
-- but the unfolded existential has none (except the classical one).
def IsEven (n : Nat) : Prop := ∃ k, n = 2 * k
instance : DecidablePred IsEven := fun n =>
if h : n % 2 = 0 then
.isTrue ⟨n / 2, by omega⟩
else
.isFalse (by intro ⟨k, hk⟩; omega)
-- Works, using the provided `DecidablePred` instance.
example : decide (IsEven 2) = true := by cbv
example : decide (IsEven 3) = false := by cbv
-- Should not fail, when `Classical.propDecidable` becomes available.
open Classical in
example : decide (IsEven 2) = true := by cbv
open Classical in
example : decide (IsEven 3) = false := by cbv

View file

@ -0,0 +1,35 @@
set_option cbv.warning false
set_option trace.Meta.Tactic true
axiom P : Prop
axiom instDecidableP : Decidable P
axiom Q : Prop
axiom P_eval : P = Q
attribute [cbv_eval] P_eval
example : P = Q := by cbv
axiom hQ : Q
axiom instDecidableQ : Decidable Q
noncomputable instance : Decidable P := instDecidableP
noncomputable instance : Decidable Q := instDecidableQ
axiom dQ_eval : instDecidableQ = Decidable.isTrue hQ
attribute [cbv_eval] dQ_eval
example : (if P then true else false) = true := by cbv
example : (if P then true else false) = true := by conv => lhs; cbv
example : (if _ : P then true else false) = true := by cbv
example : P := by decide_cbv
example : decide P = true := by conv => lhs; cbv
example : Q := by decide_cbv
example : decide Q = true := by conv => lhs; cbv
example : (if Q then true else false) = true := by cbv
example : (if _ : Q then true else false) = true := by cbv