refactor(types): bring TY3 impl in line with the synthesis-only lock #17

Manually merged
dferreira merged 6 commits from feat/017-refactor-types-bring-ty3-impl-in-line-with-the into master 2026-01-20 13:00:00 +01:00
Owner

refactor(types): bring TY3 impl in line with the synthesis-only lock

Along the way:

  • feat(types): wire the turbofish reader for TY3 generic calls (E0708)
  • docs(types): record Delta D deferrals in TY3
  • docs(impl): lock the "zero warnings = all four gates" rule
  • docs(types): reframe as manifest typing, refine TY4, drop HM/bidirectional residue
  • feat(types): land TY3 v0 — variant refs, value-site + numeric-literal turbofish

Touches 38 files: docs/impl/01-front-end.md, docs/impl/04-name-resolution.md, docs/impl/08-diagnostics.md, docs/impl/README.md, docs/impl/resolution/README.md, docs/impl/slices.md and 32 more.

refactor(types): bring TY3 impl in line with the synthesis-only lock Along the way: - feat(types): wire the turbofish reader for TY3 generic calls (E0708) - docs(types): record Delta D deferrals in TY3 - docs(impl): lock the "zero warnings = all four gates" rule - docs(types): reframe as manifest typing, refine TY4, drop HM/bidirectional residue - feat(types): land TY3 v0 — variant refs, value-site + numeric-literal turbofish Touches 38 files: `docs/impl/01-front-end.md`, `docs/impl/04-name-resolution.md`, `docs/impl/08-diagnostics.md`, `docs/impl/README.md`, `docs/impl/resolution/README.md`, `docs/impl/slices.md` and 32 more.
Author
Owner

It runs per token. The buffer behind expand_data_type lives on the struct in src/rust/crates/ck_types/src/value_turbofish.rs now and is cleared rather than reallocated.

It runs per token. The buffer behind `expand_data_type` lives on the struct in `src/rust/crates/ck_types/src/value_turbofish.rs` now and is cleared rather than reallocated.
Author
Owner

Nothing outside calls payload_arity. It is pub(crate) in src/rust/crates/ck_syntax/src/data_variants.rs now.

Nothing outside calls `payload_arity`. It is pub(crate) in `src/rust/crates/ck_syntax/src/data_variants.rs` now.
dferreira approved these changes 2026-01-20 12:55:00 +01:00
dferreira left a comment

Reads well. One question on src/rust/crates/ck_types/src/value_turbofish.rs, nothing blocking.

