wcc: opaque assignability sink + reinterpret-cast verify (#108)
#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.
This commit is contained in:
@@ -356,6 +356,33 @@ type_assignable(Type *dst, Type *src)
|
||||
return pa == NULL && pb == NULL;
|
||||
}
|
||||
|
||||
/* #108(c): opaque is a type-erasure sink. Any pointer is assignable
|
||||
* to *opaque, and any slice to []opaque — the universal void-pointer
|
||||
* and erased slice. harec type_is_assignable: ptr→*opaque at ref/harec/src/
|
||||
* types.c:1053 (`case STORAGE_OPAQUE: break;` inside the pointer arm,
|
||||
* i.e. the referent need not match), slice→[]opaque at :1094 (`if
|
||||
* (to_secondary->storage == STORAGE_OPAQUE) return true;`).
|
||||
*
|
||||
* Array→[]opaque (harec's STORAGE_POINTER-to-array and array→slice
|
||||
* decay, types.c:1080-1099) is deliberately EXCLUDED: ww has no
|
||||
* implicit array→slice conversion for any element type (`let s:
|
||||
* []i32 = a` is rejected too — a slice is built only via an explicit
|
||||
* `a[0:n]` op), 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 is the only shape it needs.
|
||||
*
|
||||
* Both rules fire only when the destination element is opaque, so
|
||||
* they are inert on the opaque-free selfhost corpus. */
|
||||
{
|
||||
Type *du = (dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
Type *su = (src->kind == TY_NAMED) ? src->under : src;
|
||||
if (du && su && du->kind == su->kind &&
|
||||
(du->kind == TY_PTR || du->kind == TY_SLICE) &&
|
||||
du->sub && du->sub->kind == TY_OPAQUE)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user