#108 sub-fold (c): opaque as a type-erasure sink. Two implicit assignability rules + the reinterpret casts sort's impl relies on. rule 1 `*T -> *opaque` IMPLICIT — any pointer is the universal void-pointer. harec type_is_assignable pointer arm (ref/harec/src/types.c:1053: `case STORAGE_OPAQUE: break;` — the referent need not match). rule 2 `[]T -> []opaque` IMPLICIT — any slice is the erased slice; {ptr,len,cap} header is normal, byte stride supplied at runtime. harec slice arm (types.c:1094). Both fire only when the destination element is opaque, so they are inert on the opaque-free selfhost corpus. Rule-10 (per-rule, empirical): rules 1 & 2 are CSTAGE-ONLY. cstage type_assignable gains the sink; the wwstage check.ww isassignable is a resolve-only AST approximation that returns "can't tell, stay quiet" (confident=false) for a ptr/slice whose element it cannot match, so it already ACCEPTS every form (let-init AND call-arg). Verified: w6c_ww compiles each probe source exit 0, byte-identically to w6c. cstage rejected these before this change; no ww twin is needed (same align-down precedent as 960/961's cstage-only arms). Casts: N_CAST is validation-free in BOTH stages (the checker never checks cast legality), so `[]opaque -> *u8` / `*opaque -> *u8`/`*i32` are already legal. The reinterpret CGEN needed NO change: cgexpr leaves the pointer in AX for both a slice (so slice->ptr naturally takes .ptr) and a pointer (ptr->ptr is a no-op). drew described the Hare idiom as `*[*]u8`; ww has no unbounded-array `[*]`, so the ww-faithful reinterpret target is `*u8` + uintptr stride arithmetic. cs==ww byte-id proven on every probe row. Array->[]opaque (harec array->slice decay, types.c:1080-1099) is deliberately EXCLUDED: ww has no implicit array->slice for any element type (`let s: []i32 = a` is rejected too — a slice is built only via an explicit `a[0:n]`), so there is no array->slice-header cgen. Accepting array->[]opaque alone would assign a fat array local into a 24-byte slot with no decay: a silent miscompile (rule 7). sort's caller passes a slice, so slice->[]opaque suffices. opaque is unused by the bootstrap → INERT → 990-997 stay byte-identical; combined.ww unchanged (no embedded source touched). New probe 962_opaque_assign_cast_run carries both dimensions per row (cstage build+run asserting type-erasure round-trips, AND a w6c-vs- w6c_ww .s byte-id gate — the 990-997 gates never exercise opaque, so the test pins rule-10 symmetry itself): rule1_implicit_ptr, rule2_implicit_slice, and sort_pattern (byte-swap via uintptr stride through []opaque, read back through the *opaque path and the original []i32 view). Probe binds call results before comparing to dodge a pre-existing inline-call-result-in-comparison cgen bug (#116 family, reproduces with zero opaque) — same dodge 960 uses.
10 KiB
10 KiB