The let slot's fit gate #230

Manually merged
tvidal merged 1 commit from feat/230-the-let-slot-s-fit-gate into master 2026-09-16 09:30:00 +02:00
Owner

The let slot's fit gate

Along the way:

  • coerce: the let slot took any bound it was given

Touches 8 files: docs/03-types.md, docs/examples/bounded_narrowing_refuses/Main.ck, src/rust/crates/ck_driver/tests/capability.rs, src/rust/crates/ck_types/src/check_block.rs, src/rust/crates/ck_types/src/coerce.rs, tests/capability/a_bounded_list_write_through_an_impl_head.cktest and 2 more.

The `let` slot's fit gate Along the way: - coerce: the let slot took any bound it was given Touches 8 files: `docs/03-types.md`, `docs/examples/bounded_narrowing_refuses/Main.ck`, `src/rust/crates/ck_driver/tests/capability.rs`, `src/rust/crates/ck_types/src/check_block.rs`, `src/rust/crates/ck_types/src/coerce.rs`, `tests/capability/a_bounded_list_write_through_an_impl_head.cktest` and 2 more.
Author
Owner

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

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

Measured. It is a named constant in src/rust/crates/ck_driver/tests/capability.rs now, with a line saying how, at src/rust/crates/ck_driver/tests/capability.rs:701.

Measured. It is a named constant in `src/rust/crates/ck_driver/tests/capability.rs` now, with a line saying how, at src/rust/crates/ck_driver/tests/capability.rs:701.
tvidal approved these changes 2026-09-16 09:25:00 +02:00
tvidal left a comment

Happy with this. I ran the suite against the branch and it is green, src/rust/crates/ck_types/src/coerce.rs included.

Happy with this. I ran the suite against the branch and it is green, `src/rust/crates/ck_types/src/coerce.rs` included.
@ -654,0 +698,4 @@
// re-pinned is that codegen wall: `ckc build` names a different span depending on
// where the concat sits, so it is not one form, and a cell naming a mechanism
// nobody measured would be worse than no cell.
const BROKEN_CELL_COUNT: usize = 45;
Owner

Where does the number beside BROKEN_CELL_COUNT come from? It reads as measured rather than chosen, and nothing on the line says which, at src/rust/crates/ck_driver/tests/capability.rs:701.

Where does the number beside `BROKEN_CELL_COUNT` come from? It reads as measured rather than chosen, and nothing on the line says which, at src/rust/crates/ck_driver/tests/capability.rs:701.
@ -304,0 +328,4 @@
#[test]
fn a_literal_measure_wins_over_the_declared_bound() {
let (list_id, elem_id) = ids();
let elem = Ty::nominal(elem_id, Vec::new());
Owner

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

`elem` allocates a fresh buffer per call. If this runs per frame or per token, can the buffer be reused?
`check_block::coerce_into_slot` fit-gated a `let x: T = e` only when the
initializer had a LITERAL measure and returned early otherwise:

    let Some(measure) = self.literal_measure(value) else { return };

`literal_measure` answers for a list literal and a string literal and nothing
else, so a local, a call result or a `++` never reached the gate and the
annotation was taken verbatim. `let b: String = a ++ a` over a `String<128>`
was accepted; so was `let xs: List<Int, 4> = ys` over a `List<Int, 8>`. The
bound is the capacity every later stage reserves from and `len <= size` is the
invariant `Types.ck` states on the declaration line, so a narrower annotation
asserted a number the compiler never computed — Law 1 read backwards.

An unmeasurable value now measures its own DECLARED bound, the largest content
it could carry, which is conservative in the only direction that is safe: a
widening still fits and a narrowing the compiler cannot prove is E0715. That is
the same reader `coerce_container_into` already handed the turbofish surface,
so `docs/values.md` rule 3's "one operation, two surfaces" is now what the
compiler implements rather than what it claimed. The composition lives in
`coerce::slot_measure` — the clause's own home — so neither surface carries a
copy of the law.

WHAT IT COST, and the replacement, proved before landing. `List` has no `set`,
so `let joined: List<Int, 4> = xs.take(i) ++ [v] ++ xs.drop(i + 1)` was the only
spelling of a bounded-list WRITE and it is now E0715. It was never a working
write: it type-checked and died at `unsupported Floor IR form`. The spelling
that RUNS names the bound in an IMPL HEAD — where `Prelude.ck`'s own
`impl Concat<List<Int, sa>, List<Int, sb>, List<Int, sa + sb>>` already names
one — and re-presents the descriptor through the newtype constructor, the `List`
twin of `Types.ck`'s `String((| wide.value.position, wide.value.len |))`
round-trip. Its destination is a `let`-bound list literal rather than an
acquisition, because `Alloc::os_acquire`'s inventory is frozen (architecture M1)
and the slot model is the end state that freeze exists for.

Cells: `a_bounded_list_write_through_an_impl_head` lands (`runs: 42`), so
CELL_COUNT 454 -> 455. Two cells flip BROKEN -> WORKS and are renamed to what
they now pin, so BROKEN_CELL_COUNT 47 -> 45:
`a_let_slot_narrows_a_computed_bound_unchecked` ->
`a_let_slot_refuses_a_narrowing_it_cannot_prove`, and
`list_concat_bound_narrows_through_a_let_slot` ->
`list_concat_bound_does_not_narrow_through_a_let_slot`.
`list_concat_bound_does_not_narrow_through_the_turbofish` loses its "THE OWNER
CALL SURVIVES" section: the fork it described is the one this closes.

The book pins it too — ch.3's "Narrowing is refused, not truncated" showed only
the turbofish, and its fixture now carries the `let` slot beside it, so the
chapter's "the two surfaces coerce the same way" is machine-read in both
directions.

Not re-pinned: the codegen wall the old cell used to stop at. `ckc build` names
a different span depending on where the concat sits, so it is not one form, and
a cell naming a mechanism nobody measured would be worse than no cell.
tvidal manually merged commit 1244e7ca57 into master 2026-09-16 09:30:00 +02: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!230
No description provided.