cgen: #95 c2 structural variant fallback — both-stage fused
A nominally-unrelated, structurally-equal NAMED source into a NAMED
variant (kb95_unrel: ta/tb same-layout structs, src ta -> (void|tb))
was LIVE both-wrong-identical byte-id silent: both checkers accept,
both cgens tagged 0. After c1's chain arm finds no shared chain
node, match the variant whose CHASED type type_eq's/typeeq's the
source's chased bottom — chased type EQUALITY only, no
type_is_assignable scalar import, no int widening (ken's binding
scalar warning). Same NAMED-source branch, both stages
(cg_tag_for_variant / flatvariantidxt), forced fuse.
Correctness reference, cite 1 — harec tagged_select_subtype P2+P3
(ref/harec/src/types.c:702-739), verbatim:
if (t->id == subtype->id) {
return t;
}
if (type_is_assignable(ctx, t, subtype)) {
selected = t;
++nassign;
}
...
if (nassign == 1) {
return selected;
}
return NULL;
with type_is_assignable's non-tagged path dealiasing both sides and
accepting composites only via interned pointer equality
(types.c:988-1002), verbatim:
if (type_dealias(ctx, to)->storage != STORAGE_TAGGED) {
to = type_dealias(ctx, to);
from = type_dealias(ctx, from);
}
...
if (to == from && to->storage != STORAGE_VOID) {
return true;
}
Cite 2 — type_hash interns bare composites STRUCTURALLY (banked as
types.c:72-81; verified in the vendored copy at types.c:444 +
struct/union arm :514-525), verbatim:
case STORAGE_UNION:
hash = fnv1a_size(hash, type->struct_union.packed);
for (const struct struct_field *field = type->struct_union.fields;
field; field = field->next) {
if (field->name) {
hash = fnv1a_s(hash, field->name);
}
hash = fnv1a_u32(hash, type_hash(field->type));
hash = fnv1a_size(hash, field->offset);
}
— no decl ident in the hash, so harec's two decls dealias to ONE
interned node and `to == from` holds: acceptance is DEFINITIONAL
under interning, not an arm whose text could be misread. Our store
does not intern; chased type equality is the non-interned rendering
of the same rule.
Honest divergence (the >=2-structural-match hard-error STAYS): under
harec's interning two structurally-identical variants are ONE type —
a union cannot contain it twice — so the ambiguity case is
unrepresentable there; our hard-error (twin texts, shared tail
"source structurally matches >=2 variants — ambiguous without
nominal layout (#95)") is the correct nominal-lossy-model rendering,
not a harec deviation.
Pin table: unrel_struct row added (kb95_unrel graduates ok/1-ok/1 ->
0/0, byte-id held) — suite now 48/48. All c1 rows unmoved.
Invariants: 163-row dissolution matrix at tip — same 3 family
graduations as c1, ZERO new movers; five mains cs-vs-ww byte-id OK;
make all 0; sizelint 0; peellint 0 (no new peel sites — the
structural leg reads only chased ends); all 944 suites + 808 green.
w6c_ww/wwdump_ww main.combined.ww regen'd.
This commit is contained in:
@@ -921,6 +921,33 @@ cg_tag_for_variant(Type *t, Type *vt)
|
||||
fatal("cg_tag_for_variant: source alias chain "
|
||||
"reaches >=2 variants — ambiguous without "
|
||||
"nominal layout (#95)");
|
||||
if (found >= 0) return found;
|
||||
/* c2 (#95): no chain hit (found/n are still -1/0 here) —
|
||||
* structural fallback on the chased ends. harec interns
|
||||
* bare composites structurally (type_hash, ref/harec/src/
|
||||
* types.c:72-81), so a nominally-unrelated structurally-
|
||||
* equal decl DEALIASES TO THE SAME NODE there and the
|
||||
* assignability arm accepts via `to == from`
|
||||
* (types.c:1000-1002) — acceptance is definitional, not an
|
||||
* arm we could misread. Our store does not intern, so the
|
||||
* pointer compare of pass 1b misses it; chased type_eq is
|
||||
* the non-interned rendering of the same rule. EQUALITY
|
||||
* only — no type_is_assignable scalar import. The >=2
|
||||
* hard-error is the nominal-lossy-model rendering of a case
|
||||
* harec cannot represent (two structurally-identical
|
||||
* variants intern to ONE type — a union cannot contain it
|
||||
* twice), not a harec deviation. */
|
||||
idx = 0;
|
||||
for (Tparam *p = t->params; p; p = p->next, idx++) {
|
||||
if (p->type && type_eq(type_chase_named(p->type), sb)) {
|
||||
if (found < 0) found = idx;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n >= 2)
|
||||
fatal("cg_tag_for_variant: source structurally "
|
||||
"matches >=2 variants — ambiguous without "
|
||||
"nominal layout (#95)");
|
||||
return found;
|
||||
}
|
||||
/* Pass 2 (#15): no exact variant matched — try a structural match of
|
||||
|
||||
@@ -19021,6 +19021,37 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (found >= 0) { return found; };
|
||||
// c2 (#95): no chain hit (found/n are still -1/0 here) —
|
||||
// structural fallback on the chased ends. harec interns bare
|
||||
// composites structurally (type_hash, ref/harec/src/
|
||||
// types.c:72-81), so a nominally-unrelated structurally-
|
||||
// equal decl DEALIASES TO THE SAME NODE there and the
|
||||
// assignability arm accepts via `to == from`
|
||||
// (types.c:1000-1002) — acceptance is definitional, not an
|
||||
// arm we could misread. Our store does not intern, so the
|
||||
// pointer compare of pass 1b misses it; chased typeeq is the
|
||||
// non-interned rendering of the same rule. EQUALITY only —
|
||||
// no type_is_assignable scalar import. The >=2 hard-error is
|
||||
// the nominal-lossy-model rendering of a case harec cannot
|
||||
// represent (two structurally-identical variants intern to
|
||||
// ONE type — a union cannot contain it twice), not a harec
|
||||
// deviation. cs twin cg_tag_for_variant fused in this commit.
|
||||
q = ti.params;
|
||||
qi = 0;
|
||||
for (q != nil) {
|
||||
if (q.type_ != nil && typeeq(tichase(q.type_), sb)) {
|
||||
if (found < 0) { found = qi; };
|
||||
n += 1;
|
||||
};
|
||||
q = q.tnext;
|
||||
qi += 1;
|
||||
};
|
||||
if (n >= 2) {
|
||||
let msg2: str = "flatvariantidxt: source structurally matches >=2 variants — ambiguous without nominal layout (#95)\n";
|
||||
os.write(2, msg2.ptr, msg2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
return found;
|
||||
};
|
||||
// Pass 2 (#15): no exact variant matched — structurally match a BARE
|
||||
|
||||
@@ -2917,6 +2917,37 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (found >= 0) { return found; };
|
||||
// c2 (#95): no chain hit (found/n are still -1/0 here) —
|
||||
// structural fallback on the chased ends. harec interns bare
|
||||
// composites structurally (type_hash, ref/harec/src/
|
||||
// types.c:72-81), so a nominally-unrelated structurally-
|
||||
// equal decl DEALIASES TO THE SAME NODE there and the
|
||||
// assignability arm accepts via `to == from`
|
||||
// (types.c:1000-1002) — acceptance is definitional, not an
|
||||
// arm we could misread. Our store does not intern, so the
|
||||
// pointer compare of pass 1b misses it; chased typeeq is the
|
||||
// non-interned rendering of the same rule. EQUALITY only —
|
||||
// no type_is_assignable scalar import. The >=2 hard-error is
|
||||
// the nominal-lossy-model rendering of a case harec cannot
|
||||
// represent (two structurally-identical variants intern to
|
||||
// ONE type — a union cannot contain it twice), not a harec
|
||||
// deviation. cs twin cg_tag_for_variant fused in this commit.
|
||||
q = ti.params;
|
||||
qi = 0;
|
||||
for (q != nil) {
|
||||
if (q.type_ != nil && typeeq(tichase(q.type_), sb)) {
|
||||
if (found < 0) { found = qi; };
|
||||
n += 1;
|
||||
};
|
||||
q = q.tnext;
|
||||
qi += 1;
|
||||
};
|
||||
if (n >= 2) {
|
||||
let msg2: str = "flatvariantidxt: source structurally matches >=2 variants — ambiguous without nominal layout (#95)\n";
|
||||
os.write(2, msg2.ptr, msg2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
return found;
|
||||
};
|
||||
// Pass 2 (#15): no exact variant matched — structurally match a BARE
|
||||
|
||||
@@ -19021,6 +19021,37 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (found >= 0) { return found; };
|
||||
// c2 (#95): no chain hit (found/n are still -1/0 here) —
|
||||
// structural fallback on the chased ends. harec interns bare
|
||||
// composites structurally (type_hash, ref/harec/src/
|
||||
// types.c:72-81), so a nominally-unrelated structurally-
|
||||
// equal decl DEALIASES TO THE SAME NODE there and the
|
||||
// assignability arm accepts via `to == from`
|
||||
// (types.c:1000-1002) — acceptance is definitional, not an
|
||||
// arm we could misread. Our store does not intern, so the
|
||||
// pointer compare of pass 1b misses it; chased typeeq is the
|
||||
// non-interned rendering of the same rule. EQUALITY only —
|
||||
// no type_is_assignable scalar import. The >=2 hard-error is
|
||||
// the nominal-lossy-model rendering of a case harec cannot
|
||||
// represent (two structurally-identical variants intern to
|
||||
// ONE type — a union cannot contain it twice), not a harec
|
||||
// deviation. cs twin cg_tag_for_variant fused in this commit.
|
||||
q = ti.params;
|
||||
qi = 0;
|
||||
for (q != nil) {
|
||||
if (q.type_ != nil && typeeq(tichase(q.type_), sb)) {
|
||||
if (found < 0) { found = qi; };
|
||||
n += 1;
|
||||
};
|
||||
q = q.tnext;
|
||||
qi += 1;
|
||||
};
|
||||
if (n >= 2) {
|
||||
let msg2: str = "flatvariantidxt: source structurally matches >=2 variants — ambiguous without nominal layout (#95)\n";
|
||||
os.write(2, msg2.ptr, msg2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
return found;
|
||||
};
|
||||
// Pass 2 (#15): no exact variant matched — structurally match a BARE
|
||||
|
||||
@@ -359,6 +359,20 @@ static const struct row rows[] = {
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN_CS_WWERR,
|
||||
"deferred #277" }, /* ww: task #277 */
|
||||
/* ---- c2: structural fallback — was both-wrong-identical
|
||||
* byte-id silent (live metric-1: checkers accept, cgen tagged
|
||||
* 0). harec accepts by interning (one node, tag tb). */
|
||||
{ "unrel_struct",
|
||||
"package main;\n"
|
||||
"type ta = struct { a: int, b: int };\n"
|
||||
"type tb = struct { a: int, b: int };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: ta;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | tb) = s;\n"
|
||||
" if (!(v is tb)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
Reference in New Issue
Block a user