diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 2f747cda..1b6c31e6 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -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 diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index faccf5f9..dee27fc7 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -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 diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 89fa70de..9be1a9a2 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -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 diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 762262b9..2df463f2 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -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 diff --git a/test/wcc/944_variant_chain_b95_run.c b/test/wcc/944_variant_chain_b95_run.c index 09be5d37..d31a5945 100644 --- a/test/wcc/944_variant_chain_b95_run.c +++ b/test/wcc/944_variant_chain_b95_run.c @@ -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