feat: unfold nullary constants in cbv (#12646)

This PR enables the `cbv` tactic to unfold nullary (non-function)
constant
definitions such as `def myNat : Nat := 42`, allowing ground term
evaluation
(e.g. `evalEq`, `evalLT`) to recognize their values as literals.

Previously, `handleConst` skipped all nullary constants. Now it performs
direct
delta reduction using `instantiateValueLevelParams` instead of going
through
the equation theorem machinery (`getUnfoldTheorem`), which would trigger
`realizeConst` and fail for constants (such as derived typeclass
instances)
where `enableRealizationsForConst` has not been called.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Wojciech Różowski 2026-02-23 11:46:57 +00:00 committed by GitHub
parent 8b04403830
commit 24380fc900
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 6 deletions

View file

@ -233,16 +233,16 @@ def simplifyAppFn : Simproc := fun e => do
return .step newValue newProof
def handleConst : Simproc := fun e => do
let .const n _ := e | return .rfl
let .const n lvls := e | return .rfl
let info ← getConstInfo n
unless info.isDefinition do return .rfl
let eType ← Sym.inferType e
let eType ← whnfD eType
unless eType matches .forallE .. do
return .rfl
-- TODO: Check if we need to look if we applied all the levels correctly
let some thm ← getUnfoldTheorem n | return .rfl
Simproc.tryCatch (fun e => Theorem.rewrite thm e) e
if eType matches .forallE .. then return .rfl
unless info.hasValue && info.levelParams.length == lvls.length do return .rfl
let fBody ← instantiateValueLevelParams info lvls
let eNew ← Sym.share fBody
return .step eNew (← Sym.mkEqRefl eNew)
/--
Pre-pass structural dispatch. Routes each expression form to the appropriate handler:

View file

@ -0,0 +1,46 @@
set_option cbv.warning false
/-! Tests for `cbv` evaluation of nullary (non-function) constants.
Nullary definitions like `def myNat : Nat := 42` should be unfolded by `cbv`
so that ground term evaluation (e.g. `evalEq`, `evalLT`) can recognize their
values as literals.
-/
def myNat : Nat := 42
-- Arithmetic: argument goes through pattern matching in Nat.add
example : myNat + 1 = 43 := by cbv
-- Direct equality
example : myNat = 42 := by cbv
-- Prop-level equality and comparisons
example : (myNat = myNat) = True := by cbv
example : (myNat = 42) = True := by cbv
example : (myNat < 100) = True := by cbv
example : (myNat ≤ 42) = True := by cbv
-- Bool-level equality
example : (myNat == 42) = true := by cbv
-- Condition involving a nullary constant
example : (if myNat = 42 then 1 else 0) = 1 := by cbv
-- String nullary constant
def myStr : String := "hello"
example : myStr.length = 5 := by cbv
example : (myStr = "hello") = True := by cbv
-- Custom inductive type
inductive Color where | red | green | blue
def myColor : Color := .red
def colorToNat : Color → Nat
| .red => 1
| .green => 2
| .blue => 3
example : colorToNat myColor = 1 := by cbv