wcc_ww/cgen: c4 tyassignableuntyped full-chase — structural alignment, zero acceptance graduations, graduations ride #90
F2a batch-2 c4, per rob's FINAL RULING (i) (banked, .ai/rob-batch2-spec.md):
ww-only — the cs twin (cmd/wcc/type.c:369-385) is already harec-shaped
since F1 9bd0d8b. Port of ref/harec/src/types.c:989-996
type_is_assignable structure: full dealias of the dst when its chased
kind is not tagged (du = tichase(dst); the STR/BOOL/NIL arms read du);
the tagged variant drill keeps the ORIGINAL p.type_ and detects a
variant's taggedness via the full chase (was one-level — a 2-level
alias-tagged variant slipped INTO the recursion); INT/FLOAT/RUNE keep
the recursive predicates on the unpeeled dst, exactly as cs.
STRUCTURAL ALIGNMENT, ZERO ACCEPTANCE GRADUATIONS, GRADUATIONS RIDE
#90: on wwstage no bool/str literal reaches this funnel today — the
checker stamps N_TRUE/N_FALSE CONCRETE bool (check.ww:2440) where cs
stamps ty_untyped_bool (check.c:1234), the bool member of the #14
N_STRLIT stamp cluster (task #90, filed this train; its metadata
carries the graduation obligations: BOTH layers, the stamp AND the
flatvariantidxt pass-2 one-level pu.under guard at :2868/#17, must
clear before the bound rows flip). The live silent-wrong this would
close — `type bb2 = bb1 = bool; let v: (void|bb2) = true; v is bb2`
runs cs 0 / ww 1, single-line asm diff (tag store MOVQ $1 vs $0) —
therefore does NOT graduate here: the row dies one layer up
(pins-follow-the-layer).
test 944 (+7 rows): untyped_hold33_pin ((void|T) untyped-int init,
the #33 tagged-dst drill HOLD) + untyped_tagdst_2lvl_hold (2-level
alias tagged dst) + untyped_str2lvl_ctl (masked twin — the shape
fallback rescues str, documents the rescue) + untyped_int2lvl_ctl +
untyped_bool1lvl_ctl all K_RUN 0/0; untyped_bool2lvl_bound90
K_RUN_CS BOUND citing #90; untyped_nestvar_bound K_BUILDERR_CS BOUND
expected-state row (#199-alpha + #90) — untyped literal into a
NESTED-tagged alias-wrapped variant: cs CHECKER loud-rejects
("not assignable", the #199-alpha ww-stricter no-transitive-drill
rule, type.c:316-324) while ww's node-keyed checker accepts and the
binary runs 0 OBSERVED post-chase; the acceptance gate is check.ww
(batch-4 aligns ww down; the row flips to loud-BOTH then; ww half
pinned via #90 metadata — the harness has no cs-reject+ww-runs
mode). Every row's cs column OBSERVED (built+run) at dispatch-SHA cs
per the ratified condition; probes at /tmp/b2/c4.
989 lib-byteid ratchet: ZERO flips (31 ID / 9 DIVERGE / 3 WWREJECT
hold exactly as pinned). Byte-id: five-mains NEUTRAL on identical
inputs vs the c3 build; cs==ww on the regenerated five; test-unit 288
green. combined.ww regens ride along.
This commit is contained in:
@@ -18990,17 +18990,26 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
|
||||
fn tyassignableuntyped(dst: *tinfo, want: *tinfo) bool = {
|
||||
if (dst == nil) { return false; };
|
||||
if (want == nil) { return false; };
|
||||
let du: *tinfo = dst;
|
||||
if (du.kind == tykind.TY_NAMED && du.under != nil) { du = du.under; };
|
||||
if (du.kind == tykind.TY_TAGGED) {
|
||||
// c4 (rob RE-RULE 2026-06-05): FULL chase, not the one-level unwrap
|
||||
// — harec type_is_assignable dealiases the dst transitively when it
|
||||
// is not tagged and keeps the ORIGINAL dst for the tagged variant
|
||||
// drill, taggedness detected via the full chase (ref/harec/src/
|
||||
// types.c:989-996). The one-level unwrap left a 2-level named bool
|
||||
// alias variant silently mis-tagged 0 (the str twin was masked by
|
||||
// taggedvariantindext's shape fallback). Aligns ww UP to the cs
|
||||
// twin cmd/wcc/type.c:369-385, harec-shaped since F1 9bd0d8b.
|
||||
let du: *tinfo = tichase(dst);
|
||||
if (du != nil && du.kind == tykind.TY_TAGGED) {
|
||||
// concrete→tagged drill (type.c:316-324): a NESTED tagged
|
||||
// variant compares by type_eq there — never equal to an
|
||||
// untyped source — so only non-tagged variants recurse.
|
||||
// untyped source — so only non-tagged variants recurse, on
|
||||
// the variant's ORIGINAL type; the variant's taggedness is
|
||||
// detected via the full chase (was one-level: a 2-level
|
||||
// alias-tagged variant slipped INTO the recursion, diverging
|
||||
// from cs's checker-level #199α reject — see the c4 finding).
|
||||
let p: *tparam = du.params;
|
||||
for (p != nil) {
|
||||
let pu: *tinfo = p.type_;
|
||||
if (pu != nil && pu.kind == tykind.TY_NAMED
|
||||
&& pu.under != nil) { pu = pu.under; };
|
||||
let pu: *tinfo = tichase(p.type_);
|
||||
let nestedtagged: bool = false;
|
||||
if (pu != nil) {
|
||||
if (pu.kind == tykind.TY_TAGGED) { nestedtagged = true; };
|
||||
@@ -19012,30 +19021,25 @@ fn tyassignableuntyped(dst: *tinfo, want: *tinfo) bool = {
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// type.c:357-369 — the per-kind holds checks run on the UNPEELED
|
||||
// dst: typeisnum/typeisfloat/typeisint self-recurse TY_NAMED like
|
||||
// their cstage twins; STR/BOOL spell out the one-level unwrap.
|
||||
// type.c:369-385 — INT/FLOAT/RUNE run on the UNPEELED dst exactly
|
||||
// like cs (typeisnum/typeisfloat/typeisint self-recurse TY_NAMED);
|
||||
// STR/BOOL/NIL read the chased du like cs reads its chased du.
|
||||
if (want.kind == tykind.TY_UNTYPED_INT) { return typeisnum(dst); };
|
||||
if (want.kind == tykind.TY_UNTYPED_FLOAT) { return typeisfloat(dst); };
|
||||
if (want.kind == tykind.TY_UNTYPED_STR) {
|
||||
if (dst.kind == tykind.TY_STR) { return true; };
|
||||
if (dst.kind == tykind.TY_NAMED && dst.under != nil) {
|
||||
return dst.under.kind == tykind.TY_STR;
|
||||
};
|
||||
return false;
|
||||
if (du == nil) { return false; };
|
||||
return du.kind == tykind.TY_STR;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_RUNE) {
|
||||
if (typeisint(dst)) { return true; };
|
||||
return dst.kind == tykind.TY_RUNE;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_BOOL) {
|
||||
if (dst.kind == tykind.TY_BOOL) { return true; };
|
||||
if (dst.kind == tykind.TY_NAMED && dst.under != nil) {
|
||||
return dst.under.kind == tykind.TY_BOOL;
|
||||
};
|
||||
return false;
|
||||
if (du == nil) { return false; };
|
||||
return du.kind == tykind.TY_BOOL;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_NIL) {
|
||||
if (du == nil) { return false; };
|
||||
if (du.kind == tykind.TY_PTR) { return true; };
|
||||
if (du.kind == tykind.TY_SLICE) { return true; };
|
||||
if (du.kind == tykind.TY_CHAN) { return true; };
|
||||
|
||||
@@ -2918,17 +2918,26 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
|
||||
fn tyassignableuntyped(dst: *tinfo, want: *tinfo) bool = {
|
||||
if (dst == nil) { return false; };
|
||||
if (want == nil) { return false; };
|
||||
let du: *tinfo = dst;
|
||||
if (du.kind == tykind.TY_NAMED && du.under != nil) { du = du.under; };
|
||||
if (du.kind == tykind.TY_TAGGED) {
|
||||
// c4 (rob RE-RULE 2026-06-05): FULL chase, not the one-level unwrap
|
||||
// — harec type_is_assignable dealiases the dst transitively when it
|
||||
// is not tagged and keeps the ORIGINAL dst for the tagged variant
|
||||
// drill, taggedness detected via the full chase (ref/harec/src/
|
||||
// types.c:989-996). The one-level unwrap left a 2-level named bool
|
||||
// alias variant silently mis-tagged 0 (the str twin was masked by
|
||||
// taggedvariantindext's shape fallback). Aligns ww UP to the cs
|
||||
// twin cmd/wcc/type.c:369-385, harec-shaped since F1 9bd0d8b.
|
||||
let du: *tinfo = tichase(dst);
|
||||
if (du != nil && du.kind == tykind.TY_TAGGED) {
|
||||
// concrete→tagged drill (type.c:316-324): a NESTED tagged
|
||||
// variant compares by type_eq there — never equal to an
|
||||
// untyped source — so only non-tagged variants recurse.
|
||||
// untyped source — so only non-tagged variants recurse, on
|
||||
// the variant's ORIGINAL type; the variant's taggedness is
|
||||
// detected via the full chase (was one-level: a 2-level
|
||||
// alias-tagged variant slipped INTO the recursion, diverging
|
||||
// from cs's checker-level #199α reject — see the c4 finding).
|
||||
let p: *tparam = du.params;
|
||||
for (p != nil) {
|
||||
let pu: *tinfo = p.type_;
|
||||
if (pu != nil && pu.kind == tykind.TY_NAMED
|
||||
&& pu.under != nil) { pu = pu.under; };
|
||||
let pu: *tinfo = tichase(p.type_);
|
||||
let nestedtagged: bool = false;
|
||||
if (pu != nil) {
|
||||
if (pu.kind == tykind.TY_TAGGED) { nestedtagged = true; };
|
||||
@@ -2940,30 +2949,25 @@ fn tyassignableuntyped(dst: *tinfo, want: *tinfo) bool = {
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// type.c:357-369 — the per-kind holds checks run on the UNPEELED
|
||||
// dst: typeisnum/typeisfloat/typeisint self-recurse TY_NAMED like
|
||||
// their cstage twins; STR/BOOL spell out the one-level unwrap.
|
||||
// type.c:369-385 — INT/FLOAT/RUNE run on the UNPEELED dst exactly
|
||||
// like cs (typeisnum/typeisfloat/typeisint self-recurse TY_NAMED);
|
||||
// STR/BOOL/NIL read the chased du like cs reads its chased du.
|
||||
if (want.kind == tykind.TY_UNTYPED_INT) { return typeisnum(dst); };
|
||||
if (want.kind == tykind.TY_UNTYPED_FLOAT) { return typeisfloat(dst); };
|
||||
if (want.kind == tykind.TY_UNTYPED_STR) {
|
||||
if (dst.kind == tykind.TY_STR) { return true; };
|
||||
if (dst.kind == tykind.TY_NAMED && dst.under != nil) {
|
||||
return dst.under.kind == tykind.TY_STR;
|
||||
};
|
||||
return false;
|
||||
if (du == nil) { return false; };
|
||||
return du.kind == tykind.TY_STR;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_RUNE) {
|
||||
if (typeisint(dst)) { return true; };
|
||||
return dst.kind == tykind.TY_RUNE;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_BOOL) {
|
||||
if (dst.kind == tykind.TY_BOOL) { return true; };
|
||||
if (dst.kind == tykind.TY_NAMED && dst.under != nil) {
|
||||
return dst.under.kind == tykind.TY_BOOL;
|
||||
};
|
||||
return false;
|
||||
if (du == nil) { return false; };
|
||||
return du.kind == tykind.TY_BOOL;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_NIL) {
|
||||
if (du == nil) { return false; };
|
||||
if (du.kind == tykind.TY_PTR) { return true; };
|
||||
if (du.kind == tykind.TY_SLICE) { return true; };
|
||||
if (du.kind == tykind.TY_CHAN) { return true; };
|
||||
|
||||
@@ -18990,17 +18990,26 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
|
||||
fn tyassignableuntyped(dst: *tinfo, want: *tinfo) bool = {
|
||||
if (dst == nil) { return false; };
|
||||
if (want == nil) { return false; };
|
||||
let du: *tinfo = dst;
|
||||
if (du.kind == tykind.TY_NAMED && du.under != nil) { du = du.under; };
|
||||
if (du.kind == tykind.TY_TAGGED) {
|
||||
// c4 (rob RE-RULE 2026-06-05): FULL chase, not the one-level unwrap
|
||||
// — harec type_is_assignable dealiases the dst transitively when it
|
||||
// is not tagged and keeps the ORIGINAL dst for the tagged variant
|
||||
// drill, taggedness detected via the full chase (ref/harec/src/
|
||||
// types.c:989-996). The one-level unwrap left a 2-level named bool
|
||||
// alias variant silently mis-tagged 0 (the str twin was masked by
|
||||
// taggedvariantindext's shape fallback). Aligns ww UP to the cs
|
||||
// twin cmd/wcc/type.c:369-385, harec-shaped since F1 9bd0d8b.
|
||||
let du: *tinfo = tichase(dst);
|
||||
if (du != nil && du.kind == tykind.TY_TAGGED) {
|
||||
// concrete→tagged drill (type.c:316-324): a NESTED tagged
|
||||
// variant compares by type_eq there — never equal to an
|
||||
// untyped source — so only non-tagged variants recurse.
|
||||
// untyped source — so only non-tagged variants recurse, on
|
||||
// the variant's ORIGINAL type; the variant's taggedness is
|
||||
// detected via the full chase (was one-level: a 2-level
|
||||
// alias-tagged variant slipped INTO the recursion, diverging
|
||||
// from cs's checker-level #199α reject — see the c4 finding).
|
||||
let p: *tparam = du.params;
|
||||
for (p != nil) {
|
||||
let pu: *tinfo = p.type_;
|
||||
if (pu != nil && pu.kind == tykind.TY_NAMED
|
||||
&& pu.under != nil) { pu = pu.under; };
|
||||
let pu: *tinfo = tichase(p.type_);
|
||||
let nestedtagged: bool = false;
|
||||
if (pu != nil) {
|
||||
if (pu.kind == tykind.TY_TAGGED) { nestedtagged = true; };
|
||||
@@ -19012,30 +19021,25 @@ fn tyassignableuntyped(dst: *tinfo, want: *tinfo) bool = {
|
||||
};
|
||||
return false;
|
||||
};
|
||||
// type.c:357-369 — the per-kind holds checks run on the UNPEELED
|
||||
// dst: typeisnum/typeisfloat/typeisint self-recurse TY_NAMED like
|
||||
// their cstage twins; STR/BOOL spell out the one-level unwrap.
|
||||
// type.c:369-385 — INT/FLOAT/RUNE run on the UNPEELED dst exactly
|
||||
// like cs (typeisnum/typeisfloat/typeisint self-recurse TY_NAMED);
|
||||
// STR/BOOL/NIL read the chased du like cs reads its chased du.
|
||||
if (want.kind == tykind.TY_UNTYPED_INT) { return typeisnum(dst); };
|
||||
if (want.kind == tykind.TY_UNTYPED_FLOAT) { return typeisfloat(dst); };
|
||||
if (want.kind == tykind.TY_UNTYPED_STR) {
|
||||
if (dst.kind == tykind.TY_STR) { return true; };
|
||||
if (dst.kind == tykind.TY_NAMED && dst.under != nil) {
|
||||
return dst.under.kind == tykind.TY_STR;
|
||||
};
|
||||
return false;
|
||||
if (du == nil) { return false; };
|
||||
return du.kind == tykind.TY_STR;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_RUNE) {
|
||||
if (typeisint(dst)) { return true; };
|
||||
return dst.kind == tykind.TY_RUNE;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_BOOL) {
|
||||
if (dst.kind == tykind.TY_BOOL) { return true; };
|
||||
if (dst.kind == tykind.TY_NAMED && dst.under != nil) {
|
||||
return dst.under.kind == tykind.TY_BOOL;
|
||||
};
|
||||
return false;
|
||||
if (du == nil) { return false; };
|
||||
return du.kind == tykind.TY_BOOL;
|
||||
};
|
||||
if (want.kind == tykind.TY_UNTYPED_NIL) {
|
||||
if (du == nil) { return false; };
|
||||
if (du.kind == tykind.TY_PTR) { return true; };
|
||||
if (du.kind == tykind.TY_SLICE) { return true; };
|
||||
if (du.kind == tykind.TY_CHAN) { return true; };
|
||||
|
||||
@@ -797,6 +797,93 @@ static const struct row rows[] = {
|
||||
" if (y != 70000) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* ---- F2a batch-2 c4 (rob FINAL RULING (i)): tyassignableuntyped
|
||||
* full-chase — STRUCTURAL alignment to cs type.c:369-385 / harec
|
||||
* type_is_assignable; ZERO acceptance graduations in this commit
|
||||
* (graduations ride task #90). Holds + controls pin the chase as
|
||||
* behavior-neutral on every reachable shape; the tagged-dst rows
|
||||
* are the #33 pins (drill semantics unchanged). */
|
||||
{ "untyped_hold33_pin", /* (void|T) untyped-int init — #33 fixed
|
||||
* behavior, tagged-dst drill HOLD */
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let mn: (void | size) = 5;\n"
|
||||
" if (!(mn is size)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "untyped_tagdst_2lvl_hold", /* 2-level alias TAGGED dst holds */
|
||||
"package main;\n"
|
||||
"type r1 = (void | size);\n"
|
||||
"type r2 = r1;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: r2 = 5;\n"
|
||||
" if (!(v is size)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "untyped_str2lvl_ctl", /* masked twin: ww stamps the str
|
||||
* literal CONCRETE (#14 cluster), the
|
||||
* shape fallback rescues the tag — 0/0
|
||||
* all along; documents the rescue */
|
||||
"package main;\n"
|
||||
"type sa1 = str;\n"
|
||||
"type sa2 = sa1;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | sa2) = \"hi\";\n"
|
||||
" if (!(v is sa2)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "untyped_int2lvl_ctl", /* typeisnum NAMED-recursion control */
|
||||
"package main;\n"
|
||||
"type ia1 = i64;\n"
|
||||
"type ia2 = ia1;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | ia2) = 7;\n"
|
||||
" if (!(v is ia2)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "untyped_bool1lvl_ctl", /* 1 level: ww passes via the
|
||||
* fallback's one peel — 0/0 observed */
|
||||
"package main;\n"
|
||||
"type ba1 = bool;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | ba1) = true;\n"
|
||||
" if (!(v is ba1)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* BOUND row (task #90): ww stamps N_TRUE CONCRETE bool
|
||||
* (check.ww:2440) vs cs untyped_bool (check.c:1234), so the bool
|
||||
* literal never reaches the c4-chased untyped funnel on ww — it
|
||||
* dies at flatvariantidxt pass-2's one-level pu.under guard
|
||||
* (cgenutil.ww:2868, the #17 site): SILENT tag 0, ww runs exit 1
|
||||
* where cs runs 0. Flip to K_RUN when #90 clears BOTH layers
|
||||
* (stamp + fallback peel). */
|
||||
{ "untyped_bool2lvl_bound90",
|
||||
"package main;\n"
|
||||
"type bb1 = bool;\n"
|
||||
"type bb2 = bb1;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | bb2) = true;\n"
|
||||
" if (!(v is bb2)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN_CS, NULL }, /* ww: task #90 */
|
||||
/* BOUND expected-state row (#199α + #90): untyped literal into a
|
||||
* NESTED-tagged alias-wrapped variant. cs CHECKER loud-rejects —
|
||||
* the #199α ww-stricter no-transitive-drill rule (type.c:316-324);
|
||||
* ww's node-keyed checker ACCEPTS and the binary runs exit 0
|
||||
* (observed post-c4; the harness has no cs-reject+ww-runs mode,
|
||||
* so the ww half is pinned via #90's metadata, not here). The c4
|
||||
* pu-chase aligned the CGEN drill structure to cs; the acceptance
|
||||
* gate is check.ww — batch-4 aligns ww down, then this row flips
|
||||
* to K_BUILDERR (loud BOTH). */
|
||||
{ "untyped_nestvar_bound",
|
||||
"package main;\n"
|
||||
"type inner = (i64 | str);\n"
|
||||
"type inner2 = inner;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | inner2) = 7;\n"
|
||||
" if (v is void) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_BUILDERR_CS, "not assignable" },
|
||||
/* ---- task #71 (rob probe-ruled F1 enrollment): nested fields
|
||||
* whose TYPES are aliases. The chained-dot store/read/addr-of
|
||||
* walks single-peeled each hop, aborted on the alias, and fell
|
||||
|
||||
Reference in New Issue
Block a user