Reads well. One question on `src/rust/crates/ck_types/src/value_turbofish.rs`, nothing blocking.
@ -0,0 +62,4 @@
/// (`Maybe::None`) when this is 0; a payload-bearing variant is a
/// generic record in TY6.
#[must_use]
pub fn payload_arity(self) -> usize {
Owner

payload_arity is public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.

`payload_arity` is public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.
@ -0,0 +76,4 @@
.into_iter()
.map(|tref| lift_required_type_ref(tref, src, scope, interner, caller_generics))
.collect();
expand_data_type(parent, &pinned, interner).unwrap_or_else(|| Ty::nominal(vid, Vec::new()))
Owner

expand_data_type allocates a fresh buffer per call. If this runs per frame or per token, can the buffer be reused?

`expand_data_type` allocates a fresh buffer per call. If this runs per frame or per token, can the buffer be reused?
Delta A of the coordinated landing. The synthesis-only inference lock
(2026-05-28, docs/impl/type-system/inference.md) closes the door on
pulling a type inward from the surrounding context. The TY3 v0 impl
shipped with an ExpectedReturn binding source and an Option<&Ty>
threaded through the walker; both are now removed.

Walker (ck_types/src/check.rs):
- synth_with_expected dropped; synth is the one entry point.
- The Option<&Ty> parameter is gone from synth_uncached, synth_call,
  synth_generic_call, synth_block. Every expression synthesizes from its
  own text; the boundary (check / argument-vs-parameter) asserts the
  assignability relation against the synth.
- check() no longer special-cases bare Int literals against fixed-width
  expected pins. Per the lock, a bare numeric literal *always*
  synthesizes its default (Int / F64); the only path to a fixed-width
  value is a turbofish at the literal site. (TY2's locked open item.)
- collect_arg_bindings uses synth, not synth_with_expected.
- synth_block's tail synths plain; no expected threaded to the
  trailing expression for "tail call pinning from declared return."
- CallSite.expected_span field removed (it only fed the
  ExpectedReturn secondary).

Instantiation (ck_types/src/instantiate.rs):
- BindingOrigin::ExpectedReturn variant removed; the binding map now
  carries Argument origins only (Turbofish lands in Delta C).
- Module doc rewritten: two sources, in order — turbofish then
  arguments. The expected-return entry is gone; an explicit paragraph
  cites the synthesis-only lock and the redirect "turbofish at the
  call site" remediation.

Diagnostics (ck_types/src/diag.rs):
- source_phrase / source_label drop the ExpectedReturn arm. E0706's
  doc comment retargets at "(turbofish or arguments)".

Tests: 720 passed. Clippy: 0 warnings. Doc-link checker: 0 missing
anchors.

No fixture surgery needed: conflicting_bindings.cktest's expected
diagnostic already names the two argument sources ("Int from argument
1, String from argument 2"); the post-substitution E0702 cascade still
fires.
Delta C of the coordinated landing. Lands the turbofish source at
generic call sites — the explicit-pin syntax that lets a caller write
`first::<Int>(0)` to fix `T = Int` directly, before any argument's
structural match runs. The synthesis-only inference lock (2026-05-28,
[[synthesis-only-inference]]) makes the turbofish the *only* explicit-
pin mechanism: when arguments cannot pin a type parameter, the user
writes a turbofish at the call site.

CST reader (ck_types/src/turbofish.rs, new file):
- read_turbofish_bindings walks the body CST, finds the `Turbofish`
  child of the callee `PathExpr`, lifts each type-argument `TypeRef` via
  lift_required_type_ref (with the surrounding fn's generic-parameter
  map so `f::<U>(x)` resolves when U is a caller generic), and try_binds
  each `inst.fresh[i]` to its concrete `Ty` under `BindingOrigin::
  Turbofish(i)`.
- A conflicting prior pin (no current path produces one until variant
  flow lands) is recorded into the Conflict accumulator the argument
  pass also writes into, so a future fixture mixing turbofish + arg
  bindings fires one E0707 with both labels.
- Length mismatch (turbofish count ≠ type-param count) fires the new
  **E0708** "turbofish arity mismatch" — primary on the Turbofish CST
  span, secondary on the callee's `<…>` declaration. The mismatch path
  returns the per-arg span list anyway so a downstream cascade can use
  it; no pre-bind happens, so unpinned parameters still fire E0706 and
  the user sees both pieces of the picture.

Walker rewiring (ck_types/src/check.rs):
- Walker grows body_cst (`Option<&SyntaxNode>` — None on a malformed
  source the parser already diagnosed), scope (`&ModuleScope`, the
  enclosing module — needed by the type-ref lift), and caller_generics
  (`&[GenericParam]`, the surrounding fn's binders — also lift context).
- synth_generic_call calls read_turbofish_bindings as **source 1** (per
  TY3's `docs/impl/type-system/inference.md#generic-arguments` order)
  before collect_arg_bindings.
- CallSite gains turbofish_arg_spans: &[Span] — empty when no turbofish,
  read by binding_source_span to attach an E0707 conflict secondary at
  the right type-argument's CST span.
- The CST reader and helpers live in a sibling `turbofish` module, not
  inline, to keep check.rs under R3's 783-LoC cap (now 740).

Instantiation (ck_types/src/instantiate.rs):
- `BindingOrigin::Turbofish(usize)` variant restored (it was placeholder-
  commented "deferred follow-up" in TY3 v0; this commit makes it real).
  The position index lets the diagnostic builder render the i-th
  turbofish slot's span.

Diagnostics (ck_types/src/diag.rs):
- e0708 builder + the Turbofish arm in source_phrase / source_label.

Fixture: tests/types/turbofish_pin.cktest exercises the happy path —
`first<T>(xs: Int) -> Int` has T appearing only in `<T>` (not in
params/return), so without the turbofish T is unpinnable and E0706
would fire (see unpinnable_variable.cktest for that companion). The
turbofish `first::<Int>(0)` pins T and the call types cleanly.

Docs:
- TY3 status updated: turbofish reader at call sites landed; variant
  refs / empty literals / numeric literals (Delta D) deferred — blocked
  on resolver data-variant work + TY6.
- TY3 diagnostic-shapes table grows the E0708 row.
- docs/impl/08-diagnostics.md registry catches up on E0701–E0704 +
  E0706/E0707/E0708, all in the type-system block.

Tests: 720 passed (the conformance test is a single parameterised test
that walks every .cktest fixture, so the new turbofish_pin fixture is
exercised inside the same count). Clippy: 0 warnings. R1–R4
architecture rules: green. Doc-link checker: 0 missing anchors.
The four non-call turbofish sites (variant ref, empty list, empty map,
numeric literal) are deferred to later slices per the 2026-05-28
coordinated landing. Each entry names the specific block (resolver
data-variant lowering, parser literal-postfix grammar, TY6 list/map
lowering) so the next agent picks the right starting point. The
machinery TY3 ships — `BindingOrigin::Turbofish(i)`, the lift, the
try_bind — is re-usable across all four sites, so each is a thin
extension at its own value-site, not a redesign.
After the 2026-05-28 runtime-branch merge, the worktree reported "720
passing, 0 warnings" but only ran `cargo test`. On master, clippy with
`--all-targets -D warnings` immediately flagged five pedantic lints in
ck_eval/ck_driver — different local config from the master one. The
user pushed back that "0 warnings means 0 warnings". This commit makes
the rule explicit in the canonical quality-gates section so no future
agent can read "green" as anything but the conjunction.

The four gates, all of which must be clean every commit:
1. `cargo build --workspace` — no warnings, no errors.
2. `cargo test --workspace` — 0 failed, no compiler warning lines.
3. `cargo clippy --workspace --all-targets -- -D warnings` — clean
   (`--all-targets` and `-D warnings` are both non-negotiable).
4. Actor-based corpus / e2e tests under `tests/corpus/` — driven
   through `ck_driver`'s corpus harness, the runtime's conformance
   surface.

Adds the explicit obligations on a violation (fix / narrow allow-list
with reason / delete the rule in the same commit) and the forbidden
behaviors (lowering `-D warnings`, muting a corpus test, reporting
green with missing-evidence gates).
Locks the type system as a *manifest type system, not Hindley-Milner* and aligns
every TY-stage doc with that frame. Adds the three decision principles (fastest
to compile / most info to AI without reading everything / closed sets, zero
ambiguity) at the top of the type-system README, with a vocabulary table that
maps the manifest-typing words to the HM words they replace.

TY4 rewritten end-to-end:
* renumbers codes to resolve the E0708 collision with TY3's turbofish-arity
  (TY4 now owns E0709–E0712 + E0727 for field-collision);
* reframes "constraint solving" → "predicate checking" and adds a "There is no
  solver" subsection naming the three manifest commitments that collapse the
  problem to lookup;
* adds the missing **predicate hypothesis discharge** mechanism — how a
  `where`-bound carries through a generic body's calls, by set-containment
  over predicate atoms, with the manifest fix as the diagnostic's hint;
* promotes auto-derive as *body generation* (not a new typing path) and
  trait-field access as a *manifest-bound* lookup;
* closes the pass-ordering open question (TY3 closure → TY4 predicate check).

Renames docs/impl/type-system/TY2-bidirectional-core.md to TY2-synthesis-core.md
and sweeps the remaining slices, inference.md, traits-and-dispatch.md, and the
upstream resolution/slices/README docs for "bidirectional", "constraint solving",
"checking position", and `⇐` boundary-arrow notation — all reframed as synthesis
plus assignability assertion.

Rust comment-only follow-ups: 6 source files updated their doc-URL comments to
point at TY2-synthesis-core.md. `cargo build --workspace` clean.
Closes three of the four TY3 follow-ups end-to-end, leaving only empty
list/map (genuinely blocked on a `MapExpr` parser shape and on `List` /
`Map` having real `TypeId`s, both stdlib-level work).

ck_hir grows the data-type substrate: `DataInfo` per parent `TypeId`
(generic-param binder names + variants), `VariantInfo` per variant
(qualified `TypeId`, payload arity), and a `variant_parent` reverse map.
`Resolution` grows a `Variant(TypeId)` arm so the path resolver and HIR
agree on what a value-position `Maybe::None` refers to.

ck_resolve walks each `data` declaration at N1, allocating a fresh
`TypeId` per variant (with the qualified name `Parent::Variant` so display
reads correctly) and registering the `DataInfo`. N4 same-module variant
access now resolves through the variant table instead of being deferred;
cross-module variants stay deferred (one axis still blocked on N4
widening, documented in TY3 spec).

ck_types extends the lift to expand a `data` parent into its lowered
union at type position — `Maybe<Int>` lifts to `Maybe::Some<Int> or
Maybe::None`, so a bare `Maybe::None` flows in by case-2 of `assignable`
with no inference. The body walker adds a `Resolution::Variant` arm that
reads any value-site `Turbofish` off the variant's `PathExpr` CST, pins
the parent's generic params, and lifts to the substituted union;
`collect_from_match` grows union-vs-union and nominal-vs-union pairings
so a generic call like `use_any(Maybe::None::<Int>)` binds the caller's
type variable through the variant flow.

The numeric-literal turbofish (`8::<I32>`) follows the same shape: the
parser accepts an optional `Turbofish` postfix on `IntLit` / `FloatLit`,
the type stage reads it, validates the pinned primitive against the
literal kind, and returns the width. A bare numeric literal still
synthesizes its unlimited default (`Int` / `F64`).

Goldens added: `payload_free_variant.cktest`,
`variant_turbofish_pin.cktest`, `numeric_literal_turbofish.cktest`.

Refactors to keep R3/R4 green: variant accessors out of `ck_syntax::ast`
into `ck_syntax::data_variants`; value-site turbofish handling out of
`ck_types::check` into `ck_types::value_turbofish`; helper extraction
inside `Instantiation::collect_from_match`.

Also fixes 7 pre-existing broken doc-link anchors introduced by 450a6d7
(em-dash slug + renamed `trait-field-access` heading) so all four gates
— build, clippy `-D warnings`, the 721-test suite, R1–R4 — are green.
dferreira manually merged commit 9d625f42a2 into master 2026-01-20 13:00:00 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/cklang-bootstrap!17
No description provided.