wcc/check: #14 nominal typeeqast (aliassym) closes A6 cross-module variant identity (wwstage)
typeeqast's N_TNAME arm compared variant types by SURFACE string (streq(aa.str, bb.str)), so a cross-module type referenced bare (oserror) vs qualified (os.oserror) mis-compared unequal -> A6: concrete->tagged silently mis-identified the variant. Resolve each name to its canonical type sym before comparing: keep the streq fast-path, else aliassym(c,aa)==aliassym(c,bb) (ww's existing resolver maps bare [#53] and qualified [#51] to the same SK_TYPE sym). AST analog of cstage type.c:278 TY_NAMED a==b / harec types.c:579 ident_equal -- NO interning. `c` threaded into 21 typeeqast sites. B-full Layer 1: closes A6/#14; Layer 2 (A7 tagged->tagged subset reject) stays deferred to the #199b flatten arc. Byte-id-neutral; graduates shlex #59.15 (989 M_WWREJECT->M_ID). test/wcc/839 pins both-stage symmetry (discriminating teeth = 989 #59.15, per 839 doc).
This commit is contained in:
7
Makefile
7
Makefile
@@ -263,6 +263,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_tagged_assignable_cluster \
|
$(BIN)/test_tagged_assignable_cluster \
|
||||||
$(BIN)/test_idcast_tagged_return \
|
$(BIN)/test_idcast_tagged_return \
|
||||||
$(BIN)/test_global_tagged_castinit \
|
$(BIN)/test_global_tagged_castinit \
|
||||||
|
$(BIN)/test_xmod_nominal_typeeqast \
|
||||||
$(BIN)/test_inferred_array_global \
|
$(BIN)/test_inferred_array_global \
|
||||||
$(BIN)/test_slice_str_global_arg \
|
$(BIN)/test_slice_str_global_arg \
|
||||||
$(BIN)/test_slice_str_global_zero \
|
$(BIN)/test_slice_str_global_zero \
|
||||||
@@ -758,6 +759,12 @@ $(BIN)/test_global_tagged_castinit: test/wcc/838_global_tagged_castinit.c $(BIN)
|
|||||||
$(LIB)/libwwrt.a | $(BIN)
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BIN)/test_xmod_nominal_typeeqast: test/wcc/839_xmod_nominal_typeeqast.c $(BIN)/ww \
|
||||||
|
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||||
|
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||||
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
$(BIN)/test_inferred_array_global: test/wcc/833_inferred_array_global.c $(BIN)/ww \
|
$(BIN)/test_inferred_array_global: test/wcc/833_inferred_array_global.c $(BIN)/ww \
|
||||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||||
|
|||||||
@@ -11121,23 +11121,33 @@ fn resolvealias(c: *checker, n: *node) *node = {
|
|||||||
|
|
||||||
// typeeqast — structural equality on AST type expressions, mod
|
// typeeqast — structural equality on AST type expressions, mod
|
||||||
// the `!` wrapper. Mirrors variant_match in cgen + check.c: NAMED
|
// the `!` wrapper. Mirrors variant_match in cgen + check.c: NAMED
|
||||||
// types compare by string (the closest stand-in for pointer
|
// types compare by string fast-path, else by resolved-decl identity
|
||||||
// identity at the AST level); other nodes recurse by kind.
|
// (the AST analog of cstage type.c:278 `TY_NAMED: a == b` and harec
|
||||||
fn typeeqast(a: *node, b: *node) bool = {
|
// types.c:579 `STORAGE_ALIAS: ident_equal`). #14 B-full Layer 1:
|
||||||
|
// bare `oserror` vs qualified `os.oserror` resolve to the SAME
|
||||||
|
// SK_TYPE sym (aliassym maps both via #51/#53), so a cross-module
|
||||||
|
// nominal forward compares equal where the surface streq said false.
|
||||||
|
fn typeeqast(c: *checker, a: *node, b: *node) bool = {
|
||||||
let aa: *node = unwrapbang(a);
|
let aa: *node = unwrapbang(a);
|
||||||
let bb: *node = unwrapbang(b);
|
let bb: *node = unwrapbang(b);
|
||||||
if (aa == nil) { return bb == nil; };
|
if (aa == nil) { return bb == nil; };
|
||||||
if (bb == nil) { return false; };
|
if (bb == nil) { return false; };
|
||||||
if (aa.kind != bb.kind) { return false; };
|
if (aa.kind != bb.kind) { return false; };
|
||||||
let k: nkind = aa.kind;
|
let k: nkind = aa.kind;
|
||||||
if (k == nkind.N_TNAME) { return streq(aa.str, bb.str); };
|
if (k == nkind.N_TNAME) {
|
||||||
if (k == nkind.N_TPTR) { return typeeqast(aa.lhs, bb.lhs); };
|
if (streq(aa.str, bb.str)) { return true; };
|
||||||
if (k == nkind.N_TSLICE){ return typeeqast(aa.lhs, bb.lhs); };
|
let sa: *sym = aliassym(c, aa);
|
||||||
if (k == nkind.N_TCHAN) { return typeeqast(aa.lhs, bb.lhs); };
|
let sb: *sym = aliassym(c, bb);
|
||||||
|
if (sa != nil && sa == sb) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (k == nkind.N_TPTR) { return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
|
if (k == nkind.N_TSLICE){ return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
|
if (k == nkind.N_TCHAN) { return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
if (k == nkind.N_TFN) {
|
if (k == nkind.N_TFN) {
|
||||||
// Divergence: cstage type.c:239 compares resolved Type; we
|
// Divergence: cstage type.c:239 compares resolved Type; we
|
||||||
// compare AST. See #178.
|
// compare AST. See #178.
|
||||||
if (!typeeqast(aa.lhs, bb.lhs)) { return false; };
|
if (!typeeqast(c, aa.lhs, bb.lhs)) { return false; };
|
||||||
let pa: *node = aa.list;
|
let pa: *node = aa.list;
|
||||||
let pb: *node = bb.list;
|
let pb: *node = bb.list;
|
||||||
for (pa != nil) {
|
for (pa != nil) {
|
||||||
@@ -11149,7 +11159,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
let va: bool = pa.op == tkind.TK_ELLIPSIS;
|
let va: bool = pa.op == tkind.TK_ELLIPSIS;
|
||||||
let vb: bool = pb.op == tkind.TK_ELLIPSIS;
|
let vb: bool = pb.op == tkind.TK_ELLIPSIS;
|
||||||
if (va != vb) { return false; };
|
if (va != vb) { return false; };
|
||||||
if (!typeeqast(pa.lhs, pb.lhs)) { return false; };
|
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
|
||||||
};
|
};
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
@@ -11168,7 +11178,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
let pb: *node = bb.list;
|
let pb: *node = bb.list;
|
||||||
for (pa != nil) {
|
for (pa != nil) {
|
||||||
if (pb == nil) { return false; };
|
if (pb == nil) { return false; };
|
||||||
if (!typeeqast(pa.lhs, pb.lhs)) { return false; };
|
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
};
|
};
|
||||||
@@ -11199,7 +11209,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
// lands; b1c's (void|size) has none, so byte-id is untouched.
|
// lands; b1c's (void|size) has none, so byte-id is untouched.
|
||||||
if (pa.op == tkind.TK_ELLIPSIS) { return false; };
|
if (pa.op == tkind.TK_ELLIPSIS) { return false; };
|
||||||
if (pb.op == tkind.TK_ELLIPSIS) { return false; };
|
if (pb.op == tkind.TK_ELLIPSIS) { return false; };
|
||||||
if (!typeeqast(pa, pb)) { return false; };
|
if (!typeeqast(c, pa, pb)) { return false; };
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
};
|
};
|
||||||
@@ -12492,7 +12502,7 @@ fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
|||||||
if (ru) {
|
if (ru) {
|
||||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||||
};
|
};
|
||||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
if (typeeqast(c, ltn, rtn)) { return ltn; };
|
||||||
// Mismatched typed pair — return ltn so the binop stamps something;
|
// Mismatched typed pair — return ltn so the binop stamps something;
|
||||||
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
||||||
// the helper was split out of binoptype.
|
// the helper was split out of binoptype.
|
||||||
@@ -12568,7 +12578,7 @@ fn binoptype(c: *checker, e: *node) *node = {
|
|||||||
// broad differing-types flip (typeeqast vs cstage type_eq
|
// broad differing-types flip (typeeqast vs cstage type_eq
|
||||||
// asymmetry risk) stays out.
|
// asymmetry risk) stays out.
|
||||||
if (varianterr(c, ltn) || varianterr(c, rtn)) {
|
if (varianterr(c, ltn) || varianterr(c, rtn)) {
|
||||||
if (!typeeqast(ltn, rtn)) {
|
if (!typeeqast(c, ltn, rtn)) {
|
||||||
deffolderr(c, e, "operands have differing types");
|
deffolderr(c, e, "operands have differing types");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -13994,7 +14004,7 @@ fn addrfnptrmatches(c: *checker, ptr: *node, synth: *node) bool = {
|
|||||||
let ref: *node = resolvealias(c, unwrapbang(pu.lhs));
|
let ref: *node = resolvealias(c, unwrapbang(pu.lhs));
|
||||||
if (ref == nil) { return false; };
|
if (ref == nil) { return false; };
|
||||||
if (ref.kind != nkind.N_TFN) { return false; };
|
if (ref.kind != nkind.N_TFN) { return false; };
|
||||||
return typeeqast(synth, ref);
|
return typeeqast(c, synth, ref);
|
||||||
};
|
};
|
||||||
|
|
||||||
// assignableaddrfn — project #206 Option C gate. Mirror of cstage
|
// assignableaddrfn — project #206 Option C gate. Mirror of cstage
|
||||||
@@ -14060,7 +14070,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let su: *node = resolvealias(c, unwrapbang(src));
|
let su: *node = resolvealias(c, unwrapbang(src));
|
||||||
if (du == nil) { *confident = false; return true; };
|
if (du == nil) { *confident = false; return true; };
|
||||||
if (su == nil) { *confident = false; return true; };
|
if (su == nil) { *confident = false; return true; };
|
||||||
if (typeeqast(du, su)) { return true; };
|
if (typeeqast(c, du, su)) { return true; };
|
||||||
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
||||||
// array with a defined length wherever its element slice is expected
|
// array with a defined length wherever its element slice is expected
|
||||||
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
||||||
@@ -14070,7 +14080,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
// explicit full slice via desugararrayslice; cgen is untouched.
|
// explicit full slice via desugararrayslice; cgen is untouched.
|
||||||
if (du.kind == nkind.N_TSLICE) {
|
if (du.kind == nkind.N_TSLICE) {
|
||||||
if (su.kind == nkind.N_TARRAY) {
|
if (su.kind == nkind.N_TARRAY) {
|
||||||
if (typeeqast(du.lhs, su.lhs)) { return true; };
|
if (typeeqast(c, du.lhs, su.lhs)) { return true; };
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -14201,7 +14211,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let vtagged: bool = false;
|
let vtagged: bool = false;
|
||||||
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
||||||
if (vtagged && !vspread) {
|
if (vtagged && !vspread) {
|
||||||
if (typeeqast(v, src)) { return true; };
|
if (typeeqast(c, v, src)) { return true; };
|
||||||
} else {
|
} else {
|
||||||
let innerconf: bool = false;
|
let innerconf: bool = false;
|
||||||
if (isassignable(c, v, src, &innerconf)) { return true; };
|
if (isassignable(c, v, src, &innerconf)) { return true; };
|
||||||
@@ -14228,7 +14238,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let vu: *node = resolvealias(c, unwrapbang(v));
|
let vu: *node = resolvealias(c, unwrapbang(v));
|
||||||
if (vu != nil) {
|
if (vu != nil) {
|
||||||
if (vu.kind == nkind.N_TTAGGED) {
|
if (vu.kind == nkind.N_TTAGGED) {
|
||||||
if (typeeqast(v, src)) { return true; };
|
if (typeeqast(c, v, src)) { return true; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
v = v.next;
|
v = v.next;
|
||||||
@@ -14375,12 +14385,12 @@ fn taggeddefmod(c: *checker, st: *node) str = {
|
|||||||
|
|
||||||
fn casecovers(c: *checker, cs: *node, want: *node, unionmod: str) bool = {
|
fn casecovers(c: *checker, cs: *node, want: *node, unionmod: str) bool = {
|
||||||
if (cs.lhs != nil) {
|
if (cs.lhs != nil) {
|
||||||
if (typeeqast(cs.lhs, want)) { return true; };
|
if (typeeqast(c, cs.lhs, want)) { return true; };
|
||||||
if (casevariantpairmatch(want, cs.lhs, unionmod)) { return true; };
|
if (casevariantpairmatch(want, cs.lhs, unionmod)) { return true; };
|
||||||
};
|
};
|
||||||
let alt: *node = cs.list;
|
let alt: *node = cs.list;
|
||||||
for (alt != nil) {
|
for (alt != nil) {
|
||||||
if (typeeqast(alt, want)) { return true; };
|
if (typeeqast(c, alt, want)) { return true; };
|
||||||
if (casevariantpairmatch(want, alt, unionmod)) { return true; };
|
if (casevariantpairmatch(want, alt, unionmod)) { return true; };
|
||||||
alt = alt.next;
|
alt = alt.next;
|
||||||
};
|
};
|
||||||
@@ -14427,7 +14437,7 @@ fn casevariantin(c: *checker, tagged: *node, pat: *node, unionmod: str) bool = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (typeeqast(v, pat)) { return true; };
|
if (typeeqast(c, v, pat)) { return true; };
|
||||||
if (casevariantpairmatch(v, pat, unionmod)) { return true; };
|
if (casevariantpairmatch(v, pat, unionmod)) { return true; };
|
||||||
v = v.next;
|
v = v.next;
|
||||||
};
|
};
|
||||||
@@ -14602,7 +14612,7 @@ fn taggedarrayvariantctor(c: *checker, dst: *node, src: *node) bool = {
|
|||||||
let vtagged: bool = false;
|
let vtagged: bool = false;
|
||||||
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
||||||
if (vtagged && !vspread) {
|
if (vtagged && !vspread) {
|
||||||
if (typeeqast(v, src)) { return false; };
|
if (typeeqast(c, v, src)) { return false; };
|
||||||
} else {
|
} else {
|
||||||
let innerconf: bool = false;
|
let innerconf: bool = false;
|
||||||
if (isassignable(c, v, src, &innerconf)) {
|
if (isassignable(c, v, src, &innerconf)) {
|
||||||
@@ -14822,7 +14832,7 @@ fn desugararrayslice(c: *checker, dsttn: *node, srctn: *node, val: *node) *node
|
|||||||
if (su == nil) { return val; };
|
if (su == nil) { return val; };
|
||||||
if (du.kind != nkind.N_TSLICE) { return val; };
|
if (du.kind != nkind.N_TSLICE) { return val; };
|
||||||
if (su.kind != nkind.N_TARRAY) { return val; };
|
if (su.kind != nkind.N_TARRAY) { return val; };
|
||||||
if (!typeeqast(du.lhs, su.lhs)) { return val; };
|
if (!typeeqast(c, du.lhs, su.lhs)) { return val; };
|
||||||
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
||||||
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
||||||
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
||||||
@@ -14898,7 +14908,7 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
|||||||
let au: *node = resolvealias(c, unwrapbang(atype));
|
let au: *node = resolvealias(c, unwrapbang(atype));
|
||||||
if (pu != nil) { if (au != nil) {
|
if (pu != nil) { if (au != nil) {
|
||||||
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
||||||
if (!typeeqast(pu.lhs, au.lhs)) {
|
if (!typeeqast(c, pu.lhs, au.lhs)) {
|
||||||
errnotassign(c, param.lhs, atype, "argument");
|
errnotassign(c, param.lhs, atype, "argument");
|
||||||
};
|
};
|
||||||
}; };
|
}; };
|
||||||
@@ -15433,7 +15443,7 @@ fn checktryprop(c: *checker, n: *node) void = {
|
|||||||
let found: bool = false;
|
let found: bool = false;
|
||||||
let rv: *node = r.list;
|
let rv: *node = r.list;
|
||||||
for (rv != nil) {
|
for (rv != nil) {
|
||||||
if (typeeqast(rv, ev)) {
|
if (typeeqast(c, rv, ev)) {
|
||||||
found = true;
|
found = true;
|
||||||
rv = nil;
|
rv = nil;
|
||||||
} else { rv = rv.next; };
|
} else { rv = rv.next; };
|
||||||
|
|||||||
@@ -840,23 +840,33 @@ fn resolvealias(c: *checker, n: *node) *node = {
|
|||||||
|
|
||||||
// typeeqast — structural equality on AST type expressions, mod
|
// typeeqast — structural equality on AST type expressions, mod
|
||||||
// the `!` wrapper. Mirrors variant_match in cgen + check.c: NAMED
|
// the `!` wrapper. Mirrors variant_match in cgen + check.c: NAMED
|
||||||
// types compare by string (the closest stand-in for pointer
|
// types compare by string fast-path, else by resolved-decl identity
|
||||||
// identity at the AST level); other nodes recurse by kind.
|
// (the AST analog of cstage type.c:278 `TY_NAMED: a == b` and harec
|
||||||
fn typeeqast(a: *node, b: *node) bool = {
|
// types.c:579 `STORAGE_ALIAS: ident_equal`). #14 B-full Layer 1:
|
||||||
|
// bare `oserror` vs qualified `os.oserror` resolve to the SAME
|
||||||
|
// SK_TYPE sym (aliassym maps both via #51/#53), so a cross-module
|
||||||
|
// nominal forward compares equal where the surface streq said false.
|
||||||
|
fn typeeqast(c: *checker, a: *node, b: *node) bool = {
|
||||||
let aa: *node = unwrapbang(a);
|
let aa: *node = unwrapbang(a);
|
||||||
let bb: *node = unwrapbang(b);
|
let bb: *node = unwrapbang(b);
|
||||||
if (aa == nil) { return bb == nil; };
|
if (aa == nil) { return bb == nil; };
|
||||||
if (bb == nil) { return false; };
|
if (bb == nil) { return false; };
|
||||||
if (aa.kind != bb.kind) { return false; };
|
if (aa.kind != bb.kind) { return false; };
|
||||||
let k: nkind = aa.kind;
|
let k: nkind = aa.kind;
|
||||||
if (k == nkind.N_TNAME) { return streq(aa.str, bb.str); };
|
if (k == nkind.N_TNAME) {
|
||||||
if (k == nkind.N_TPTR) { return typeeqast(aa.lhs, bb.lhs); };
|
if (streq(aa.str, bb.str)) { return true; };
|
||||||
if (k == nkind.N_TSLICE){ return typeeqast(aa.lhs, bb.lhs); };
|
let sa: *sym = aliassym(c, aa);
|
||||||
if (k == nkind.N_TCHAN) { return typeeqast(aa.lhs, bb.lhs); };
|
let sb: *sym = aliassym(c, bb);
|
||||||
|
if (sa != nil && sa == sb) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (k == nkind.N_TPTR) { return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
|
if (k == nkind.N_TSLICE){ return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
|
if (k == nkind.N_TCHAN) { return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
if (k == nkind.N_TFN) {
|
if (k == nkind.N_TFN) {
|
||||||
// Divergence: cstage type.c:239 compares resolved Type; we
|
// Divergence: cstage type.c:239 compares resolved Type; we
|
||||||
// compare AST. See #178.
|
// compare AST. See #178.
|
||||||
if (!typeeqast(aa.lhs, bb.lhs)) { return false; };
|
if (!typeeqast(c, aa.lhs, bb.lhs)) { return false; };
|
||||||
let pa: *node = aa.list;
|
let pa: *node = aa.list;
|
||||||
let pb: *node = bb.list;
|
let pb: *node = bb.list;
|
||||||
for (pa != nil) {
|
for (pa != nil) {
|
||||||
@@ -868,7 +878,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
let va: bool = pa.op == tkind.TK_ELLIPSIS;
|
let va: bool = pa.op == tkind.TK_ELLIPSIS;
|
||||||
let vb: bool = pb.op == tkind.TK_ELLIPSIS;
|
let vb: bool = pb.op == tkind.TK_ELLIPSIS;
|
||||||
if (va != vb) { return false; };
|
if (va != vb) { return false; };
|
||||||
if (!typeeqast(pa.lhs, pb.lhs)) { return false; };
|
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
|
||||||
};
|
};
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
@@ -887,7 +897,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
let pb: *node = bb.list;
|
let pb: *node = bb.list;
|
||||||
for (pa != nil) {
|
for (pa != nil) {
|
||||||
if (pb == nil) { return false; };
|
if (pb == nil) { return false; };
|
||||||
if (!typeeqast(pa.lhs, pb.lhs)) { return false; };
|
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
};
|
};
|
||||||
@@ -918,7 +928,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
// lands; b1c's (void|size) has none, so byte-id is untouched.
|
// lands; b1c's (void|size) has none, so byte-id is untouched.
|
||||||
if (pa.op == tkind.TK_ELLIPSIS) { return false; };
|
if (pa.op == tkind.TK_ELLIPSIS) { return false; };
|
||||||
if (pb.op == tkind.TK_ELLIPSIS) { return false; };
|
if (pb.op == tkind.TK_ELLIPSIS) { return false; };
|
||||||
if (!typeeqast(pa, pb)) { return false; };
|
if (!typeeqast(c, pa, pb)) { return false; };
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
};
|
};
|
||||||
@@ -2211,7 +2221,7 @@ fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
|||||||
if (ru) {
|
if (ru) {
|
||||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||||
};
|
};
|
||||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
if (typeeqast(c, ltn, rtn)) { return ltn; };
|
||||||
// Mismatched typed pair — return ltn so the binop stamps something;
|
// Mismatched typed pair — return ltn so the binop stamps something;
|
||||||
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
||||||
// the helper was split out of binoptype.
|
// the helper was split out of binoptype.
|
||||||
@@ -2287,7 +2297,7 @@ fn binoptype(c: *checker, e: *node) *node = {
|
|||||||
// broad differing-types flip (typeeqast vs cstage type_eq
|
// broad differing-types flip (typeeqast vs cstage type_eq
|
||||||
// asymmetry risk) stays out.
|
// asymmetry risk) stays out.
|
||||||
if (varianterr(c, ltn) || varianterr(c, rtn)) {
|
if (varianterr(c, ltn) || varianterr(c, rtn)) {
|
||||||
if (!typeeqast(ltn, rtn)) {
|
if (!typeeqast(c, ltn, rtn)) {
|
||||||
deffolderr(c, e, "operands have differing types");
|
deffolderr(c, e, "operands have differing types");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -3713,7 +3723,7 @@ fn addrfnptrmatches(c: *checker, ptr: *node, synth: *node) bool = {
|
|||||||
let ref: *node = resolvealias(c, unwrapbang(pu.lhs));
|
let ref: *node = resolvealias(c, unwrapbang(pu.lhs));
|
||||||
if (ref == nil) { return false; };
|
if (ref == nil) { return false; };
|
||||||
if (ref.kind != nkind.N_TFN) { return false; };
|
if (ref.kind != nkind.N_TFN) { return false; };
|
||||||
return typeeqast(synth, ref);
|
return typeeqast(c, synth, ref);
|
||||||
};
|
};
|
||||||
|
|
||||||
// assignableaddrfn — project #206 Option C gate. Mirror of cstage
|
// assignableaddrfn — project #206 Option C gate. Mirror of cstage
|
||||||
@@ -3779,7 +3789,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let su: *node = resolvealias(c, unwrapbang(src));
|
let su: *node = resolvealias(c, unwrapbang(src));
|
||||||
if (du == nil) { *confident = false; return true; };
|
if (du == nil) { *confident = false; return true; };
|
||||||
if (su == nil) { *confident = false; return true; };
|
if (su == nil) { *confident = false; return true; };
|
||||||
if (typeeqast(du, su)) { return true; };
|
if (typeeqast(c, du, su)) { return true; };
|
||||||
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
||||||
// array with a defined length wherever its element slice is expected
|
// array with a defined length wherever its element slice is expected
|
||||||
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
||||||
@@ -3789,7 +3799,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
// explicit full slice via desugararrayslice; cgen is untouched.
|
// explicit full slice via desugararrayslice; cgen is untouched.
|
||||||
if (du.kind == nkind.N_TSLICE) {
|
if (du.kind == nkind.N_TSLICE) {
|
||||||
if (su.kind == nkind.N_TARRAY) {
|
if (su.kind == nkind.N_TARRAY) {
|
||||||
if (typeeqast(du.lhs, su.lhs)) { return true; };
|
if (typeeqast(c, du.lhs, su.lhs)) { return true; };
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -3920,7 +3930,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let vtagged: bool = false;
|
let vtagged: bool = false;
|
||||||
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
||||||
if (vtagged && !vspread) {
|
if (vtagged && !vspread) {
|
||||||
if (typeeqast(v, src)) { return true; };
|
if (typeeqast(c, v, src)) { return true; };
|
||||||
} else {
|
} else {
|
||||||
let innerconf: bool = false;
|
let innerconf: bool = false;
|
||||||
if (isassignable(c, v, src, &innerconf)) { return true; };
|
if (isassignable(c, v, src, &innerconf)) { return true; };
|
||||||
@@ -3947,7 +3957,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let vu: *node = resolvealias(c, unwrapbang(v));
|
let vu: *node = resolvealias(c, unwrapbang(v));
|
||||||
if (vu != nil) {
|
if (vu != nil) {
|
||||||
if (vu.kind == nkind.N_TTAGGED) {
|
if (vu.kind == nkind.N_TTAGGED) {
|
||||||
if (typeeqast(v, src)) { return true; };
|
if (typeeqast(c, v, src)) { return true; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
v = v.next;
|
v = v.next;
|
||||||
@@ -4094,12 +4104,12 @@ fn taggeddefmod(c: *checker, st: *node) str = {
|
|||||||
|
|
||||||
fn casecovers(c: *checker, cs: *node, want: *node, unionmod: str) bool = {
|
fn casecovers(c: *checker, cs: *node, want: *node, unionmod: str) bool = {
|
||||||
if (cs.lhs != nil) {
|
if (cs.lhs != nil) {
|
||||||
if (typeeqast(cs.lhs, want)) { return true; };
|
if (typeeqast(c, cs.lhs, want)) { return true; };
|
||||||
if (casevariantpairmatch(want, cs.lhs, unionmod)) { return true; };
|
if (casevariantpairmatch(want, cs.lhs, unionmod)) { return true; };
|
||||||
};
|
};
|
||||||
let alt: *node = cs.list;
|
let alt: *node = cs.list;
|
||||||
for (alt != nil) {
|
for (alt != nil) {
|
||||||
if (typeeqast(alt, want)) { return true; };
|
if (typeeqast(c, alt, want)) { return true; };
|
||||||
if (casevariantpairmatch(want, alt, unionmod)) { return true; };
|
if (casevariantpairmatch(want, alt, unionmod)) { return true; };
|
||||||
alt = alt.next;
|
alt = alt.next;
|
||||||
};
|
};
|
||||||
@@ -4146,7 +4156,7 @@ fn casevariantin(c: *checker, tagged: *node, pat: *node, unionmod: str) bool = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (typeeqast(v, pat)) { return true; };
|
if (typeeqast(c, v, pat)) { return true; };
|
||||||
if (casevariantpairmatch(v, pat, unionmod)) { return true; };
|
if (casevariantpairmatch(v, pat, unionmod)) { return true; };
|
||||||
v = v.next;
|
v = v.next;
|
||||||
};
|
};
|
||||||
@@ -4321,7 +4331,7 @@ fn taggedarrayvariantctor(c: *checker, dst: *node, src: *node) bool = {
|
|||||||
let vtagged: bool = false;
|
let vtagged: bool = false;
|
||||||
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
||||||
if (vtagged && !vspread) {
|
if (vtagged && !vspread) {
|
||||||
if (typeeqast(v, src)) { return false; };
|
if (typeeqast(c, v, src)) { return false; };
|
||||||
} else {
|
} else {
|
||||||
let innerconf: bool = false;
|
let innerconf: bool = false;
|
||||||
if (isassignable(c, v, src, &innerconf)) {
|
if (isassignable(c, v, src, &innerconf)) {
|
||||||
@@ -4541,7 +4551,7 @@ fn desugararrayslice(c: *checker, dsttn: *node, srctn: *node, val: *node) *node
|
|||||||
if (su == nil) { return val; };
|
if (su == nil) { return val; };
|
||||||
if (du.kind != nkind.N_TSLICE) { return val; };
|
if (du.kind != nkind.N_TSLICE) { return val; };
|
||||||
if (su.kind != nkind.N_TARRAY) { return val; };
|
if (su.kind != nkind.N_TARRAY) { return val; };
|
||||||
if (!typeeqast(du.lhs, su.lhs)) { return val; };
|
if (!typeeqast(c, du.lhs, su.lhs)) { return val; };
|
||||||
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
||||||
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
||||||
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
||||||
@@ -4617,7 +4627,7 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
|||||||
let au: *node = resolvealias(c, unwrapbang(atype));
|
let au: *node = resolvealias(c, unwrapbang(atype));
|
||||||
if (pu != nil) { if (au != nil) {
|
if (pu != nil) { if (au != nil) {
|
||||||
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
||||||
if (!typeeqast(pu.lhs, au.lhs)) {
|
if (!typeeqast(c, pu.lhs, au.lhs)) {
|
||||||
errnotassign(c, param.lhs, atype, "argument");
|
errnotassign(c, param.lhs, atype, "argument");
|
||||||
};
|
};
|
||||||
}; };
|
}; };
|
||||||
@@ -5152,7 +5162,7 @@ fn checktryprop(c: *checker, n: *node) void = {
|
|||||||
let found: bool = false;
|
let found: bool = false;
|
||||||
let rv: *node = r.list;
|
let rv: *node = r.list;
|
||||||
for (rv != nil) {
|
for (rv != nil) {
|
||||||
if (typeeqast(rv, ev)) {
|
if (typeeqast(c, rv, ev)) {
|
||||||
found = true;
|
found = true;
|
||||||
rv = nil;
|
rv = nil;
|
||||||
} else { rv = rv.next; };
|
} else { rv = rv.next; };
|
||||||
|
|||||||
@@ -11121,23 +11121,33 @@ fn resolvealias(c: *checker, n: *node) *node = {
|
|||||||
|
|
||||||
// typeeqast — structural equality on AST type expressions, mod
|
// typeeqast — structural equality on AST type expressions, mod
|
||||||
// the `!` wrapper. Mirrors variant_match in cgen + check.c: NAMED
|
// the `!` wrapper. Mirrors variant_match in cgen + check.c: NAMED
|
||||||
// types compare by string (the closest stand-in for pointer
|
// types compare by string fast-path, else by resolved-decl identity
|
||||||
// identity at the AST level); other nodes recurse by kind.
|
// (the AST analog of cstage type.c:278 `TY_NAMED: a == b` and harec
|
||||||
fn typeeqast(a: *node, b: *node) bool = {
|
// types.c:579 `STORAGE_ALIAS: ident_equal`). #14 B-full Layer 1:
|
||||||
|
// bare `oserror` vs qualified `os.oserror` resolve to the SAME
|
||||||
|
// SK_TYPE sym (aliassym maps both via #51/#53), so a cross-module
|
||||||
|
// nominal forward compares equal where the surface streq said false.
|
||||||
|
fn typeeqast(c: *checker, a: *node, b: *node) bool = {
|
||||||
let aa: *node = unwrapbang(a);
|
let aa: *node = unwrapbang(a);
|
||||||
let bb: *node = unwrapbang(b);
|
let bb: *node = unwrapbang(b);
|
||||||
if (aa == nil) { return bb == nil; };
|
if (aa == nil) { return bb == nil; };
|
||||||
if (bb == nil) { return false; };
|
if (bb == nil) { return false; };
|
||||||
if (aa.kind != bb.kind) { return false; };
|
if (aa.kind != bb.kind) { return false; };
|
||||||
let k: nkind = aa.kind;
|
let k: nkind = aa.kind;
|
||||||
if (k == nkind.N_TNAME) { return streq(aa.str, bb.str); };
|
if (k == nkind.N_TNAME) {
|
||||||
if (k == nkind.N_TPTR) { return typeeqast(aa.lhs, bb.lhs); };
|
if (streq(aa.str, bb.str)) { return true; };
|
||||||
if (k == nkind.N_TSLICE){ return typeeqast(aa.lhs, bb.lhs); };
|
let sa: *sym = aliassym(c, aa);
|
||||||
if (k == nkind.N_TCHAN) { return typeeqast(aa.lhs, bb.lhs); };
|
let sb: *sym = aliassym(c, bb);
|
||||||
|
if (sa != nil && sa == sb) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (k == nkind.N_TPTR) { return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
|
if (k == nkind.N_TSLICE){ return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
|
if (k == nkind.N_TCHAN) { return typeeqast(c, aa.lhs, bb.lhs); };
|
||||||
if (k == nkind.N_TFN) {
|
if (k == nkind.N_TFN) {
|
||||||
// Divergence: cstage type.c:239 compares resolved Type; we
|
// Divergence: cstage type.c:239 compares resolved Type; we
|
||||||
// compare AST. See #178.
|
// compare AST. See #178.
|
||||||
if (!typeeqast(aa.lhs, bb.lhs)) { return false; };
|
if (!typeeqast(c, aa.lhs, bb.lhs)) { return false; };
|
||||||
let pa: *node = aa.list;
|
let pa: *node = aa.list;
|
||||||
let pb: *node = bb.list;
|
let pb: *node = bb.list;
|
||||||
for (pa != nil) {
|
for (pa != nil) {
|
||||||
@@ -11149,7 +11159,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
let va: bool = pa.op == tkind.TK_ELLIPSIS;
|
let va: bool = pa.op == tkind.TK_ELLIPSIS;
|
||||||
let vb: bool = pb.op == tkind.TK_ELLIPSIS;
|
let vb: bool = pb.op == tkind.TK_ELLIPSIS;
|
||||||
if (va != vb) { return false; };
|
if (va != vb) { return false; };
|
||||||
if (!typeeqast(pa.lhs, pb.lhs)) { return false; };
|
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
|
||||||
};
|
};
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
@@ -11168,7 +11178,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
let pb: *node = bb.list;
|
let pb: *node = bb.list;
|
||||||
for (pa != nil) {
|
for (pa != nil) {
|
||||||
if (pb == nil) { return false; };
|
if (pb == nil) { return false; };
|
||||||
if (!typeeqast(pa.lhs, pb.lhs)) { return false; };
|
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
};
|
};
|
||||||
@@ -11199,7 +11209,7 @@ fn typeeqast(a: *node, b: *node) bool = {
|
|||||||
// lands; b1c's (void|size) has none, so byte-id is untouched.
|
// lands; b1c's (void|size) has none, so byte-id is untouched.
|
||||||
if (pa.op == tkind.TK_ELLIPSIS) { return false; };
|
if (pa.op == tkind.TK_ELLIPSIS) { return false; };
|
||||||
if (pb.op == tkind.TK_ELLIPSIS) { return false; };
|
if (pb.op == tkind.TK_ELLIPSIS) { return false; };
|
||||||
if (!typeeqast(pa, pb)) { return false; };
|
if (!typeeqast(c, pa, pb)) { return false; };
|
||||||
pa = pa.next;
|
pa = pa.next;
|
||||||
pb = pb.next;
|
pb = pb.next;
|
||||||
};
|
};
|
||||||
@@ -12492,7 +12502,7 @@ fn unifyarith(c: *checker, ltn: *node, rtn: *node) *node = {
|
|||||||
if (ru) {
|
if (ru) {
|
||||||
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
if (isassignable(c, ltn, rtn, &conf)) { return ltn; };
|
||||||
};
|
};
|
||||||
if (typeeqast(ltn, rtn)) { return ltn; };
|
if (typeeqast(c, ltn, rtn)) { return ltn; };
|
||||||
// Mismatched typed pair — return ltn so the binop stamps something;
|
// Mismatched typed pair — return ltn so the binop stamps something;
|
||||||
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
// 5-lite-b: the trailing nil-on-mismatch shape was eliminated when
|
||||||
// the helper was split out of binoptype.
|
// the helper was split out of binoptype.
|
||||||
@@ -12568,7 +12578,7 @@ fn binoptype(c: *checker, e: *node) *node = {
|
|||||||
// broad differing-types flip (typeeqast vs cstage type_eq
|
// broad differing-types flip (typeeqast vs cstage type_eq
|
||||||
// asymmetry risk) stays out.
|
// asymmetry risk) stays out.
|
||||||
if (varianterr(c, ltn) || varianterr(c, rtn)) {
|
if (varianterr(c, ltn) || varianterr(c, rtn)) {
|
||||||
if (!typeeqast(ltn, rtn)) {
|
if (!typeeqast(c, ltn, rtn)) {
|
||||||
deffolderr(c, e, "operands have differing types");
|
deffolderr(c, e, "operands have differing types");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -13994,7 +14004,7 @@ fn addrfnptrmatches(c: *checker, ptr: *node, synth: *node) bool = {
|
|||||||
let ref: *node = resolvealias(c, unwrapbang(pu.lhs));
|
let ref: *node = resolvealias(c, unwrapbang(pu.lhs));
|
||||||
if (ref == nil) { return false; };
|
if (ref == nil) { return false; };
|
||||||
if (ref.kind != nkind.N_TFN) { return false; };
|
if (ref.kind != nkind.N_TFN) { return false; };
|
||||||
return typeeqast(synth, ref);
|
return typeeqast(c, synth, ref);
|
||||||
};
|
};
|
||||||
|
|
||||||
// assignableaddrfn — project #206 Option C gate. Mirror of cstage
|
// assignableaddrfn — project #206 Option C gate. Mirror of cstage
|
||||||
@@ -14060,7 +14070,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let su: *node = resolvealias(c, unwrapbang(src));
|
let su: *node = resolvealias(c, unwrapbang(src));
|
||||||
if (du == nil) { *confident = false; return true; };
|
if (du == nil) { *confident = false; return true; };
|
||||||
if (su == nil) { *confident = false; return true; };
|
if (su == nil) { *confident = false; return true; };
|
||||||
if (typeeqast(du, su)) { return true; };
|
if (typeeqast(c, du, su)) { return true; };
|
||||||
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
// #258: implicit [N]T -> []T array-to-slice borrow. Hare admits an
|
||||||
// array with a defined length wherever its element slice is expected
|
// array with a defined length wherever its element slice is expected
|
||||||
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
// (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). Element types
|
||||||
@@ -14070,7 +14080,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
// explicit full slice via desugararrayslice; cgen is untouched.
|
// explicit full slice via desugararrayslice; cgen is untouched.
|
||||||
if (du.kind == nkind.N_TSLICE) {
|
if (du.kind == nkind.N_TSLICE) {
|
||||||
if (su.kind == nkind.N_TARRAY) {
|
if (su.kind == nkind.N_TARRAY) {
|
||||||
if (typeeqast(du.lhs, su.lhs)) { return true; };
|
if (typeeqast(c, du.lhs, su.lhs)) { return true; };
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -14201,7 +14211,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let vtagged: bool = false;
|
let vtagged: bool = false;
|
||||||
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
||||||
if (vtagged && !vspread) {
|
if (vtagged && !vspread) {
|
||||||
if (typeeqast(v, src)) { return true; };
|
if (typeeqast(c, v, src)) { return true; };
|
||||||
} else {
|
} else {
|
||||||
let innerconf: bool = false;
|
let innerconf: bool = false;
|
||||||
if (isassignable(c, v, src, &innerconf)) { return true; };
|
if (isassignable(c, v, src, &innerconf)) { return true; };
|
||||||
@@ -14228,7 +14238,7 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
let vu: *node = resolvealias(c, unwrapbang(v));
|
let vu: *node = resolvealias(c, unwrapbang(v));
|
||||||
if (vu != nil) {
|
if (vu != nil) {
|
||||||
if (vu.kind == nkind.N_TTAGGED) {
|
if (vu.kind == nkind.N_TTAGGED) {
|
||||||
if (typeeqast(v, src)) { return true; };
|
if (typeeqast(c, v, src)) { return true; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
v = v.next;
|
v = v.next;
|
||||||
@@ -14375,12 +14385,12 @@ fn taggeddefmod(c: *checker, st: *node) str = {
|
|||||||
|
|
||||||
fn casecovers(c: *checker, cs: *node, want: *node, unionmod: str) bool = {
|
fn casecovers(c: *checker, cs: *node, want: *node, unionmod: str) bool = {
|
||||||
if (cs.lhs != nil) {
|
if (cs.lhs != nil) {
|
||||||
if (typeeqast(cs.lhs, want)) { return true; };
|
if (typeeqast(c, cs.lhs, want)) { return true; };
|
||||||
if (casevariantpairmatch(want, cs.lhs, unionmod)) { return true; };
|
if (casevariantpairmatch(want, cs.lhs, unionmod)) { return true; };
|
||||||
};
|
};
|
||||||
let alt: *node = cs.list;
|
let alt: *node = cs.list;
|
||||||
for (alt != nil) {
|
for (alt != nil) {
|
||||||
if (typeeqast(alt, want)) { return true; };
|
if (typeeqast(c, alt, want)) { return true; };
|
||||||
if (casevariantpairmatch(want, alt, unionmod)) { return true; };
|
if (casevariantpairmatch(want, alt, unionmod)) { return true; };
|
||||||
alt = alt.next;
|
alt = alt.next;
|
||||||
};
|
};
|
||||||
@@ -14427,7 +14437,7 @@ fn casevariantin(c: *checker, tagged: *node, pat: *node, unionmod: str) bool = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (typeeqast(v, pat)) { return true; };
|
if (typeeqast(c, v, pat)) { return true; };
|
||||||
if (casevariantpairmatch(v, pat, unionmod)) { return true; };
|
if (casevariantpairmatch(v, pat, unionmod)) { return true; };
|
||||||
v = v.next;
|
v = v.next;
|
||||||
};
|
};
|
||||||
@@ -14602,7 +14612,7 @@ fn taggedarrayvariantctor(c: *checker, dst: *node, src: *node) bool = {
|
|||||||
let vtagged: bool = false;
|
let vtagged: bool = false;
|
||||||
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
if (vu != nil) { if (vu.kind == nkind.N_TTAGGED) { vtagged = true; }; };
|
||||||
if (vtagged && !vspread) {
|
if (vtagged && !vspread) {
|
||||||
if (typeeqast(v, src)) { return false; };
|
if (typeeqast(c, v, src)) { return false; };
|
||||||
} else {
|
} else {
|
||||||
let innerconf: bool = false;
|
let innerconf: bool = false;
|
||||||
if (isassignable(c, v, src, &innerconf)) {
|
if (isassignable(c, v, src, &innerconf)) {
|
||||||
@@ -14822,7 +14832,7 @@ fn desugararrayslice(c: *checker, dsttn: *node, srctn: *node, val: *node) *node
|
|||||||
if (su == nil) { return val; };
|
if (su == nil) { return val; };
|
||||||
if (du.kind != nkind.N_TSLICE) { return val; };
|
if (du.kind != nkind.N_TSLICE) { return val; };
|
||||||
if (su.kind != nkind.N_TARRAY) { return val; };
|
if (su.kind != nkind.N_TARRAY) { return val; };
|
||||||
if (!typeeqast(du.lhs, su.lhs)) { return val; };
|
if (!typeeqast(c, du.lhs, su.lhs)) { return val; };
|
||||||
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
let sl: *node = newnode(nkind.N_SLICE, val.file, val.line, val.col);
|
||||||
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
sl.lhs = val; // sliced base; lo (.rhs) / hi (.cond) nil → 0 : len(arr)
|
||||||
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
let slt: *node = newnode(nkind.N_TSLICE, "", 0, 0);
|
||||||
@@ -14898,7 +14908,7 @@ fn desugarcallargs(c: *checker, n: *node) void = {
|
|||||||
let au: *node = resolvealias(c, unwrapbang(atype));
|
let au: *node = resolvealias(c, unwrapbang(atype));
|
||||||
if (pu != nil) { if (au != nil) {
|
if (pu != nil) { if (au != nil) {
|
||||||
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
if (pu.kind == nkind.N_TSLICE) { if (au.kind == nkind.N_TARRAY) {
|
||||||
if (!typeeqast(pu.lhs, au.lhs)) {
|
if (!typeeqast(c, pu.lhs, au.lhs)) {
|
||||||
errnotassign(c, param.lhs, atype, "argument");
|
errnotassign(c, param.lhs, atype, "argument");
|
||||||
};
|
};
|
||||||
}; };
|
}; };
|
||||||
@@ -15433,7 +15443,7 @@ fn checktryprop(c: *checker, n: *node) void = {
|
|||||||
let found: bool = false;
|
let found: bool = false;
|
||||||
let rv: *node = r.list;
|
let rv: *node = r.list;
|
||||||
for (rv != nil) {
|
for (rv != nil) {
|
||||||
if (typeeqast(rv, ev)) {
|
if (typeeqast(c, rv, ev)) {
|
||||||
found = true;
|
found = true;
|
||||||
rv = nil;
|
rv = nil;
|
||||||
} else { rv = rv.next; };
|
} else { rv = rv.next; };
|
||||||
|
|||||||
279
test/wcc/839_xmod_nominal_typeeqast.c
Normal file
279
test/wcc/839_xmod_nominal_typeeqast.c
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
* 839_xmod_nominal_typeeqast — B-full Layer 1 (#14): typeeqast's N_TNAME
|
||||||
|
* arm now compares by RESOLVED-decl identity (aliassym pointer-equality),
|
||||||
|
* not surface spelling. A cross-module type referenced bare inside its
|
||||||
|
* defining module (`myerr`) and qualified by a consumer (`e.myerr`) used
|
||||||
|
* to mis-compare unequal (streq), so a nominal forward / variant match was
|
||||||
|
* silently mis-identified. cstage already equates them (type.c:278
|
||||||
|
* `TY_NAMED: a == b` on resolved Type); harec equates them
|
||||||
|
* (types.c:579 `STORAGE_ALIAS: ident_equal`). ww now matches at the AST
|
||||||
|
* level via the existing #51/#53 resolver — NO type-interning layer.
|
||||||
|
*
|
||||||
|
* The headline discriminator for this fix is the lib/shlex graduation
|
||||||
|
* (989 #59.15: M_WWREJECT -> M_ID). That flip needs shlex's full
|
||||||
|
* `(size | io.error)` match pipeline and is NOT reproducible standalone,
|
||||||
|
* so the gate that actually distinguishes pre-fix surface-streq from
|
||||||
|
* post-fix nominal-aliassym is 989_lib_byteid, not this test.
|
||||||
|
*
|
||||||
|
* scenario | shape | gate
|
||||||
|
* ------------------+---------------------------------------------+------
|
||||||
|
* nominal_match | match e.res whose variants are spelled BARE | 42
|
||||||
|
* | inside e, with QUALIFIED case patterns |
|
||||||
|
* | (`case e.myerr`) — nominal cover |
|
||||||
|
* a6_concrete | concrete `e.myerr` returned into `e.res` | 0
|
||||||
|
* | (A6 concrete->tagged, bare-vs-qualified) |
|
||||||
|
* incompat_concrete | `str` into `(i64 | e.myerr)` — NO str | FAIL
|
||||||
|
* | variant: confident reject (no over-accept)|
|
||||||
|
* distinct_alias | distinct alias `other = !i64` into `e.res` | FAIL
|
||||||
|
* | — rejected at CGEN (#95 structural |
|
||||||
|
* | ambiguity: two i64-underlying variants), |
|
||||||
|
* | symmetric both stages |
|
||||||
|
*
|
||||||
|
* SCOPE / no-teeth disclosure: all four rows decide IDENTICALLY against
|
||||||
|
* the pre-fix surface-streq HEAD (nominal_match->42, a6_concrete->0, the
|
||||||
|
* two NEG rows reject) on BOTH stages — the nominal-identity change does
|
||||||
|
* not flip any of them, because each is reached through a sibling
|
||||||
|
* mechanism that already handled bare-vs-qualified: nominal_match via
|
||||||
|
* casevariantpairmatch (#205 match-cover resolve), a6_concrete via the
|
||||||
|
* concrete->tagged arm's underlying-i64 resolvealias, and distinct_alias
|
||||||
|
* via the cgen #95 ambiguity reject (the CHECKER accepts it — `other`'s
|
||||||
|
* underlying i64 matches a variant; the reject is structural, not
|
||||||
|
* nominal). So this test does NOT guard a revert of typeeqast to streq;
|
||||||
|
* that is 989 #59.15's job. What it DOES pin is that the four
|
||||||
|
* cross-module nominal accept/reject decisions stay correct AND symmetric
|
||||||
|
* across cstage/wwstage (rule-10) — a forward guard against either stage
|
||||||
|
* drifting on cross-module nominal forwards.
|
||||||
|
*
|
||||||
|
* Both stages must agree (rule-10): the POS rows build+run to the same
|
||||||
|
* exit on cstage and wwstage; the NEG rows build-FAIL on both. Byte-id of
|
||||||
|
* the emitted asm is NOT asserted here — receiving a cross-module tagged
|
||||||
|
* return and re-passing it crosses a pre-existing, unrelated cgen frame-
|
||||||
|
* layout divergence; B-full Layer 1's byte-id-neutrality is gated by
|
||||||
|
* 989_lib_byteid + 990-997 (the bootstrap forwards).
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
runwait(const char *cmd)
|
||||||
|
{
|
||||||
|
int rc = system(cmd);
|
||||||
|
if (rc == -1) return -1;
|
||||||
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file { const char *name; const char *src; };
|
||||||
|
|
||||||
|
struct scenario {
|
||||||
|
const char *label;
|
||||||
|
const struct file *files; /* name==NULL terminates */
|
||||||
|
int expect_build; /* 1 = build+run to want_exit; 0 = must FAIL */
|
||||||
|
int want_exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- nominal_match: bare union variants, qualified case patterns ---- */
|
||||||
|
static const struct file nominal_match_files[] = {
|
||||||
|
{ "e.ww",
|
||||||
|
"package e;\n"
|
||||||
|
"\n"
|
||||||
|
"export type myerr = !i64;\n"
|
||||||
|
"export type res = (i64 | myerr);\n"
|
||||||
|
"\n"
|
||||||
|
"export fn ok(v: i64) res = { return v; };\n"
|
||||||
|
"export fn bad() res = { return (-7: myerr); };\n" },
|
||||||
|
{ "main.ww",
|
||||||
|
"package main;\n"
|
||||||
|
"\n"
|
||||||
|
"import e;\n"
|
||||||
|
"\n"
|
||||||
|
"fn unwrap(r: e.res) i64 = {\n"
|
||||||
|
" match (r) {\n"
|
||||||
|
" case let n: i64 => return n;\n"
|
||||||
|
" case let x: e.myerr => return -1;\n"
|
||||||
|
" };\n"
|
||||||
|
"};\n"
|
||||||
|
"\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
" let a = unwrap(e.ok(40));\n"
|
||||||
|
" let b = unwrap(e.bad());\n"
|
||||||
|
" if (a != 40) { return 11; };\n"
|
||||||
|
" if (b != -1) { return 12; };\n"
|
||||||
|
" return 42;\n"
|
||||||
|
"};\n" },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- a6_concrete: concrete e.myerr into e.res return (A6) ---------- */
|
||||||
|
static const struct file a6_concrete_files[] = {
|
||||||
|
{ "e.ww",
|
||||||
|
"package e;\n"
|
||||||
|
"\n"
|
||||||
|
"export type myerr = !i64;\n"
|
||||||
|
"export type res = (i64 | myerr);\n" },
|
||||||
|
{ "main.ww",
|
||||||
|
"package main;\n"
|
||||||
|
"\n"
|
||||||
|
"import e;\n"
|
||||||
|
"\n"
|
||||||
|
"fn wrap(x: e.myerr) e.res = { return x; };\n"
|
||||||
|
"\n"
|
||||||
|
"export fn main() i32 = { return 0; };\n" },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- incompat_concrete: str into (i64|e.myerr) — must reject ------- */
|
||||||
|
static const struct file incompat_concrete_files[] = {
|
||||||
|
{ "e.ww",
|
||||||
|
"package e;\n"
|
||||||
|
"\n"
|
||||||
|
"export type myerr = !i64;\n" },
|
||||||
|
{ "main.ww",
|
||||||
|
"package main;\n"
|
||||||
|
"\n"
|
||||||
|
"import e;\n"
|
||||||
|
"\n"
|
||||||
|
"type u = (i64 | e.myerr);\n"
|
||||||
|
"\n"
|
||||||
|
"fn bad(s: str) u = { return s; };\n"
|
||||||
|
"\n"
|
||||||
|
"export fn main() i32 = { return 0; };\n" },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- distinct_alias: distinct alias over same underlying — reject -- */
|
||||||
|
static const struct file distinct_alias_files[] = {
|
||||||
|
{ "e.ww",
|
||||||
|
"package e;\n"
|
||||||
|
"\n"
|
||||||
|
"export type myerr = !i64;\n"
|
||||||
|
"export type res = (i64 | myerr);\n" },
|
||||||
|
{ "main.ww",
|
||||||
|
"package main;\n"
|
||||||
|
"\n"
|
||||||
|
"import e;\n"
|
||||||
|
"\n"
|
||||||
|
"type other = !i64;\n"
|
||||||
|
"\n"
|
||||||
|
"fn f(x: other) e.res = { return x; };\n"
|
||||||
|
"\n"
|
||||||
|
"export fn main() i32 = { return 0; };\n" },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct scenario scenarios[] = {
|
||||||
|
{ "nominal_match", nominal_match_files, 1, 42 },
|
||||||
|
{ "a6_concrete", a6_concrete_files, 1, 0 },
|
||||||
|
{ "incompat_concrete", incompat_concrete_files, 0, 0 },
|
||||||
|
{ "distinct_alias", distinct_alias_files, 0, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct driver { const char *name; char path[2100]; int gated; };
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_one(const struct driver *drv, const struct scenario *sc)
|
||||||
|
{
|
||||||
|
char dir[] = "/tmp/ww839_XXXXXX";
|
||||||
|
if (mkdtemp(dir) == NULL) {
|
||||||
|
fprintf(stderr, "839[%s][%s]: mkdtemp failed\n",
|
||||||
|
drv->name, sc->label);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[1024], cmd[4096];
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
for (int i = 0; sc->files[i].name; i++) {
|
||||||
|
snprintf(path, sizeof path, "%s/%s", dir, sc->files[i].name);
|
||||||
|
FILE *f = fopen(path, "wb");
|
||||||
|
if (!f) {
|
||||||
|
fprintf(stderr, "839[%s][%s]: write %s\n",
|
||||||
|
drv->name, sc->label, sc->files[i].name);
|
||||||
|
rc = -1; goto done;
|
||||||
|
}
|
||||||
|
fputs(sc->files[i].src, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd, "cd %s && %s build -I %s %s/main.ww "
|
||||||
|
">/dev/null 2>&1", dir, drv->path, dir, dir);
|
||||||
|
int brc = runwait(cmd);
|
||||||
|
|
||||||
|
if (!sc->expect_build) {
|
||||||
|
/* NEG: the build must FAIL on this stage. */
|
||||||
|
if (brc == 0) {
|
||||||
|
fprintf(stderr, "839[%s][%s]: built ok, expected a "
|
||||||
|
"confident reject\n", drv->name, sc->label);
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brc != 0) {
|
||||||
|
fprintf(stderr, "839[%s][%s]: build failed, expected ok\n",
|
||||||
|
drv->name, sc->label);
|
||||||
|
rc = -1; goto done;
|
||||||
|
}
|
||||||
|
snprintf(path, sizeof path, "%s/main", dir);
|
||||||
|
int got = runwait(path);
|
||||||
|
if (got != sc->want_exit) {
|
||||||
|
fprintf(stderr, "839[%s][%s]: exit %d, want %d\n",
|
||||||
|
drv->name, sc->label, got, sc->want_exit);
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
|
||||||
|
(void)runwait(cmd);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
const char *bin = getenv("BIN");
|
||||||
|
if (!bin) bin = "out/bin";
|
||||||
|
char absbin[2048];
|
||||||
|
if (bin[0] != '/') {
|
||||||
|
char cwd[1024];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||||
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||||
|
bin = absbin;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct driver drivers[2];
|
||||||
|
snprintf(drivers[0].path, sizeof drivers[0].path, "%s/ww", bin);
|
||||||
|
drivers[0].name = "cstage";
|
||||||
|
drivers[0].gated = 0;
|
||||||
|
snprintf(drivers[1].path, sizeof drivers[1].path, "%s/ww_ww", bin);
|
||||||
|
drivers[1].name = "wwstage";
|
||||||
|
drivers[1].gated = 1;
|
||||||
|
|
||||||
|
int n = (int)(sizeof scenarios / sizeof scenarios[0]);
|
||||||
|
int total = 0, fail = 0;
|
||||||
|
|
||||||
|
for (int d = 0; d < 2; d++) {
|
||||||
|
if (drivers[d].gated && access(drivers[d].path, X_OK) != 0) {
|
||||||
|
fprintf(stderr, "839: skip %s (no %s)\n",
|
||||||
|
drivers[d].name, drivers[d].path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
total++;
|
||||||
|
if (run_one(&drivers[d], &scenarios[i]) != 0)
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail) {
|
||||||
|
fprintf(stderr, "839 xmod_nominal_typeeqast: %d/%d failed\n",
|
||||||
|
fail, total);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("xmod_nominal_typeeqast: %d/%d ok (cross-module nominal "
|
||||||
|
"accept + reject, both stages)\n", total, total);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -157,8 +157,11 @@ static const struct ent ents[] = {
|
|||||||
.mode = M_WWREJECT, .cite = "#59.13" },
|
.mode = M_WWREJECT, .cite = "#59.13" },
|
||||||
{ .fixture = "lib/fnmatch/fnmatchtest.ww",
|
{ .fixture = "lib/fnmatch/fnmatchtest.ww",
|
||||||
.mode = M_WWREJECT, .cite = "#59.14" },
|
.mode = M_WWREJECT, .cite = "#59.14" },
|
||||||
|
/* #59.15 shlex graduated to M_ID by B-full Layer 1 (#14 nominal
|
||||||
|
* typeeqast): the cross-module bare-vs-qualified variant forward
|
||||||
|
* w6c_ww over-rejected now compiles byte-identically. */
|
||||||
{ .fixture = "lib/shlex/shlextest.ww",
|
{ .fixture = "lib/shlex/shlextest.ww",
|
||||||
.mode = M_WWREJECT, .cite = "#59.15" },
|
.mode = M_ID, .cite = "#59.15 graduated by #14 nominal typeeqast" },
|
||||||
{ .fixture = NULL },
|
{ .fixture = NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user