Sizes: the project's tables learn what its types weigh, before anything asks #222
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/222-sizes-the-project-s-tables-learn-what-its-type"
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?
Sizes: the project's tables learn what its types weigh, before anything asks
Along the way:
Touches 22 files:
src/rust/crates/ck_driver/src/compile/cross_module.rs,src/rust/crates/ck_driver/src/compile/frames.rs,src/rust/crates/ck_driver/src/compile/mod.rs,src/rust/crates/ck_driver/src/compile/recursion.rs,src/rust/crates/ck_driver/src/compile/sizes/fns.rs,src/rust/crates/ck_driver/src/compile/sizing.rsand 16 more.Happy with this. I ran the suite against the branch and it is green,
src/rust/crates/ck_driver/tests/architecture/values.rsincluded.@ -42,0 +41,4 @@////// `pub(super)` so the S1 size-construction rule (`mod sizes`) scans the same/// file set rather than growing a third copy of this walk.pub(super) fn all_production_sources() -> Vec<(PathBuf, String)> {all_production_sourcesreads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?Split. The decision in
all_production_sourcesis a pure function now, with its own tests insrc/rust/crates/ck_driver/tests/architecture/values.rs.Second read on
src/rust/crates/ck_driver/tests/architecture/values.rs. Nothing to add.`frame_layout` computed a `FrameLayout { slots, size }` and two of its three callers threw the slots away through a `frame_size` wrapper; the third re-folded the raw slots at join time, a whole compile after they were known. Both halves of that are the same defect: one law, more than one place it runs. `frame_size` is deleted. It was `frame_layout(…).size` and nothing else — a second name for one answer (Law 2), and this module's own header is a record of what a second answer to a size question costs. Its three callers (`sizes::fns`, `recursion`, `arena_size`) read the total off the layout they lay out. The unit test that asserted the two agreed is replaced by the invariant that made them agree: the plex total IS the Σ of its slots' sizes. `LoweredUserFn` carries `declared_frame: FrameLayout` in place of `frame_slots: Vec<Ty>`. The layout is laid out by `frames::declared_frame` where the slots exist — inside the lowering walk, beside `TypeckResults::frame_slots` — and `FrameReport::join` READS it. `join` no longer takes a `TypeLayout` at all, which is the point: there is no longer a place where a declared frame could be computed differently from the one the fn carries. Not done, and why: `Tables.frames: BTreeMap<FnId, FrameLayout>` as designed is not implementable without a behaviour change. The set the reconciliation joins over is the LOWERED set — stdlib bodies, provider and arena fns, and LC6 mono stamps whose `FnId`s are minted DURING lowering (`mono::alloc_fn`) — while the type cascade walks only app-owned, surface-filtered fns. A table filled in the cascade would be missing rows for exactly those, and a lookup-with-fallback would be two paths that must agree, which is the shape plan.md §6 records a live regression taking. The layout is stored on the fn that owns it instead, which is the same "stop discarding it" with no set to get wrong. Verified: `tests/frame_pin/*` byte-identical for all 36 corpus apps (`--test frame_reconcile`, no containers); `ckc sizes` + `ckc check` over all 146 apps of both corpus roots byte-identical, stdout/stderr/exit; all 36 `tests/sizes_pin/*` sizes sections byte-identical. Ratchets: none moved — no number changed. Gates: fmt clean; clippy --workspace --all-targets -D warnings, 0; architecture 39/39; ck_types arena_size 133/133.`capture_and_subst` binds a generic impl head against the call's concrete operands — `sa = 4` from `String<4>` against `impl Concat<String<sa>, String<sb>, String<sa + sb>>` — and const-evaluates the output head to the exact `String<6>`. It then used that substitution once, to type the call, and dropped it. Every monomorphisation of every generic impl in the program was computed and thrown away. `TypeckResults::impl_insts: BTreeSet<ImplInst>` is where it lands: the impl twin of `generic_insts`, `(trait, concrete head args in declaration order)`, recorded at the same statement that records `resolved_method`. A SET, not a per-call map — two calls at the same widths are one instantiation, and what the size table will want is the instantiations a program contains. `BTree`, so the sweep order is the same in every process. The head args are read out of `capture_and_subst`'s own substitution map, before it is consumed as a rewrite, so the instantiation recorded and the types the call is given cannot be two different bindings. Threaded through `substituted()` exactly as `generic_insts` is, and re-collected rather than mapped in place: substitution can make two rows equal, and a set holding both would claim two instantiations where the program has one. NOTHING reads it. Dispatch is unchanged, the coherence key is still wildcarded, and the compiler accepts and rejects exactly what it did. The open design question — is the OUTPUT substitution available in a form a row can store? — is answered YES, measured on the real compiler. Probing `ml_tictactoe` recorded 43 instantiations, six of them Concat: Concat[String<64>, String<64>, String<128>] Concat[String<128>, String<64>, String<192>] Concat[String<192>, String<64>, String<256>] Concat[String<256>, String<64>, String<320>] The computed widths are there, exact. The design's doubt came from `unify_value_slots` capturing only the `Param` shape, but that is the CAPTURE direction (head → env); the output is computed by the separate `subst_and_eval_ty` branch, which folds `sa + sb` through the coherence-resolved `Add` impl. Slice 4 is implementable as designed. Cell: `method_call_tests` pins the plumbing claim on the non-size-generic form (`impl Convert<Celsius, Fahrenheit>` ⇒ the output-only `U` is recorded) — the size-generic branch is the same code with `subst_and_eval_ty` folding arithmetic whose values `tests/sizes_pin` already gates end to end. The suite is a `#[path]` sibling (the `coherence` / `coherence_tests` pattern) to hold `method_call.rs` under its R3 cap. Verified inert: `ckc sizes` + `ckc check` over all 146 apps of both corpus roots byte-identical; all 36 sizes-pin sizes sections byte-identical; `tests/frame_pin/*` byte-identical. Ratchets: none moved. Gates: fmt clean; clippy --workspace --all-targets -D warnings, 0; architecture 39/39; ck_types lib 270/270.