wcc: #5 F1 promote type_chase_named + transitive-peel acceptance align-cs-up
Promote type_chase_named from cmd/w6c/cgen.c (static) to cmd/wcc/type.c (exported via ww.h) and re-route every checker single-NAMED-peel through it: check.c's ~28 inline ternaries + 3 ad-hoc loops, type.c's assignability/untyped/borrow/opaque peels. type_eq's nominal identity (check.c:114) and the resolve machinery guards stay untouched. The re-route IS the acceptance align-up — cstage loud-rejected alias shapes wwstage accepts AND runs Hare-right (F0 census, harec dealiases at every consumer): - #54 binop alias-vs-base: unify_arith gains the harec type_promote arm (ref/harec/src/check.c:1083-1105) — one-sided alias + dealias-equal promotes to the ALIAS side; alias-vs-alias stays rejected. - alias-cond family: if/for/&&/||/! chase-then-bool (harec check.c:2141/2515/3229/3572). assert stays loud (F0 2a symmetric). - #70 field access through 2-level alias chains (ken c3_chain3). - assignability through the full chain (harec types.c:989-996 dealias-both): return/init/assign legs, F0 8b idx/slice walls. - alias-of-ptr deref (harec types.c:19-22 type_dereference). The widening reaches cgen arms whose own single peels then misbehaved — both classes are closed IN THIS COMMIT so no intermediate state ships a loud->silent flip (bisect no-silent invariant): - index family: the 8b acceptance hit ptr-load base + esz=1 (SEGV / prefix-luck) — idx_eff + the N_INDEX read / index-write / &base[i] / N_SLICE (expr + call-arg) / N_FORRANGE / aggarg_srcaddr-index / castsrcprim-dot / match-field base classifies chase. - kind classifiers (ken #61-root-verify v3 find): a 2-level f64 alias param reached cg_isfloat's single peel and classified INT — silent wrong-register-class. cg_isfloat / type_isf32 / fld_isfloat / type_isstr / type_isslice chase. ken's v3 row is pinned with credit. Bootstrap asm is byte-identical before/after (w6c on every main.combined.ww cmp-equal vs a pristine738d7f4scratch; 989 lib_byteid pins unchanged): 2-level chains were checker-walled pre-F1, so no previously-accepted program changes shape. test: 944_alias_accept_run (20 rows): acceptance graduations pinned runtime + byte-id both stages; idx/slice/range/slice-param rows cs-only until the wwstage #60 esz family lands (F2 batch 1); cs-only harec-parity loud pin for alias-vs-alias binop; assert stays-loud row; ken-v3 + f64/str/slice kind rows. Mutation-checked at738d7f4. reviewer-F1 fold — the same invariant, outside the F0 census: this commit ADMITS 2+-level alias slice/str/aggregate types in STRUCT FIELD position, therefore this commit must keep them correct-or-loud. The cgen FIELD-TYPE gates single-peeled, so the slice/str 3-word arms fell to word0-only scalar tails — accept-and-corrupt, ww correct, every shape loud at the pristine base. Chased (probe-proven, byte-id graduations): single-dot field store + via-ptr twin, struct-lit fill, chained store-walk LEAF (the #71 walk chases hops, not leaves), chained-ptr-field store, single-dot / via-ptr / chained-walk field reads (clobber-probed — word0 reads luck-passed on stale BX/CX). The six unprobed sibling gates (indexed-elem store/read, ptr-chain read, heap fill, tuple-elem read, static emit) hard-error via fld_alias_tripwire on a 2+-level alias over an aggregate base, citing task #73 (the family's scheduled chase); <=1-level and scalar bases never fire — zero behavior change for any pre-#5-legal program (five selfhost mains cmp-identical vs the pristine738d7f4scratch). test: 944 +11 rows (9 K_RUN byte-id, wholeread K_RUN_CS [#60 ww half + pre-existing 1-level read-spine divergence], #73 tripwire K_BUILDERR_CS pin); 1-level controls per gate in /tmp/revF1.
This commit is contained in:
@@ -147,6 +147,22 @@ type_named(Arena *a, const char *name, Type *under)
|
||||
return t;
|
||||
}
|
||||
|
||||
/* type_chase_named — walk the TY_NAMED.under chain to the deepest non-
|
||||
* named type. Chain-of-aliases (#22): `type b = a; type a = struct;`
|
||||
* stacks two TY_NAMED layers — a single peel leaves `t` pointing at
|
||||
* the inner alias (still TY_NAMED), so kind-gated arms (TY_STRUCT,
|
||||
* TY_SLICE, TY_TAGGED, TY_PTR) miss and the consumer silently falls
|
||||
* through to a scalar shape. Mirror of wwstage's structlookupchain /
|
||||
* resolvealias and harec's type_dealias (ref/harec/src/types.c:53).
|
||||
* Promoted from cmd/w6c/cgen.c for the #5 alias arc so the checker's
|
||||
* acceptance sites and the cgen classify sites share one peel. */
|
||||
Type *
|
||||
type_chase_named(Type *t)
|
||||
{
|
||||
while (t && t->kind == TY_NAMED) t = t->under;
|
||||
return t;
|
||||
}
|
||||
|
||||
int
|
||||
type_isint(Type *t)
|
||||
{
|
||||
@@ -297,11 +313,11 @@ type_assignable(Type *dst, Type *src)
|
||||
* src appears as a variant of dst (Hare-style subset). Tag
|
||||
* remap at the use site handles different variant indices.
|
||||
* Checked before the untyped branch so untyped literals flow
|
||||
* through to a variant's typed slot. Unwraps a NAMED alias on
|
||||
* either side so `type result = (T|E);` accepts variants too. */
|
||||
* through to a variant's typed slot. Chases the NAMED alias chain
|
||||
* on either side so `type result = (T|E);` accepts variants too. */
|
||||
{
|
||||
Type *du = (dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
Type *su = (src->kind == TY_NAMED) ? src->under : src;
|
||||
Type *du = type_chase_named(dst);
|
||||
Type *su = type_chase_named(src);
|
||||
if (du && du->kind == TY_TAGGED &&
|
||||
!(su && su->kind == TY_TAGGED)) {
|
||||
/* #199(α): direct variant only — no transitive drill into
|
||||
@@ -314,8 +330,7 @@ type_assignable(Type *dst, Type *src)
|
||||
* `is`/`as`'s non-recursive variant lookup. Callers compose
|
||||
* `let inner: Wrapper = sub; let r: parent = inner;`. */
|
||||
for (Tparam *p = du->params; p; p = p->next) {
|
||||
Type *pu = (p->type && p->type->kind == TY_NAMED)
|
||||
? p->type->under : p->type;
|
||||
Type *pu = type_chase_named(p->type);
|
||||
if (pu && pu->kind == TY_TAGGED) {
|
||||
if (type_eq(p->type, src)) return 1;
|
||||
continue;
|
||||
@@ -335,8 +350,7 @@ type_assignable(Type *dst, Type *src)
|
||||
* the subset loop below would reject. SSoT with `is`/`as`
|
||||
* variant lookup (#198 family). */
|
||||
for (Tparam *dp = du->params; dp; dp = dp->next) {
|
||||
Type *pu = (dp->type && dp->type->kind == TY_NAMED)
|
||||
? dp->type->under : dp->type;
|
||||
Type *pu = type_chase_named(dp->type);
|
||||
if (pu && pu->kind == TY_TAGGED &&
|
||||
type_eq(dp->type, src)) return 1;
|
||||
}
|
||||
@@ -354,15 +368,15 @@ type_assignable(Type *dst, Type *src)
|
||||
|
||||
/* Untyped → typed: only if the typed kind can hold the value. */
|
||||
if (type_isuntyped(src)) {
|
||||
Type *du = type_chase_named(dst);
|
||||
if (src->kind == TY_UNTYPED_INT && type_isnum(dst)) return 1;
|
||||
if (src->kind == TY_UNTYPED_FLOAT && type_isfloat(dst)) return 1;
|
||||
if (src->kind == TY_UNTYPED_STR && (dst->kind == TY_STR ||
|
||||
(dst->kind == TY_NAMED && dst->under && dst->under->kind == TY_STR))) return 1;
|
||||
if (src->kind == TY_UNTYPED_STR && du && du->kind == TY_STR)
|
||||
return 1;
|
||||
if (src->kind == TY_UNTYPED_RUNE && (type_isint(dst) || dst->kind == TY_RUNE)) return 1;
|
||||
if (src->kind == TY_UNTYPED_BOOL && (dst->kind == TY_BOOL ||
|
||||
(dst->kind == TY_NAMED && dst->under && dst->under->kind == TY_BOOL))) return 1;
|
||||
if (src->kind == TY_UNTYPED_BOOL && du && du->kind == TY_BOOL)
|
||||
return 1;
|
||||
if (src->kind == TY_UNTYPED_NIL) {
|
||||
Type *du = (dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
if (du && (du->kind == TY_PTR || du->kind == TY_SLICE ||
|
||||
du->kind == TY_CHAN || du->kind == TY_FN))
|
||||
return 1;
|
||||
@@ -370,11 +384,16 @@ type_assignable(Type *dst, Type *src)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Named on either side: compare to the underlying. NAMED is a
|
||||
* distinct type from its under; but assignment from under to
|
||||
* named (and vice-versa) is allowed in this minimal checker. */
|
||||
if (dst->kind == TY_NAMED && type_eq(dst->under, src)) return 1;
|
||||
if (src->kind == TY_NAMED && type_eq(dst, src->under)) return 1;
|
||||
/* Named on either side: assignable through the FULL alias chain.
|
||||
* harec type_is_assignable dealiases both sides whenever the dst
|
||||
* is not tagged (ref/harec/src/types.c:993-996; the tagged dst
|
||||
* returned above) — so base↔alias and alias↔alias-of-same-base
|
||||
* all flow. The pre-#5 single peel rejected any 2-level chain
|
||||
* (`return x*2` from a myint2 local louded at the int return). */
|
||||
if (dst->kind == TY_NAMED || src->kind == TY_NAMED) {
|
||||
if (type_eq(type_chase_named(dst), type_chase_named(src)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Tuple-to-tuple: element-wise assignable. */
|
||||
if (dst->kind == TY_TUPLE && src->kind == TY_TUPLE) {
|
||||
@@ -397,8 +416,8 @@ type_assignable(Type *dst, Type *src)
|
||||
* {.ptr=&arr[0], .len=N, .cap=N} is byte-identical across stages.
|
||||
* Element types must match exactly — no element decay. */
|
||||
{
|
||||
Type *du = (dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
Type *su = (src->kind == TY_NAMED) ? src->under : src;
|
||||
Type *du = type_chase_named(dst);
|
||||
Type *su = type_chase_named(src);
|
||||
if (du && su && du->kind == TY_SLICE && su->kind == TY_ARRAY &&
|
||||
su->alen != SIZE_UNDEFINED && type_eq(du->sub, su->sub))
|
||||
return 1;
|
||||
@@ -421,8 +440,8 @@ type_assignable(Type *dst, Type *src)
|
||||
* 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;
|
||||
Type *du = type_chase_named(dst);
|
||||
Type *su = type_chase_named(src);
|
||||
if (du && su && du->kind == su->kind &&
|
||||
(du->kind == TY_PTR || du->kind == TY_SLICE) &&
|
||||
du->sub && du->sub->kind == TY_OPAQUE)
|
||||
|
||||
Reference in New Issue
Block a user