lean4-htt/tests/lean/run/cbv_opaque_guard.lean
Wojciech Różowski 0520de7374
fix: respect @[cbv_opaque] in reduceRecMatcher and reduceProj (#12621)
This PR fixes a bug where `reduceRecMatcher?` and `reduceProj?` bypassed
the `@[cbv_opaque]` attribute. These kernel-level reduction functions
use `whnf` internally, which does not know about `@[cbv_opaque]`. This
meant `@[cbv_opaque]` values were unfolded when they appeared as match
discriminants, recursor major premises, or projection targets. The fix
introduces `withCbvOpaqueGuard`, which wraps these calls with
`withCanUnfoldPred` to prevent `whnf` from unfolding `@[cbv_opaque]`
definitions.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 16:13:26 +00:00

95 lines
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.

set_option cbv.warning false
/-! 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
/-! The kernel's `isDefEq` in `cbvGoalCore` still closes `@[cbv_opaque]` goals. -/
example : secret = 42 := by cbv
example : secretPair.1 = 10 := by cbv