lfglabs-dev / lfglabs-dev/verity

verity_contract bodies hit a Lean 4.31 do-notation elaboration failure (name-dependent) with mutable reassignments interleaved with binds

Open
#2,427 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Lean
Stars
148
Forks
20
Avg merge
1d 14h
Merged PRs (30d)
29

Description

Found while translating Pareto IdleCreditVault.prepareStopEpochWithApr0 (closure repo gap G19). This is a Lean 4 core do-elaborator issue, not a macro bug, but every verity_contract body is a Lean do block, so translators hit it and the error is opaque.

Symptom (lean4 v4.31.0, the toolchain pinned by Verity): with three let mut locals and an if whose branch interleaves let x ← e binds with reassignments, the if (and an else pure () arm) is elaborated against the word type instead of Unit:

type mismatch, result value has type
  Unit
but is expected to have type
  Uint256

It depends on the identifier names only. Plain StateM repro (no Verity import):

def step (n : Nat) : StateM Nat Nat := do
  modify (· + 1); return n + 1

-- fails
def r0 (_interest _pendingFees g fee : Nat) : StateM Nat (Nat × Nat) := do
  let mut _expInterest := _interest
  let mut _adjPendingWithdrawFees := _pendingFees
  let mut _apr0NetInterest := 0
  if g != 0 then
    let n ← step fee
    _apr0NetInterest := n
    let a2 ← step _adjPendingWithdrawFees
    _adjPendingWithdrawFees := a2
    let e2 ← step _expInterest
    _expInterest := e2
  return (_expInterest, _adjPendingWithdrawFees)

-- elaborates: only the third mutable is renamed (`_apr0NetInterest` -> `q`)
def r2 (_interest _pendingFees g fee : Nat) : StateM Nat (Nat × Nat) := do
  let mut _expInterest := _interest
  let mut _adjPendingWithdrawFees := _pendingFees
  let mut q := 0
  if g != 0 then
    let n ← step fee
    q := n
    let a2 ← step _adjPendingWithdrawFees
    _adjPendingWithdrawFees := a2
    let e2 ← step _expInterest
    _expInterest := e2
  return (_expInterest, _adjPendingWithdrawFees)

Renaming other locals (_aprNetInterest still fails, _x0y, _zzz pass), adding a type ascription, adding else pure (), or reading the third mutable afterwards do not change the outcome; hoisting the binds above the reassignments always works:

  if g != 0 then
    let n ← step fee
    let a2 ← step _adjPendingWithdrawFees
    let e2 ← step _expInterest
    _apr0NetInterest := n
    _adjPendingWithdrawFees := a2
    _expInterest := e2

Suggested Verity actions (small):

  1. Document the pitfall and the binds-first workaround in docs/MODIFIERS_AND_INHERITANCE.md or the contract-authoring guide, next to the existing let mut notes.
  2. Optionally have the macro detect the shape (reassignment, bind, reassignment inside one branch) and emit a hint pointing at the workaround, since the raw Lean error names no variable.
  3. Report upstream to leanprover/lean4 with the repro above (I did not find an existing issue).

Context: Pareto translation review of #2413 (issues #2415-#2424, PR #2425).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the Lean 4.31 failure with the plain StateM r0/r2 examples in the issue, then read docs/MODIFIERS_AND_INHERITANCE.md and the contract-authoring guide near the existing let mut notes. Document the binds-first workaround and the opaque error for verity_contract authors; the documentation is done when the failure mode and workaround are clear.

Written by the indexing model from the issue text.

Assessment

Domain
compilers, documentation
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.