selfhost/cmd/wcc: isassignable arm 6 → recursive (cascade Commit 1, #39)
The concrete→tagged arm walked variants with typeeqast-only, rejecting widenings that aren't strict surface-eq: NAMED-aliased variants, nested tagged inside a variant, and concrete → variant after the wrap-induced exprtype reshape that Commit 3 (#41, variadic wrap re-land) introduces. Replace the walk with a recursive isassignable call per variant, mirroring harec tagged_select_subtype (ref/harec/src/types.c:702-739, recursive type_is_assignable at :718; invoked from the TAGGED arm at :1110-1112) and the byte-id cstage precedent at cmd/wcc/type.c:298-299. The recursive call's leading typeeqast (check.ww:2257) preserves the #55 surface-nominal fast path; the #57 bare-vs-qualified TNAME residual is unchanged. No new test: the new widen path is dormant pre-Commit-3; existing tagged-union tests cover the surface-eq fast path. Commit 3's fmttest exercise will exercise the recursive widen for free. Closes 39 cascade errors that fire once #41 lands.
This commit is contained in:
@@ -9275,30 +9275,21 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Tagged-union variant inclusion: src is one of dst's variants.
|
// Tagged-union variant inclusion: src is one of dst's variants.
|
||||||
|
// Recursive isassignable mirrors cstage type_assignable
|
||||||
|
// (cmd/wcc/type.c:298-299) and harec tagged_select_subtype's
|
||||||
|
// recursive type_is_assignable call (ref/harec/src/types.c:702-739,
|
||||||
|
// :718; invoked from the TAGGED arm at :1110-1112). #39 cascade:
|
||||||
|
// the prior typeeqast-only walk rejected widenings that aren't
|
||||||
|
// strict surface-eq (NAMED-aliased variants, nested tagged inside
|
||||||
|
// a variant, concrete → variant after the wrap-induced exprtype
|
||||||
|
// reshape). #55 surface-nominal fast path is preserved by the
|
||||||
|
// recursive call's leading typeeqast (line 2257). #57 bare-vs-
|
||||||
|
// qualified TNAME residual unchanged.
|
||||||
if (du.kind == nkind.N_TTAGGED && su.kind != nkind.N_TTAGGED) {
|
if (du.kind == nkind.N_TTAGGED && su.kind != nkind.N_TTAGGED) {
|
||||||
// #55: nominal-first compare. Cstage variant_match
|
|
||||||
// (cmd/wcc/check.c:90-100) takes NAMED types as
|
|
||||||
// pointer-identical, so two `err`s match before bodies
|
|
||||||
// are resolved. Wwstage's typeeqast already does
|
|
||||||
// string-nominal on N_TNAME, but pre-fix this branch
|
|
||||||
// resolvealias-d both v and src to their bodies (e.g.
|
|
||||||
// N_TSTRUCT), and typeeqast's conservative struct arm
|
|
||||||
// returned false — `return e;` inside `fn f() (void | err)`
|
|
||||||
// got flagged. Compare surface forms first; fall through
|
|
||||||
// to resolved compare only when the surface mismatches
|
|
||||||
// (covers structurally-anonymous variant cases that
|
|
||||||
// resolvealias actually disambiguates). Residual: bare-vs-
|
|
||||||
// qualified TNAME (`(void | modM.err)` variant vs bare `err`
|
|
||||||
// inside modM) still misses both arms — filed as #57.
|
|
||||||
let srcraw: *node = unwrapbang(src);
|
|
||||||
let v: *node = du.list;
|
let v: *node = du.list;
|
||||||
for (v != nil) {
|
for (v != nil) {
|
||||||
let vraw: *node = unwrapbang(v);
|
let innerconf: bool = false;
|
||||||
if (typeeqast(vraw, srcraw)) { return true; };
|
if (isassignable(c, v, src, &innerconf)) { return true; };
|
||||||
let vu: *node = resolvealias(c, vraw);
|
|
||||||
if (vu != nil) {
|
|
||||||
if (typeeqast(vu, su)) { return true; };
|
|
||||||
};
|
|
||||||
v = v.next;
|
v = v.next;
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -2311,30 +2311,21 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Tagged-union variant inclusion: src is one of dst's variants.
|
// Tagged-union variant inclusion: src is one of dst's variants.
|
||||||
|
// Recursive isassignable mirrors cstage type_assignable
|
||||||
|
// (cmd/wcc/type.c:298-299) and harec tagged_select_subtype's
|
||||||
|
// recursive type_is_assignable call (ref/harec/src/types.c:702-739,
|
||||||
|
// :718; invoked from the TAGGED arm at :1110-1112). #39 cascade:
|
||||||
|
// the prior typeeqast-only walk rejected widenings that aren't
|
||||||
|
// strict surface-eq (NAMED-aliased variants, nested tagged inside
|
||||||
|
// a variant, concrete → variant after the wrap-induced exprtype
|
||||||
|
// reshape). #55 surface-nominal fast path is preserved by the
|
||||||
|
// recursive call's leading typeeqast (line 2257). #57 bare-vs-
|
||||||
|
// qualified TNAME residual unchanged.
|
||||||
if (du.kind == nkind.N_TTAGGED && su.kind != nkind.N_TTAGGED) {
|
if (du.kind == nkind.N_TTAGGED && su.kind != nkind.N_TTAGGED) {
|
||||||
// #55: nominal-first compare. Cstage variant_match
|
|
||||||
// (cmd/wcc/check.c:90-100) takes NAMED types as
|
|
||||||
// pointer-identical, so two `err`s match before bodies
|
|
||||||
// are resolved. Wwstage's typeeqast already does
|
|
||||||
// string-nominal on N_TNAME, but pre-fix this branch
|
|
||||||
// resolvealias-d both v and src to their bodies (e.g.
|
|
||||||
// N_TSTRUCT), and typeeqast's conservative struct arm
|
|
||||||
// returned false — `return e;` inside `fn f() (void | err)`
|
|
||||||
// got flagged. Compare surface forms first; fall through
|
|
||||||
// to resolved compare only when the surface mismatches
|
|
||||||
// (covers structurally-anonymous variant cases that
|
|
||||||
// resolvealias actually disambiguates). Residual: bare-vs-
|
|
||||||
// qualified TNAME (`(void | modM.err)` variant vs bare `err`
|
|
||||||
// inside modM) still misses both arms — filed as #57.
|
|
||||||
let srcraw: *node = unwrapbang(src);
|
|
||||||
let v: *node = du.list;
|
let v: *node = du.list;
|
||||||
for (v != nil) {
|
for (v != nil) {
|
||||||
let vraw: *node = unwrapbang(v);
|
let innerconf: bool = false;
|
||||||
if (typeeqast(vraw, srcraw)) { return true; };
|
if (isassignable(c, v, src, &innerconf)) { return true; };
|
||||||
let vu: *node = resolvealias(c, vraw);
|
|
||||||
if (vu != nil) {
|
|
||||||
if (typeeqast(vu, su)) { return true; };
|
|
||||||
};
|
|
||||||
v = v.next;
|
v = v.next;
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -9275,30 +9275,21 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
// Tagged-union variant inclusion: src is one of dst's variants.
|
// Tagged-union variant inclusion: src is one of dst's variants.
|
||||||
|
// Recursive isassignable mirrors cstage type_assignable
|
||||||
|
// (cmd/wcc/type.c:298-299) and harec tagged_select_subtype's
|
||||||
|
// recursive type_is_assignable call (ref/harec/src/types.c:702-739,
|
||||||
|
// :718; invoked from the TAGGED arm at :1110-1112). #39 cascade:
|
||||||
|
// the prior typeeqast-only walk rejected widenings that aren't
|
||||||
|
// strict surface-eq (NAMED-aliased variants, nested tagged inside
|
||||||
|
// a variant, concrete → variant after the wrap-induced exprtype
|
||||||
|
// reshape). #55 surface-nominal fast path is preserved by the
|
||||||
|
// recursive call's leading typeeqast (line 2257). #57 bare-vs-
|
||||||
|
// qualified TNAME residual unchanged.
|
||||||
if (du.kind == nkind.N_TTAGGED && su.kind != nkind.N_TTAGGED) {
|
if (du.kind == nkind.N_TTAGGED && su.kind != nkind.N_TTAGGED) {
|
||||||
// #55: nominal-first compare. Cstage variant_match
|
|
||||||
// (cmd/wcc/check.c:90-100) takes NAMED types as
|
|
||||||
// pointer-identical, so two `err`s match before bodies
|
|
||||||
// are resolved. Wwstage's typeeqast already does
|
|
||||||
// string-nominal on N_TNAME, but pre-fix this branch
|
|
||||||
// resolvealias-d both v and src to their bodies (e.g.
|
|
||||||
// N_TSTRUCT), and typeeqast's conservative struct arm
|
|
||||||
// returned false — `return e;` inside `fn f() (void | err)`
|
|
||||||
// got flagged. Compare surface forms first; fall through
|
|
||||||
// to resolved compare only when the surface mismatches
|
|
||||||
// (covers structurally-anonymous variant cases that
|
|
||||||
// resolvealias actually disambiguates). Residual: bare-vs-
|
|
||||||
// qualified TNAME (`(void | modM.err)` variant vs bare `err`
|
|
||||||
// inside modM) still misses both arms — filed as #57.
|
|
||||||
let srcraw: *node = unwrapbang(src);
|
|
||||||
let v: *node = du.list;
|
let v: *node = du.list;
|
||||||
for (v != nil) {
|
for (v != nil) {
|
||||||
let vraw: *node = unwrapbang(v);
|
let innerconf: bool = false;
|
||||||
if (typeeqast(vraw, srcraw)) { return true; };
|
if (isassignable(c, v, src, &innerconf)) { return true; };
|
||||||
let vu: *node = resolvealias(c, vraw);
|
|
||||||
if (vu != nil) {
|
|
||||||
if (typeeqast(vu, su)) { return true; };
|
|
||||||
};
|
|
||||||
v = v.next;
|
v = v.next;
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user