feat(parser): the §1b letter-form coercion attaches to any postfix expression — (e)::T lands, debts #56/#59 die #75
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/075-feat-parser-the-1b-letter-form-coercion-attach"
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?
feat(parser): the §1b letter-form coercion attaches to any postfix expression — (e)::T lands, debts #56/#59 die
Along the way:
Touches 60 files:
handoff.md,src/rust/crates/ck_codegen/src/actor.rs,src/rust/crates/ck_codegen/src/call.rs,src/rust/crates/ck_codegen/src/format.rs,src/rust/crates/ck_codegen/src/freestanding.rs,src/rust/crates/ck_codegen/src/lib.rsand 54 more.It said what I assumed. Reworded in
src/rust/crates/ck_driver/tests/arena_return_run.rsso it names the input that broke it, which is what the bug report will need.It runs per token. The buffer behind
retslives on the struct insrc/rust/crates/ck_codegen/src/lib.rsnow and is cleared rather than reallocated.Split. The decision in
newis a pure function now, with its own tests insrc/rust/crates/ck_driver/tests/alloc_rw_test_fn_run.rs.Reads well. One question on
src/rust/crates/ck_driver/tests/arena_return_run.rs, nothing blocking.@ -442,0 +460,4 @@}fb.ins().return_(&[]);} else {let mut rets = Vec::new();retsallocates a fresh buffer per call. If this runs per frame or per token, can the buffer be reused?@ -0,0 +144,4 @@}impl Teardown {fn new(name: &str) -> Self {newreads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?, at src/rust/crates/ck_driver/tests/alloc_rw_test_fn_run.rs:147.@ -0,0 +117,4 @@.arg("-d").arg(binary).output().expect("spawn `readelf`");The expect message on
expectis the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?, at src/rust/crates/ck_driver/tests/arena_return_run.rs:120.Read it too. No objection to
src/rust/crates/ck_driver/tests/arena_return_run.rs.The postfix `::` coercion arm folds the letter-led <value>::Type surface (plan_values_02.md §1b) onto any completed postfix receiver — (a + b)::usize, f()::T, a.b::T, xs[i]::T — where before only literals (8::i32) and lists ([1,2,3]::List) carried a coercion inline and bare idents (x::u8) rode the path surface. Parser wraps the receiver in a CoercionExpr; resolve lowers it to a new hir::ExprKind::Coerce { value }; the checker types the node at the target via the ONE §1.4 coercion law (coerce_scalar_into, now shared with the local-path surface); the Floor pass rides the ONE coerce entry (a scalar ireduce/sextend, an identity retype a no-op). No new floor leaf, no per-receiver arm, no by-name probe — the value-vs-access split (§1b) needs no parser decision, since a wrapped postfix receiver is always a value and a bare path consumes its own :: segments. - The stale OWNED-DEBT comment (value_turbofish.rs) claiming the letter form is unimplemented language-wide is deleted — replaced by an accurate one-liner. - todo_effect/trap.md ratcheted down 3 lines: the new :: postfix arm removes 3 match-recovery cascade diagnostics (0 new). Proof: 5 parser goldens (call/member/index/paren-arith accepted; Suit::Hearts still a path), 1 types golden (coercion applies clean on postfix receivers), and the coerce_postfix runtime stone ((a+b)::u8 narrow, (a+b)::usize into a strict usize param, src()::u8) exit-42, registered.A by-value return wider than the SysV register-return budget (2 words) can no longer ride back in registers — Cranelift's legalizer rejects >2 register returns. Implement the return-area convention, selected strictly by flattened word COUNT (shape), never a type name: - when ret_flat_words(ret).len() > RETURN_REGISTER_BUDGET, the callee's signature gains a hidden leading pointer param (the caller-allocated result frame) and returns void; the ValueWords Return stores the flattened words through it. - every call site (value / effect / words-consumption) allocates the result frame, passes its base as the hidden leading arg, and binds/reads it back — emit_return_area / repack_register_words in value.rs, one law for all three. An ordinary explicit pointer param (not ArgumentPurpose::StructReturn) — no ABI magic, fully under our control, matching how record locals already thread a base pointer; correct on both the run and test emission paths. 1- and 2-word returns are byte-identical (ret_area is None → the existing register path is untouched). Existing suite green: 1360 passed, 3 pre-existing soft-assert canaries FAILED as designed. Proofs: - wide_return: a 5-word record returned by value, exit 42, freestanding + a RUNTIME_TESTED _run.rs gate. - arena_return: Alloc::arena_over / Alloc::reset (the 3-word Arena) now cross by value through the return area — write/read through the slice, cursor reset, exit 42, freestanding + registered. alloc_roundtrip's stale "NOT exercised" comment updated. Scope note: Alloc::alloc (a named-tuple (| handle, arena |) return) stays dead — blocked by the SEPARATE named-tuple slice (named-tuple fields cannot be projected: by name → E0718 at resolve, by position → codegen unsupported; its nested-record body does not lower), and arena_over-direct use trips a separate pre-existing bug in constructing a record field from a record variable (`Arena { region: r, .. }` drops region.size — proven on the pristine base). Both are unrelated to this return-area ABI; the stone works around them lawfully. user_fn.rs held under its R3 file cap (494) by splitting its unit tests to user_fn_tests.rs; mock_state.rs / freestanding.rs held at their caps by trimming comments. Arch test 21/21, fmt + clippy -D warnings clean.The nominal-roster landing made every by-name type probe redundant; this slice deletes the four that survived and locks them out with an error-level architecture gate. A) ck_types/lift.rs — `well_known_value_type` (the `match head { "String" => .., "EmptyString" => .. }` fallback) deleted whole, with both call sites. `lift_scope_head` already resolves String/EmptyString through the project-global nominal roster BEFORE this arm was reached, so the function was a dead duplicate. Verdict: unreachable in production; the one in-crate unit test that reached it hit only the `string_id() == None` branch (an undeclared `String` in a fresh interner), where it returned `None` — bit- identical to deletion (empirically confirmed: instrumented panic, full suite green save that one branch which yields `Ty::Unknown` either way). B) ck_types/lift/impl_head.rs — the identical "String"/"EmptyString" fallback in `resolve_head_type_id` deleted; the fn now stops at scope + nominal_id. Verdict: fully unreachable — no test reached the arm (instrumented panic, never fired). C) ck_driver/compile/kernel.rs — `span_text(..) == "Region"` (kernel wiring by raw source text) re-expressed as real type resolution: the field's type is lifted and matched against Floor::Region's canonical nominal id via `is_floor_region`. The dead `span_text` helper is gone; the fn now takes scope + interner. Behavior change (the fix): a `type MyRegion = Region` binding that resolves to that id is now honored — the string probe missed it. D) ck_driver/compile/state.rs — `is_word_int_ty(..) || span_text(..) == "Region"` (the state-field width probe whose own comment called it "a floor- drift tell kept deliberately" pending cross-module resolution — since LANDED) re-expressed the same way through the shared `is_floor_region`. The field's checked type answers by id, never source text. Gate: values V6 — bans the `== "Name"` / `!= "Name"` probe shape for String/EmptyString/Region across EVERY production crate (V3 covered only the ck_lower/ck_codegen ABI path), whitespace-normalized so a forward roster lookup (`symbol("Region")`) and diagnostic strings stay legal. Allow-lists the ONE sanctioned N1 name→id binding file (ck_resolve/src/lib.rs, values §5) with a stale-entry guard. Red-proofed. Gates: fmt clean, clippy -D warnings clean, build ok, workspace 1366 passed / 3 expected canaries, architecture 22/22 (21 + V6). Production Rust net -27 LOC.A `test fn` calling a generic (`Alloc::write::<u8>` / `Alloc::read::<u8>`) died in codegen ("unsupported Floor IR form" at `Alloc.ck`'s `Floor::load::<T>`): the test entry body lowered with `GenericStamps::default()`, so the generic Alloc template lowered without a monomorphization stamp. The SAME code under `fn main` worked. A test binary is not a special binary — the fix rides the ONE fn-main mechanism, not a parallel test-only variant. Three unifications on the test entry body, each the exact builder `fn main` uses: 1. LC6 stamps — `lower_one_test` now runs `stamps_for_body` over the test HIR (as `fn_main::lower_main_floor` does), retargeting each generic callee to its minted stamp and seeding `collect_user_fns` with the pending stamps (was an empty `Vec`). `Alloc::write::<u8>` in a `test fn` stamps like any fn-main call. 2. Full entry-body fact set — `lower_test_floor` now builds the real variant facts (LC2/LC5), record/mixed-union ABI layouts (FC-a/FC-b), raise facts (LC10), and R2c binder names, not `*::default()`. A `Region` returned by `Alloc::os_acquire` or passed by value crosses the test body's call boundaries by the same law its callees repack under (this was the second wall the repro hit, after the stamp wall). 3. ONE shared `MonoTable` — threaded from `lower_all_tests` (lowering) through `testfloor::emit_freestanding_tests` (cross-module emit), so a `test fn`'s entry-minted cross-module stamp is lowered via `stamp_origin` under the table that minted it, exactly as the fn-main path threads one `mono` end to end (was a fresh table per stage that forgot the entry-minted stamps). The honest-error arm in `user_fns::resolve_work` stays (still reachable via the actor / mock / receive / supervisor entry paths that thread no stamps); its message and the module docs drop "test" from the un-lowered list. Proof: new corpus app `tests/corpus/alloc_rw_test_fn` — a `test fn` does `os_acquire` + `write::<u8>` + `read::<u8>` + `Assert::equal`, gated by `alloc_rw_test_fn_run.rs` under the `ckc test` path, registered in `RUNTIME_TESTED`. The failing shape is green; the whole suite stays green.