Summary
vera verify generates no call-site precondition obligation at all when the called function's requires() constrains ADT-typed parameters — no obligation record, no E501, no Tier-3 demotion warning. The identical program with Int parameters correctly produces a call_pre obligation (E501 violated). Enforcement is runtime-only: vera run traps with a precondition violation, so this is not a false Tier-1 — it is a silent static-coverage gap: verify reports ok: true with a clean summary for a call it never statically examined, in exactly the class (structural ADT equality in requires) where #871 showed static checking matters.
Verified on main at c0af1b9; found during PR #879's model-parity panel.
Repro (verified)
private data P { MkP(Int) }
private fn g(@P, @P -> @Bool)
requires(@P.1 == @P.0) ensures(true) effects(pure)
{
true
}
public fn main(@Unit -> @Bool)
requires(true) ensures(true) effects(pure)
{
g(MkP(1), MkP(2))
}
vera verify --json → ok: true, {'tier1_verified': 4, 'tier3_runtime': 0, 'total': 4} — no call_pre obligation for the g call site, no diagnostic, no warning.
- Control with
g(@Int, @Int -> @Bool) / g(1, 2) → ok: false, [E501] (call-site precondition violated) — same obligation machinery, so the walker demonstrably skips only the ADT-typed case.
vera run → exit 1, precondition violation at runtime (loud, correct).
Expected
Per DESIGN.md (maximise static guarantees; degrade loudly): the call site should generate a call_pre obligation. With #879 merged, ADT equality over concrete fields is statically modellable (per-field, fpEQ for floats), so the obligation should discharge or produce a counterexample (MkP(1) vs MkP(2) is statically refutable). Where the argument expression genuinely can't be modelled, the obligation must demote to a loud Tier-3 (E522), never silently not exist — the verify summary currently overstates coverage.
Pointers
Summary
vera verifygenerates no call-site precondition obligation at all when the called function'srequires()constrains ADT-typed parameters — no obligation record, no E501, no Tier-3 demotion warning. The identical program withIntparameters correctly produces acall_preobligation (E501 violated). Enforcement is runtime-only:vera runtraps with a precondition violation, so this is not a false Tier-1 — it is a silent static-coverage gap:verifyreportsok: truewith a clean summary for a call it never statically examined, in exactly the class (structural ADT equality inrequires) where #871 showed static checking matters.Verified on main at c0af1b9; found during PR #879's model-parity panel.
Repro (verified)
vera verify --json→ok: true,{'tier1_verified': 4, 'tier3_runtime': 0, 'total': 4}— no call_pre obligation for thegcall site, no diagnostic, no warning.g(@Int, @Int -> @Bool)/g(1, 2)→ok: false,[E501](call-site precondition violated) — same obligation machinery, so the walker demonstrably skips only the ADT-typed case.vera run→ exit 1, precondition violation at runtime (loud, correct).Expected
Per DESIGN.md (maximise static guarantees; degrade loudly): the call site should generate a
call_preobligation. With #879 merged, ADT equality over concrete fields is statically modellable (per-field, fpEQ for floats), so the obligation should discharge or produce a counterexample (MkP(1)vsMkP(2)is statically refutable). Where the argument expression genuinely can't be modelled, the obligation must demote to a loud Tier-3 (E522), never silently not exist — the verify summary currently overstates coverage.Pointers
vera/verifier.py(wherevercall_preobligations are built) — it evidently bails on ADT-typed argument translation before recording an obligation.