lean4-htt/tests/elab/cbv_opaque_guard.lean
Wojciech Różowski 7e3e7cf5d9
feat: add cbv annotations to iterators and strings (#12961)
This PR adds `cbv` annotations to some iterator and string operations.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 11:39:40 +00:00

171 lines
4.2 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-! Test that `@[cbv_opaque]` is respected by `reduceRecMatcher` and `reduceProj`. -/
/-! `@[cbv_opaque]` constants are not unfolded. -/
@[cbv_opaque] def secret : Nat := 42
/--
error: unsolved goals
⊢ secret = 42
-/
#guard_msgs in
example : secret = 42 := by conv => lhs; cbv
/-! Equation theorems fire but `@[cbv_opaque]` arguments stay intact. -/
def addOne (n : Nat) : Nat := n + 1
/--
error: unsolved goals
⊢ secret.succ = 43
-/
#guard_msgs in
example : addOne secret = 43 := by conv => lhs; cbv
/-! Pattern matching on a `@[cbv_opaque]` discriminant gets stuck. -/
def isZero (n : Nat) : Bool :=
match n with
| 0 => true
| _ + 1 => false
/--
error: unsolved goals
⊢ (match secret with
| 0 => true
| n.succ => false) =
false
-/
#guard_msgs in
example : isZero secret = false := by conv => lhs; cbv
/-! Recursive functions with `@[cbv_opaque]` arguments get stuck at the match. -/
def myLength : List Nat → Nat
| [] => 0
| _ :: xs => 1 + myLength xs
@[cbv_opaque] def secretList : List Nat := [1, 2, 3]
/--
error: unsolved goals
⊢ (match secretList with
| [] => 0
| head :: xs => 1 + myLength xs) =
3
-/
#guard_msgs in
example : myLength secretList = 3 := by conv => lhs; cbv
/-! Projections on `@[cbv_opaque]` structures are not reduced. -/
@[cbv_opaque] def secretPair : Nat × Nat := (10, 20)
/--
error: unsolved goals
⊢ secretPair.1 = 10
-/
#guard_msgs in
example : secretPair.1 = 10 := by conv => lhs; cbv
/--
error: unsolved goals
⊢ secretPair.2 = 20
-/
#guard_msgs in
example : secretPair.2 = 20 := by conv => lhs; cbv
/-! Non `@[cbv_opaque]` values still reduce normally. -/
def normalVal : Nat := 42
example : normalVal + 1 = 43 := by cbv
example : isZero normalVal = false := by cbv
def normalPair : Nat × Nat := (10, 20)
example : normalPair.1 = 10 := by cbv
example : normalPair.2 = 20 := by cbv
/-! `@[cbv_eval]` can override `@[cbv_opaque]`. -/
@[cbv_opaque] def opaqueAdd (a b : Nat) : Nat := a + b
@[cbv_eval] theorem opaqueAdd_eq (a b : Nat) : opaqueAdd a b = a + b := rfl
example : opaqueAdd 1 2 = 3 := by conv => lhs; cbv
/-! `@[cbv_eval]` works on bare constants (no arguments). -/
def bareConst : Nat := 2 + 3
@[cbv_eval] theorem bareConst_eq : bareConst = 5 := rfl
example : bareConst = 5 := by conv => lhs; cbv
/-! The kernel's `isDefEq` in `cbvGoalCore` still closes `@[cbv_opaque]` goals. -/
example : secret = 42 := by cbv
example : secretPair.1 = 10 := by cbv
/-! ## Interaction of `@[cbv_opaque]` and `@[cbv_eval]` -/
/-! `@[cbv_eval]` on an opaque bare constant rewrites it. -/
@[cbv_opaque] def opaqueConst : Nat := 99
@[cbv_eval] theorem opaqueConst_eq : opaqueConst = 99 := rfl
example : opaqueConst = 99 := by conv => lhs; cbv
/-! `@[cbv_eval]` on an opaque function fires and the result is further reduced. -/
@[cbv_opaque] def opaqueMul (a b : Nat) : Nat := a * b
@[cbv_eval] theorem opaqueMul_eq (a b : Nat) : opaqueMul a b = a * b := rfl
example : opaqueMul 3 4 + 1 = 13 := by conv => lhs; cbv
/-! Without `@[cbv_eval]`, an opaque constant stays stuck (cbv_opaque alone blocks). -/
@[cbv_opaque] def pureOpaque : Nat := 7
/--
error: unsolved goals
⊢ pureOpaque = 7
-/
#guard_msgs in
example : pureOpaque = 7 := by conv => lhs; cbv
/-! `@[cbv_eval ←]` (inverted) also works with `@[cbv_opaque]`. -/
@[cbv_opaque] def opaqueAlias : Nat := 42
@[cbv_eval ←] theorem opaqueAlias_eq : 42 = opaqueAlias := rfl
example : opaqueAlias = 42 := by conv => lhs; cbv
/-! `@[cbv_opaque]` with `@[cbv_eval]` still prevents unfolding of the definition itself. -/
@[cbv_opaque] def opaquePartial (n : Nat) : Nat := n * n
-- Only provide a rule for the specific case n=5
@[cbv_eval] theorem opaquePartial_5 : opaquePartial 5 = 25 := rfl
example : opaquePartial 5 = 25 := by conv => lhs; cbv
-- No rule for n=3, so it stays stuck
/--
error: unsolved goals
⊢ opaquePartial 3 = 9
-/
#guard_msgs in
example : opaquePartial 3 = 9 := by conv => lhs; cbv
/-! Opaque constant used as argument to a non-opaque function stays opaque. -/
def double (n : Nat) : Nat := n + n
/--
error: unsolved goals
⊢ (match pureOpaque, pureOpaque with
| a, Nat.zero => a
| a, b.succ => (a.add b).succ) =
14
-/
#guard_msgs in
example : double pureOpaque = 14 := by conv => lhs; cbv