Surface type-system soundness: type-check = expr bodies + cycle guard + Lean cardinality preservation - #9
Merged
Merged
Conversation
…ndness) Soundness hole: the Typing stage only checked clause (| p <-> q) bodies. A combinator/composition body (= f ; g, = iter T h, = p, = ~q) was never type-checked -- neither internally nor against the declared signature. So check() accepted ill-typed and wrong-cardinality isos: iso f : Bit <-> Bit = swap2 -- body is Bit*Bit<->Bit*Bit (|.|=4 != 2) iso f : Bit <-> Bit = notb ; swap2 -- ill-typed composition iso f : Bit <-> Bit = iter Bit notb -- non-numeral counter (T0601 never surfaced) The declared signature -- used for value marshaling and as the iso type for downstream composition -- was thus a lie: eval/compile could then crash or produce out-of-domain values. This is exactly the Pi/Theseus rule a combinators synthesized type must equal its declared boundary (and hence |A| = |B|, a prerequisite for the denotation being a bijection). Fix: after the clause checks, elaborate every = expr body and require type_of(body) == (lower(lhs), lower(rhs)); an internally ill-typed body (type_of Err) or a mismatch is T0102, and combinator errors (T06xx) the elaborator localizes are now surfaced. Reuses the core type_of so id / iter / fold rules are handled once, authoritatively. Exposed a real latent bug: the prelude test asserted `tofMid = asc ; (toffoli * idb) ; ~asc` was clean, but it is ill-typed (asc yields a 2-bit group; toffoli needs 3 -- cardinality 8 vs 16). Replaced with a well-typed tofUse = toffoli ; toffoli that still exercises prelude toffoli in an = expr body. Tests: 4 reproducing cases in typing.rs (cardinality mismatch, ill-typed composition, surfaced T0601, and valid bodies stay clean).
…flow) A reversible iso may only recurse through a structural combinator (iter/fold); naming itself in an = expr body -- directly (iso f = f) or mutually (f = g ; g = f) -- is ill-founded (no terminating denotation). Elaboration recursed on the reference with no guard, so it stack- overflowed and SIGABRTed. Harmless before (check never elaborated = expr bodies) but reachable now that typing does, and a latent crash for any direct elaborate caller regardless. Thread a visiting stack of iso names through elab_iso/elab_expr; re- entering one is reported as T0102 (an ill-formed iso) instead of looping. Reproducing tests: self and mutual recursion now yield T0102, not a crash.
Add the Pi/Theseus structural type-soundness fact to Reversibility.lean: every well-typed combinator relates types of EQUAL cardinality (card_preserved, a corollary of denote_bijective via Fintype.card_congr), the semiring realization on cardinalities (card_unit/bit/prod/sum), and the sharp contrapositive no_iso_of_card_ne: there is NO iso between types of different cardinality. This is exactly the obligation the surface checker now enforces on = expr bodies -- an iso body type must equal its declared signature, hence |A| = |B|. Accepting f : Bit <-> Bit = swap2 (|Bit|=2 vs |Bit*Bit|=4) would assert an iso no_iso_of_card_ne proves cannot exist. lake build green; AxiomGuard clean (propext/Classical.choice/Quot.sound).
# Conflicts: # crates/theseus-check/src/elaborate.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Discovers and fixes two soundness holes in the surface type checker (
theseus-check), with reproducing tests, and adds the corresponding Lean type-soundness property. Grounded in the Pi (James–Sabry, Information Effects, POPL'12) and Theseus (RC'14) type theory: a well-typed isof : A <-> Bmust denote a bijection, which requires|A| = |B|and that the body's synthesized type equals the declared boundary.Bug 1 —
= expriso bodies were never type-checkedThe Typing stage (
T0102) only walked clause patterns. A combinator/composition body (= f ; g,= iter T h,= p,= ~q) was never checked — neither internally nor against the declared signature — socheckaccepted, silently:The declared signature — used for value marshaling and as the iso's type for downstream composition — was thus unchecked, so
eval/compilecould crash or produce out-of-domain values.Fix: after the clause checks, elaborate each
= exprbody and requiretype_of(body) == (lower(lhs), lower(rhs)); an internally ill-typed body or a signature mismatch isT0102, and the combinator diagnostics (T06xx) the elaborator already localizes are now surfaced. Reuses the coretype_ofsoid/iter/foldrules are handled once, authoritatively.This immediately caught a real latent bug in the checker's own prelude test:
tofMid = asc ; (toffoli * idb) ; ~ascwas asserted clean but is ill-typed —ascyields a 2-bit group whiletoffolineeds 3 (cardinality 8 vs 16). Replaced with a well-typedtofUse = toffoli ; toffolithat still exercises preludetoffoliin an= exprbody.Bug 2 — ill-founded
= exprrecursion crashed the checkerA reversible iso may only recurse through a structural combinator (
iter/fold); naming itself in an= exprbody — directly (iso f = f) or mutually (f = g ; g = f) — is ill-founded. Elaboration recursed with no guard and stack-overflowed / SIGABRTed. Harmless before (check never elaborated= exprbodies) but reachable now, and a latent crash for any directelaboratecaller.Fix: thread a
visitingstack of iso names through elaboration; re-entering one is reported asT0102instead of looping.Lean — cardinality preservation (
theseus-proofs)Adds the Pi/Theseus structural type-soundness fact to
Reversibility.lean:card_preserved : (i : Iso a b) -> Fintype.card (denoteTy a) = Fintype.card (denoteTy b)— every combinator relates equinumerous types (a corollary ofdenote_bijectiveviaFintype.card_congr).card_unit/card_bit/card_prod/card_sum—denoteTyrealizes the type-level semiring on cardinalities.no_iso_of_card_ne— the sharp contrapositive: there is no iso between types of different cardinality. This is precisely the obligation the checker fix enforces on= exprbodies; acceptingf : Bit <-> Bit = swap2would assert an iso this theorem proves cannot exist.lake buildgreen; AxiomGuard clean (propext/Classical.choice/Quot.soundonly).Tests / verification
typing.rs(cardinality mismatch, ill-typed composition, surfacedT0601, valid bodies stay clean, self-recursion, mutual recursion).nix flake checkgreen (fmt + clippy--all-targets --deny warnings+ nextest); conformance green.Not fixed (reported for a later, careful pass)
The clause path (Typing + Coverage + Linearity) looks sound — each side is partitioned, forcing a bijection and hence
|A| = |B|. Not exhaustively audited:linearity.rs(var drop/duplication) and a cosmeticis_un_typelaxness intyping.rs(not exploitable — Names gates it).