selfhost/cmd/wcc/check: resolve cross-module type refs in is/as
resolvealias only walked N_TNAME with unqualified names; cross-module
type aliases (parser emits them as one TNAME with str="pkg.alias"
via parse.ww:258-265 joindotted) returned the AST verbatim, and
checkisas at :1098-1108 then flagged "operand is not a tagged union"
on every `match (x: lib.maybe) { ... }` shape.
resolvealias now recognizes the joined-dotted form: split on the
rightmost '.', scopelookupinmodule(c.cur, head, leaf), recurse if
the body is itself an alias. Mirrors cstage resolve_typename at
cmd/wcc/check.c:74-83.
scruttype gains an N_DOT scrutinee arm — `match (pkg.var) { ... }`
or `pkg.var is T` now resolve through scopelookupinmodule. Module
head gating distinguishes top-level imported sym refs from struct
field access (both spell as N_DOT in the AST).
Standalone correctness fix; surfaces no current fixture failure
(those were enum-int reinterprets, tracked separately as #52). Sets
up #50 to wire checkfile into the wwstage cgen pipeline once #52
also lands.
This commit is contained in:
@@ -7423,7 +7423,32 @@ fn resolvealias(c: *checker, n: *node) *node = {
|
||||
let cur: *node = n;
|
||||
for (cur != nil) {
|
||||
if (cur.kind != nkind.N_TNAME) { return cur; };
|
||||
let s: *sym = scopelookup(c.cur, cur.str);
|
||||
let nm: str = cur.str;
|
||||
// #51: pkg.alias type refs land here as a single TNAME whose
|
||||
// str is the joined form (lib/ww/parse/parse.ww:258-265 in
|
||||
// parsetype). Split on the rightmost '.' and bind the leaf in
|
||||
// the head module's scope. Mirrors cstage resolve_typename
|
||||
// cmd/wcc/check.c:74-83 strrchr branch — without this the
|
||||
// raw `os.oserror` lookup misses and checkisas false-positives
|
||||
// every cross-module tagged scrutinee.
|
||||
let dotidx: i32 = -1;
|
||||
let i: i32 = 0;
|
||||
for (i < nm.len) {
|
||||
if (nm[i] == 46u8) { dotidx = i; };
|
||||
i += 1;
|
||||
};
|
||||
let s: *sym = nil;
|
||||
if (dotidx >= 0) {
|
||||
let head: str;
|
||||
head.ptr = nm.ptr;
|
||||
head.len = dotidx;
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + ((dotidx + 1): u64);
|
||||
leaf.len = nm.len - dotidx - 1;
|
||||
s = scopelookupinmodule(c.cur, head, leaf);
|
||||
} else {
|
||||
s = scopelookup(c.cur, nm);
|
||||
};
|
||||
if (s == nil) { return cur; };
|
||||
if (s.skind != skind.SK_TYPE) { return cur; };
|
||||
let body: *node = nil;
|
||||
@@ -7504,7 +7529,7 @@ fn iserrvariant(c: *checker, tagged: *node, v: *node) bool = {
|
||||
|
||||
// scruttype — resolve the type expression for a match's
|
||||
// scrutinee. Handles nkind.N_IDENT (look up local/param's declared
|
||||
// type) and nkind.N_DOT (struct-field access). Returns nil if we
|
||||
// type) and nkind.N_DOT (module-qualified ref). Returns nil if we
|
||||
// can't statically determine the type. Used by exhaustiveness.
|
||||
fn scruttype(c: *checker, e: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
@@ -7515,6 +7540,20 @@ fn scruttype(c: *checker, e: *node) *node = {
|
||||
// For nkind.N_LET / nkind.N_PARAM: declared type is decl.lhs.
|
||||
return s.decl.lhs;
|
||||
};
|
||||
// #51: `match (pkg.var)` / `pkg.var is T` — module-qualified ref.
|
||||
// lhs is N_IDENT (module bareword), str is the leaf. Bind via
|
||||
// scopelookupinmodule so the declared type carries the same
|
||||
// shape resolvealias' dotted-name branch now consumes. Falls
|
||||
// silently to nil when lhs is a value (struct-field access) —
|
||||
// the rest of the lenient-check contract.
|
||||
if (e.kind == nkind.N_DOT) {
|
||||
if (e.lhs == nil) { return nil; };
|
||||
if (e.lhs.kind != nkind.N_IDENT) { return nil; };
|
||||
let s: *sym = scopelookupinmodule(c.cur, e.lhs.str, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
|
||||
@@ -429,7 +429,32 @@ fn resolvealias(c: *checker, n: *node) *node = {
|
||||
let cur: *node = n;
|
||||
for (cur != nil) {
|
||||
if (cur.kind != nkind.N_TNAME) { return cur; };
|
||||
let s: *sym = scopelookup(c.cur, cur.str);
|
||||
let nm: str = cur.str;
|
||||
// #51: pkg.alias type refs land here as a single TNAME whose
|
||||
// str is the joined form (lib/ww/parse/parse.ww:258-265 in
|
||||
// parsetype). Split on the rightmost '.' and bind the leaf in
|
||||
// the head module's scope. Mirrors cstage resolve_typename
|
||||
// cmd/wcc/check.c:74-83 strrchr branch — without this the
|
||||
// raw `os.oserror` lookup misses and checkisas false-positives
|
||||
// every cross-module tagged scrutinee.
|
||||
let dotidx: i32 = -1;
|
||||
let i: i32 = 0;
|
||||
for (i < nm.len) {
|
||||
if (nm[i] == 46u8) { dotidx = i; };
|
||||
i += 1;
|
||||
};
|
||||
let s: *sym = nil;
|
||||
if (dotidx >= 0) {
|
||||
let head: str;
|
||||
head.ptr = nm.ptr;
|
||||
head.len = dotidx;
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + ((dotidx + 1): u64);
|
||||
leaf.len = nm.len - dotidx - 1;
|
||||
s = scopelookupinmodule(c.cur, head, leaf);
|
||||
} else {
|
||||
s = scopelookup(c.cur, nm);
|
||||
};
|
||||
if (s == nil) { return cur; };
|
||||
if (s.skind != skind.SK_TYPE) { return cur; };
|
||||
let body: *node = nil;
|
||||
@@ -510,7 +535,7 @@ fn iserrvariant(c: *checker, tagged: *node, v: *node) bool = {
|
||||
|
||||
// scruttype — resolve the type expression for a match's
|
||||
// scrutinee. Handles nkind.N_IDENT (look up local/param's declared
|
||||
// type) and nkind.N_DOT (struct-field access). Returns nil if we
|
||||
// type) and nkind.N_DOT (module-qualified ref). Returns nil if we
|
||||
// can't statically determine the type. Used by exhaustiveness.
|
||||
fn scruttype(c: *checker, e: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
@@ -521,6 +546,20 @@ fn scruttype(c: *checker, e: *node) *node = {
|
||||
// For nkind.N_LET / nkind.N_PARAM: declared type is decl.lhs.
|
||||
return s.decl.lhs;
|
||||
};
|
||||
// #51: `match (pkg.var)` / `pkg.var is T` — module-qualified ref.
|
||||
// lhs is N_IDENT (module bareword), str is the leaf. Bind via
|
||||
// scopelookupinmodule so the declared type carries the same
|
||||
// shape resolvealias' dotted-name branch now consumes. Falls
|
||||
// silently to nil when lhs is a value (struct-field access) —
|
||||
// the rest of the lenient-check contract.
|
||||
if (e.kind == nkind.N_DOT) {
|
||||
if (e.lhs == nil) { return nil; };
|
||||
if (e.lhs.kind != nkind.N_IDENT) { return nil; };
|
||||
let s: *sym = scopelookupinmodule(c.cur, e.lhs.str, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
|
||||
@@ -7423,7 +7423,32 @@ fn resolvealias(c: *checker, n: *node) *node = {
|
||||
let cur: *node = n;
|
||||
for (cur != nil) {
|
||||
if (cur.kind != nkind.N_TNAME) { return cur; };
|
||||
let s: *sym = scopelookup(c.cur, cur.str);
|
||||
let nm: str = cur.str;
|
||||
// #51: pkg.alias type refs land here as a single TNAME whose
|
||||
// str is the joined form (lib/ww/parse/parse.ww:258-265 in
|
||||
// parsetype). Split on the rightmost '.' and bind the leaf in
|
||||
// the head module's scope. Mirrors cstage resolve_typename
|
||||
// cmd/wcc/check.c:74-83 strrchr branch — without this the
|
||||
// raw `os.oserror` lookup misses and checkisas false-positives
|
||||
// every cross-module tagged scrutinee.
|
||||
let dotidx: i32 = -1;
|
||||
let i: i32 = 0;
|
||||
for (i < nm.len) {
|
||||
if (nm[i] == 46u8) { dotidx = i; };
|
||||
i += 1;
|
||||
};
|
||||
let s: *sym = nil;
|
||||
if (dotidx >= 0) {
|
||||
let head: str;
|
||||
head.ptr = nm.ptr;
|
||||
head.len = dotidx;
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + ((dotidx + 1): u64);
|
||||
leaf.len = nm.len - dotidx - 1;
|
||||
s = scopelookupinmodule(c.cur, head, leaf);
|
||||
} else {
|
||||
s = scopelookup(c.cur, nm);
|
||||
};
|
||||
if (s == nil) { return cur; };
|
||||
if (s.skind != skind.SK_TYPE) { return cur; };
|
||||
let body: *node = nil;
|
||||
@@ -7504,7 +7529,7 @@ fn iserrvariant(c: *checker, tagged: *node, v: *node) bool = {
|
||||
|
||||
// scruttype — resolve the type expression for a match's
|
||||
// scrutinee. Handles nkind.N_IDENT (look up local/param's declared
|
||||
// type) and nkind.N_DOT (struct-field access). Returns nil if we
|
||||
// type) and nkind.N_DOT (module-qualified ref). Returns nil if we
|
||||
// can't statically determine the type. Used by exhaustiveness.
|
||||
fn scruttype(c: *checker, e: *node) *node = {
|
||||
if (e == nil) { return nil; };
|
||||
@@ -7515,6 +7540,20 @@ fn scruttype(c: *checker, e: *node) *node = {
|
||||
// For nkind.N_LET / nkind.N_PARAM: declared type is decl.lhs.
|
||||
return s.decl.lhs;
|
||||
};
|
||||
// #51: `match (pkg.var)` / `pkg.var is T` — module-qualified ref.
|
||||
// lhs is N_IDENT (module bareword), str is the leaf. Bind via
|
||||
// scopelookupinmodule so the declared type carries the same
|
||||
// shape resolvealias' dotted-name branch now consumes. Falls
|
||||
// silently to nil when lhs is a value (struct-field access) —
|
||||
// the rest of the lenient-check contract.
|
||||
if (e.kind == nkind.N_DOT) {
|
||||
if (e.lhs == nil) { return nil; };
|
||||
if (e.lhs.kind != nkind.N_IDENT) { return nil; };
|
||||
let s: *sym = scopelookupinmodule(c.cur, e.lhs.str, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user