The let slot's fit gate #230
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/230-the-let-slot-s-fit-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The
letslot's fit gateAlong the way:
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.cktestand 2 more.It runs per token. The buffer behind
elemlives on the struct insrc/rust/crates/ck_types/src/coerce.rsnow and is cleared rather than reallocated.Measured. It is a named constant in
src/rust/crates/ck_driver/tests/capability.rsnow, with a line saying how, at src/rust/crates/ck_driver/tests/capability.rs:701.Happy with this. I ran the suite against the branch and it is green,
src/rust/crates/ck_types/src/coerce.rsincluded.@ -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;Where does the number beside
BROKEN_CELL_COUNTcome 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());elemallocates 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.