Test "proj-of-stuck-prop"
Expected: ✋ reject · Size: 275.9 KB · Lines: 5.3 k · lean4export: 3.1.0 · Lean: 4.29.1 · 📄 Declaration · 🔗 Source
Proof of False by projecting the Bool field out of a proposition, exploiting
that a kernel can disagree with itself about whether the structure lives in
Prop.
Same underlying defect as rec-missing-ih, but with a different consequence.
Mechanism
-
Definitional equality is not transitive here. Three functions
Bool → Boolare built fromAcc.recsuch that a kernel reportsrcA ≡ rcBandrcB ≡ rcC— both by proof irrelevance on theAccargument — whilercA ≢ rcC, because there the twoAccproofs have different types. -
In the affected kernels, the defeq cache closes that relation transitively, but only for hash-equal terms. Established equalities are kept in a union-find structure, and the comparison returns early, without consulting it, when the hashes differ:
~~~cpp if (is_eqp(a, b)) return true; if (m_use_hash && hash(a) != hash(b)) return false; // skips the union-find ... node_ref r1 = find(to_node(a)); node_ref r2 = find(to_node(b)); if (r1 == r2) return true; ~~~
So whether
rcA ≡ rcCholds depends on the hash of the surrounding term. The constants and paddings are chosen so that the hashes collide exactly when the argument is the free variable_kernel_fresh.0, and not for the closed instantiation used later. -
That comparison decides a result sort.
Native64ResultSortGateis a K-like inductive predicate, and its recursor is used as the result sort of the inductive familyNative64ResultSortOwner: the sortGate.rec x … Prop requested hreduces toProponly if the requested indices are definitionally equal to the ones ofGate.intro. HenceOwner x his a proposition in one context and a stuck sort in another:Native64ResultSort.asPropis checked against a constant standing for∀ x h, Prop, so the kernel introduces_kernel_fresh.0forx, the hashes collide,Owner x h : Propis accepted, andNative64ResultSortLeak.propositionis aPropfor every later declaration — including for proof irrelevance.Native64ResultSortLeak.observeprojects field 0 out of that proposition. There the sort of the closed termOwner false closedGateis needed, and that one is stuck — the affected kernels answerfalsewhen asked whether it is definitionally equal toProp— so they do not see a proposition and permit projecting out theBoolfield.
Proof irrelevance then identifies two Owner.mk applications carrying
different Bool fields, and observing them yields False. The affected
kernels rejected Native64ResultSortOwner with type expected as soon as the
hashes no longer collided.
This was accepted by the official kernel at v4.28.0, v4.29.1, v4.33.0 and
nightly-2026-08-01. Other kernels reject the export in one of two places:
either they refuse Native64ResultSortOwner because its result type does not
reduce to a sort, or they accept the type but refuse the projection of a data
field out of a proposition.
Both steps are ruled out now: the projection by
leanprover/lean4#14807, which
makes the kernel's is_prop check require the inferred type to reduce to a
sort, and the hash-gated transitivity of step 2 by
leanprover/lean4#14806, which
replaces the union-find defeq cache with an order-independent one. The same
projection, reached without any help from that cache, is proj-of-subst-prop.
| Checker | Result | ⏱️ | 🧠 | |||
|---|---|---|---|---|---|---|
| mathgraph | ✋ | 4 ms | 97.8 MB | |||
| nanoda | ✋ | 11 ms | 4.1 MB | |||
| nanobruijn | ✋ | 16 ms | 25.8 MB | |||
| ind-models | 💥 | 119 ms | 109.8 MB | |||
| official | ✋ | 53 ms | 66.9 MB | |||
| lean4lean | ✋ | 59 ms | 97.8 MB | |||
| evmlean | ✋ | 1.6 m | 386.5 MB | |||
| mini | 🚫 | 49 ms | 73.9 MB | |||
| sokonanoda | ✋ | 4 ms | 96.1 MB | |||
| nanoclo | ✋ | 7 ms | 63.5 MB | |||
| zignodamus | ✋ | 5 ms | 8.9 MB | |||
| vow-lean-kernel | ✋ | 867 ms | 9.9 MB | |||
| official-v4.28.0 | 👍 | 63 ms | 73.2 MB | |||
| still-nanoda | ✋ | 11 ms | 3.7 MB | |||
| kiota | ✋ | 12 ms | 16.9 MB | |||
| rpylean | ✋ | 11 ms | 27.1 MB | |||
| nyaya | ✋ | 34 ms | 14.8 MB | |||
| parse-only | 👍 | 42 ms | 65.1 MB |