vcgen: an equation in the spec list whose left-hand side is an `ite` program is not used; the `ite` is split first
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Domain
- devtools
Research direction
Start by running the standalone reproducer with set_option experimental.vcgen true, focusing on prog_spec, which fails, and prog_spec', which works. The issue points to the Std.WP and Std.Tactic.Do modules, where vcgen's handling of equational arguments is implemented. Done means prog_spec closes with guard_eq used to rewrite the ite before splitting, without a manual rw.
Written by the indexing model from the issue text.
Description
This issue was written by Claude (an AI assistant) on behalf of @nomeata.
Found while moving con-leche's cached-to-pure simulation proofs (a few thousand lines) to vcgen + grind (sym … => vcgen … <;> finish). Each case has a workaround in con-leche; the reproducer below is standalone.
An equational vcgen argument whose left-hand side is an ite program
is not used: vcgen splits the ite first.
Expected: vcgen [guard_eq, …] rewrites (if ok n then g n else pure 0)
to g n (as it rewrites any other program by an equation in its list),
leaving one verification condition about g.
Observed (v4.35.0 and nightly-2026-10-15): the ite is split; the else branch's condition
¬ ok n = true is a separate VC with pure 0, which here is unprovable
(the post needs g's answer), so finish fails on it.
Workaround: fold the equation into the caller's own unfolding equation
(con-leche: whnfAppI_cons states the loop with the ι arity guard
already discharged by iotaArityOk_guard).
import Std.WP
import Std.Tactic.Do
open Std.WP
open scoped Lean.Order
set_option experimental.vcgen true
def ok (n : Nat) : Bool := n % 2 == 0
/-- An answer only `g_spec` knows (as a walk's pure side is known only
through its callee specs). -/
@[irreducible] def spec (n : Nat) : Nat := if ok n then 1 else 0
def g (n : Nat) : StateT Nat (Except String) Nat := pure (spec n)
/-- The guard changes nothing: `g` answers `0` itself where `ok` fails. -/
theorem guard_eq (n : Nat) : (if ok n then g n else pure 0) = g n := by
by_cases h : ok n = true
· simp [h]
· simp only [h, g]; unfold spec; simp [h]
theorem g_spec (n : Nat) (s₀ : Nat) :
⦃fun s => s = s₀⦄ g n ⦃fun r s => s = s₀ ∧ r = spec n; ⊤⦄ := by
vcgen [g] with finish
def prog (n : Nat) : StateT Nat (Except String) Nat := do
let r ← (if ok n then g n else pure 0)
pure (r + 1)
/-- Fails: the `ite` is split, `guard_eq` unused. -/
theorem prog_spec (n : Nat) (s₀ : Nat) :
⦃fun s => s = s₀⦄ prog n ⦃fun r _ => r = spec n + 1; ⊤⦄ := by
vcgen [prog, guard_eq, g_spec] with finish
/-- Works: the same rewrite done by hand first. -/
theorem prog_spec' (n : Nat) (s₀ : Nat) :
⦃fun s => s = s₀⦄ prog n ⦃fun r _ => r = spec n + 1; ⊤⦄ := by
unfold prog; rw [guard_eq]
vcgen [g_spec] with finish
- Dominant language
- Lean
- Stars
- 9.6k
- Forks
- 1.1k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 279
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from leanprover/lean4
-
`IO.appPath` fails when `/proc` belongs to another PID namespacePossibly taken @treiher claimed this 1 day ago. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
leanprover/lean4#15692 · 3 reactions ·
Maintainers usually reply within 1 day
-
`IO.FS.realPath` reports every failure as `noFileOrDirectory` instead of classifying by `errno`Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
leanprover/lean4#15683 ·
Maintainers usually reply within 1 day
-
`Child.pid` leaks the `Child` and its pipe file descriptors (owned argument treated as borrowed)Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
leanprover/lean4#15681 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
leanprover/lean4#15677 ·
Maintainers usually reply within 1 day
-
`Child.takeStdin` loses `setsid`, so `Child.kill` only signals the child, not its process groupOpenbug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
leanprover/lean4#15674 ·
Maintainers usually reply within 1 day
All issues in leanprover/lean4
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 63/100
home-assistant/installer#322 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
datatug/datatug-apps#427 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 2 days
-
Edit tool converts CRLF files to LFPossibly taken @RaphaelFakhri claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
huggingface/tau#774 ·
Maintainers usually reply within 1 day
-
codegen: separate escaped supplementary-plane identifiers from following keywords when minifyingOpenbug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
swc-project/swc-next#1023 ·
Maintainers usually reply within 1 day