fix: reject bare import bindings
This commit is contained in:
@@ -237,6 +237,7 @@ fn srcimports(file: *syntax.node, modtag: str, source: i32, name: str) bool = {
|
||||
let u: *syntax.node = file.list;
|
||||
for (u != nil) {
|
||||
if (u.kind == syntax.nkind.N_USE && u.useblank == 0
|
||||
&& !invalidinitimport(u)
|
||||
&& u.sourceid == source) {
|
||||
// Skip self-imports: lib/fmt/fmt_test.ww carries
|
||||
// `use fmt;` while its module tag is also "fmt".
|
||||
@@ -267,8 +268,10 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
||||
let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, name);
|
||||
let builtin: *syntax.sym = nil;
|
||||
if (found != nil) {
|
||||
if (found.decl != nil) { return found; };
|
||||
builtin = found;
|
||||
// A pure SK_USE belongs to one source file. Defer it to the
|
||||
// source-owned path below instead of leaking a sibling qualifier.
|
||||
if (found.decl != nil && found.skind != syntax.skind.SK_USE) { return found; };
|
||||
if (found.decl == nil) { builtin = found; };
|
||||
};
|
||||
// Flat scope installation may coalesce equal qualifiers from distinct
|
||||
// files. A bare mention only locates the marker; the DOT resolution owns
|
||||
@@ -289,6 +292,45 @@ fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
||||
return builtin;
|
||||
};
|
||||
|
||||
// Return the source-local package-name object for a bare identifier without
|
||||
// marking the import used. A closer lexical value wins; a coexisting SK_USE or
|
||||
// promoted use_alias retains package-qualifier identity in the flat scope.
|
||||
fn lookupbareimportbinding(c: *checker, name: str) *syntax.sym = {
|
||||
if (name.len == 0) { return nil; };
|
||||
let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, name);
|
||||
if (found == nil) { return nil; };
|
||||
if (found.skind != syntax.skind.SK_USE && found.use_alias == 0i32) {
|
||||
let use: *syntax.sym = syntax.scopelookupuselocal(found.scope, name);
|
||||
if (use != nil) { found = use; };
|
||||
};
|
||||
if (found.skind == syntax.skind.SK_USE || found.use_alias != 0i32) {
|
||||
if (srcimports(c.file, c.curmod, c.cursource, name)) { return found; };
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
fn bareimportvalueerr(c: *checker, n: *syntax.node) void = {
|
||||
if (n.type_ == c.tc.tyerr: *void) { return; };
|
||||
cerr(n.file); cerr(":");
|
||||
cerr(strconv.i32tos(n.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(n.col, strconv.base.DEC));
|
||||
cerr(": error: use of package "); cerr(n.str);
|
||||
cerr(" not in selector\n");
|
||||
c.errs += 1;
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
|
||||
fn bareimporttypeerr(c: *checker, n: *syntax.node) void = {
|
||||
if (n.type_ == c.tc.tyerr: *void) { return; };
|
||||
cerr(n.file); cerr(":");
|
||||
cerr(strconv.i32tos(n.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(n.col, strconv.base.DEC));
|
||||
cerr(": error: "); cerr(n.str);
|
||||
cerr(" (package name) is not a type\n");
|
||||
c.errs += 1;
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
|
||||
fn lookupvisibletype(c: *checker, name: str) *syntax.sym = {
|
||||
// C resolves intrinsic type names before consulting package symbols.
|
||||
// Select the empty-module seed here so an imported interface cannot
|
||||
@@ -672,6 +714,11 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
if (c.sepmode != 0 && n.type_ != nil) { c.nresolved += 1; return; };
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
if (lookupbareimportbinding(c, nm) != nil) {
|
||||
bareimporttypeerr(c, n);
|
||||
c.nresolved += 1;
|
||||
return;
|
||||
};
|
||||
let s: *syntax.sym = lookupvisibletype(c, nm);
|
||||
let builtin: bool = builtintypename(nm);
|
||||
// `pkg.Type` — strip the last dot prefix and look up
|
||||
@@ -1011,13 +1058,35 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
if (k == syntax.nkind.N_DOT) {
|
||||
// Walk only the base; the .field name is a member, not a
|
||||
// free identifier.
|
||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == syntax.nkind.N_IDENT
|
||||
&& lookupbareimportbinding(c, n.lhs.str) != nil) {
|
||||
// A package receiver is a lookup target, not a value expression.
|
||||
c.nresolved += 1;
|
||||
} else { resolvewalk(c, n.lhs); };
|
||||
};
|
||||
// A.6.0: branch returns early; stamp here so the post-walk
|
||||
// dispatch below sees N_DOT covered.
|
||||
let _t: *syntax.node = exprtype(c, n, nil);
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_STRUCTLIT) {
|
||||
// The literal head is a type designator. A package binding there gets
|
||||
// the type diagnostic, never the ordinary bare-value diagnostic.
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == syntax.nkind.N_IDENT
|
||||
&& lookupbareimportbinding(c, n.lhs.str) != nil) {
|
||||
bareimporttypeerr(c, n.lhs);
|
||||
c.nresolved += 1;
|
||||
} else { resolvewalk(c, n.lhs); };
|
||||
};
|
||||
let sf: *syntax.node = n.list;
|
||||
for (sf != nil) { resolvewalk(c, sf); sf = sf.next; };
|
||||
let _st: *syntax.node = exprtype(c, n, nil);
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_FIELD) {
|
||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||
return;
|
||||
@@ -2098,8 +2167,18 @@ fn evaldefconst(c: *checker, n: *syntax.node, out: *u64, depth: i32) bool = {
|
||||
if (k == syntax.nkind.N_BIN) {
|
||||
let a: u64 = 0u64;
|
||||
let b: u64 = 0u64;
|
||||
if (!evaldefconst(c, n.lhs, &a, depth + 1)) { return false; };
|
||||
if (!evaldefconst(c, n.rhs, &b, depth + 1)) { return false; };
|
||||
if (!evaldefconst(c, n.lhs, &a, depth + 1)) {
|
||||
if (n.lhs != nil && n.lhs.type_ == c.tc.tyerr: *void) {
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (!evaldefconst(c, n.rhs, &b, depth + 1)) {
|
||||
if (n.rhs != nil && n.rhs.type_ == c.tc.tyerr: *void) {
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (foldbinop(n.op, a, b, out)) { return true; };
|
||||
if ((n.op == syntax.tkind.TK_SLASH || n.op == syntax.tkind.TK_PERCENT) && b == 0u64) {
|
||||
deffolderr(c, n, "def value: division by zero");
|
||||
@@ -2112,7 +2191,12 @@ fn evaldefconst(c: *checker, n: *syntax.node, out: *u64, depth: i32) bool = {
|
||||
// foldintliteral already covers unary-over-leaf; this arm
|
||||
// catches unary over a resolved ref, e.g. `-A`.
|
||||
let v: u64 = 0u64;
|
||||
if (!evaldefconst(c, n.lhs, &v, depth + 1)) { return false; };
|
||||
if (!evaldefconst(c, n.lhs, &v, depth + 1)) {
|
||||
if (n.lhs != nil && n.lhs.type_ == c.tc.tyerr: *void) {
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (n.op == syntax.tkind.TK_MINUS) { *out = (-(v: i64)): u64; return true; };
|
||||
if (n.op == syntax.tkind.TK_TILDE) { *out = ~v; return true; };
|
||||
if (n.op == syntax.tkind.TK_PLUS) { *out = v; return true; };
|
||||
@@ -2124,7 +2208,12 @@ fn evaldefconst(c: *checker, n: *syntax.node, out: *u64, depth: i32) bool = {
|
||||
// exprtype's N_CAST arm during resolvewalk). Strip the cast
|
||||
// keeping the value; a narrowing cast that loses it fails loud.
|
||||
let v: u64 = 0u64;
|
||||
if (!evaldefconst(c, n.lhs, &v, depth + 1)) { return false; };
|
||||
if (!evaldefconst(c, n.lhs, &v, depth + 1)) {
|
||||
if (n.lhs != nil && n.lhs.type_ == c.tc.tyerr: *void) {
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
let t: *syntax.tinfo = (n.type_): *syntax.tinfo;
|
||||
if (!defcastfits(t, v)) {
|
||||
deffolderr(c, n, "def value: narrowing cast loses value");
|
||||
@@ -2134,6 +2223,10 @@ fn evaldefconst(c: *checker, n: *syntax.node, out: *u64, depth: i32) bool = {
|
||||
return true;
|
||||
};
|
||||
if (k == syntax.nkind.N_IDENT) {
|
||||
if (lookupbareimportbinding(c, n.str) != nil) {
|
||||
bareimportvalueerr(c, n);
|
||||
return false;
|
||||
};
|
||||
let s: *syntax.sym = lookupvisible(c, n.str);
|
||||
if (s == nil) { return false; };
|
||||
if (s.skind != syntax.skind.SK_DEF) { return false; };
|
||||
@@ -2419,6 +2512,130 @@ fn validatestructfields(c: *checker, n: *syntax.node) void = {
|
||||
// resolvewalk's eager type-decl dispatch, sibling to validatestructfields
|
||||
// (rule-10 symmetric with cstage's once-per-resolve_type), not from the
|
||||
// per-query size/align arms.
|
||||
fn bareimporttypenode(k: syntax.nkind) bool = {
|
||||
return k == syntax.nkind.N_TPTR || k == syntax.nkind.N_TSLICE
|
||||
|| k == syntax.nkind.N_TARRAY
|
||||
|| k == syntax.nkind.N_TFN || k == syntax.nkind.N_TSTRUCT
|
||||
|| k == syntax.nkind.N_TFIELD || k == syntax.nkind.N_TCHAN
|
||||
|| k == syntax.nkind.N_TTUPLE || k == syntax.nkind.N_TTAGGED
|
||||
|| k == syntax.nkind.N_TBANG || k == syntax.nkind.N_TENUM
|
||||
|| k == syntax.nkind.N_TENUMMEMBER || k == syntax.nkind.N_TPARAM;
|
||||
};
|
||||
|
||||
fn rejectbareimporttypes(c: *checker, n: *syntax.node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
if (n.kind == syntax.nkind.N_IDENT || n.kind == syntax.nkind.N_TNAME) {
|
||||
if (lookupbareimportbinding(c, n.str) != nil) {
|
||||
bareimporttypeerr(c, n);
|
||||
return true;
|
||||
};
|
||||
// A qualified type is a legal selector use even when its surrounding
|
||||
// constant-expression shape is independently invalid.
|
||||
if (n.kind == syntax.nkind.N_TNAME && strings.contains(n.str, ".")) {
|
||||
let (head, leaf) = strings.rcut(n.str, ".");
|
||||
if (lookupbareimportbinding(c, head) != nil) {
|
||||
let marked: str = usepathfor(c.file, c.curmod, c.cursource, head);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
let bad: bool = false;
|
||||
if (n.kind == syntax.nkind.N_TARRAY) {
|
||||
if (rejectbareimporttypes(c, n.lhs)) { bad = true; };
|
||||
if (rejectbareimportvalues(c, n.rhs)) { bad = true; };
|
||||
return bad;
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_TENUM) {
|
||||
if (rejectbareimporttypes(c, n.lhs)) { bad = true; };
|
||||
let m: *syntax.node = n.list;
|
||||
for (m != nil) {
|
||||
if (rejectbareimportvalues(c, m.lhs)) { bad = true; };
|
||||
m = m.next;
|
||||
};
|
||||
return bad;
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_TENUMMEMBER) {
|
||||
return rejectbareimportvalues(c, n.lhs);
|
||||
};
|
||||
if (rejectbareimporttypes(c, n.lhs)) { bad = true; };
|
||||
if (rejectbareimporttypes(c, n.rhs)) { bad = true; };
|
||||
if (rejectbareimporttypes(c, n.cond)) { bad = true; };
|
||||
if (rejectbareimporttypes(c, n.body)) { bad = true; };
|
||||
if (rejectbareimporttypes(c, n.els)) { bad = true; };
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) {
|
||||
if (rejectbareimporttypes(c, p)) { bad = true; };
|
||||
p = p.next;
|
||||
};
|
||||
return bad;
|
||||
};
|
||||
|
||||
fn rejectbareimportvalues(c: *checker, n: *syntax.node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
if (bareimporttypenode(n.kind)) { return rejectbareimporttypes(c, n); };
|
||||
if (n.kind == syntax.nkind.N_IDENT || n.kind == syntax.nkind.N_TNAME) {
|
||||
if (lookupbareimportbinding(c, n.str) != nil) {
|
||||
bareimportvalueerr(c, n);
|
||||
return true;
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_TNAME && strings.contains(n.str, ".")) {
|
||||
let (head, leaf) = strings.rcut(n.str, ".");
|
||||
if (lookupbareimportbinding(c, head) != nil) {
|
||||
let marked: str = usepathfor(c.file, c.curmod, c.cursource, head);
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_DOT) {
|
||||
if (n.lhs != nil && n.lhs.kind == syntax.nkind.N_IDENT
|
||||
&& lookupbareimportbinding(c, n.lhs.str) != nil) {
|
||||
let marked: str = usepathfor(c.file, c.curmod, c.cursource, n.lhs.str);
|
||||
return false;
|
||||
};
|
||||
return rejectbareimportvalues(c, n.lhs);
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_CAST || n.kind == syntax.nkind.N_TYPEASSERT
|
||||
|| n.kind == syntax.nkind.N_TYPETEST) {
|
||||
let bad: bool = rejectbareimportvalues(c, n.lhs);
|
||||
if (rejectbareimporttypes(c, n.rhs)) { bad = true; };
|
||||
return bad;
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_STRUCTLIT) {
|
||||
let bad: bool = rejectbareimporttypes(c, n.lhs);
|
||||
let f: *syntax.node = n.list;
|
||||
for (f != nil) {
|
||||
if (rejectbareimportvalues(c, f.lhs)) { bad = true; };
|
||||
f = f.next;
|
||||
};
|
||||
return bad;
|
||||
};
|
||||
if (n.kind == syntax.nkind.N_CALL) {
|
||||
let bad: bool = rejectbareimportvalues(c, n.lhs);
|
||||
let typearg: bool = n.lhs != nil && n.lhs.kind == syntax.nkind.N_IDENT
|
||||
&& (syntax.streq(n.lhs.str, "size") || syntax.streq(n.lhs.str, "align"));
|
||||
let a: *syntax.node = n.list;
|
||||
for (a != nil) {
|
||||
if (typearg) {
|
||||
if (rejectbareimporttypes(c, a)) { bad = true; };
|
||||
} else { if (rejectbareimportvalues(c, a)) { bad = true; }; };
|
||||
a = a.next;
|
||||
};
|
||||
return bad;
|
||||
};
|
||||
let bad: bool = false;
|
||||
if (rejectbareimportvalues(c, n.lhs)) { bad = true; };
|
||||
if (rejectbareimportvalues(c, n.rhs)) { bad = true; };
|
||||
if (rejectbareimportvalues(c, n.cond)) { bad = true; };
|
||||
if (rejectbareimportvalues(c, n.body)) { bad = true; };
|
||||
if (rejectbareimportvalues(c, n.els)) { bad = true; };
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) {
|
||||
if (rejectbareimportvalues(c, p)) { bad = true; };
|
||||
p = p.next;
|
||||
};
|
||||
return bad;
|
||||
};
|
||||
|
||||
fn validateenummembers(c: *checker, n: *syntax.node) void = {
|
||||
// storage type: cstage resolves n->lhs then gates type_isint
|
||||
// (check.c:1004-1009); default storage is i32, always integer.
|
||||
@@ -2464,7 +2681,8 @@ fn validateenummembers(c: *checker, n: *syntax.node) void = {
|
||||
// `until = m` enforces harec's forward-only sibling-ref discipline.
|
||||
if (m.lhs != nil) {
|
||||
let v: u64 = 0u64;
|
||||
if (!enumvalfold(n, m, m.lhs, &v)) {
|
||||
let barebad: bool = rejectbareimportvalues(c, m.lhs);
|
||||
if (!barebad && !enumvalfold(n, m, m.lhs, &v)) {
|
||||
cerr(m.file);
|
||||
cerr(": error: enum value must be a constant integer expression\n");
|
||||
c.errs += 1;
|
||||
@@ -2475,14 +2693,20 @@ fn validateenummembers(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
|
||||
// stampnilexpr — stamp nil-typed nodes in a constant expr subtree to
|
||||
// `ti`. lhs/rhs cover the enum constexpr grammar enumvalfold accepts
|
||||
// (literals, unary, binary, sibling backref); non-nil nodes keep the
|
||||
// `ti`. The specialised fold accepts lhs/rhs, but invalid outer shapes still
|
||||
// pass through the checker and must not leak nil-typed descendants into the
|
||||
// final invariant walk. Non-nil nodes, including package-name errors, keep the
|
||||
// type exprtype already derived.
|
||||
fn stampnilexpr(n: *syntax.node, ti: *syntax.tinfo) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.type_ == nil) { n.type_ = ti: *void; };
|
||||
stampnilexpr(n.lhs, ti);
|
||||
stampnilexpr(n.rhs, ti);
|
||||
stampnilexpr(n.cond, ti);
|
||||
stampnilexpr(n.body, ti);
|
||||
stampnilexpr(n.els, ti);
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) { stampnilexpr(p, ti); p = p.next; };
|
||||
};
|
||||
|
||||
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
||||
@@ -2624,6 +2848,13 @@ fn tinfofornode(c: *checker, n: *syntax.node) *syntax.tinfo = {
|
||||
switch (k) {
|
||||
case syntax.nkind.N_TNAME:
|
||||
let nm: str = n.str;
|
||||
// Synthetic mktname nodes have no file. Only parsed source syntax can
|
||||
// denote a bare package binding; internal primitive/type stamps must not.
|
||||
if (n.file.len != 0 && lookupbareimportbinding(c, nm) != nil) {
|
||||
bareimporttypeerr(c, n);
|
||||
syntax.tinfocachebind(c.tc, n, c.tc.tyerr);
|
||||
return c.tc.tyerr;
|
||||
};
|
||||
if (syntax.streq(nm, "void")) { r = c.tc.tyvoid; };
|
||||
if (syntax.streq(nm, "bool")) { r = c.tc.tybool; };
|
||||
if (syntax.streq(nm, "rune")) { r = c.tc.tyrune; };
|
||||
@@ -2760,13 +2991,16 @@ fn tinfofornode(c: *checker, n: *syntax.node) *syntax.tinfo = {
|
||||
elen = n.rhs.uval;
|
||||
} else {
|
||||
let v: u64 = 0u64;
|
||||
if (evaldefconst(c, n.rhs, &v, 0)) {
|
||||
let barebad: bool = rejectbareimportvalues(c, n.rhs);
|
||||
if (!barebad && evaldefconst(c, n.rhs, &v, 0)) {
|
||||
elen = v;
|
||||
} else {
|
||||
cerr(n.file); cerr(": ");
|
||||
cerr("error: array length must be an integer literal\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
} else { if (!barebad) {
|
||||
if (n.rhs.type_ != c.tc.tyerr: *void) {
|
||||
cerr(n.file); cerr(": ");
|
||||
cerr("error: array length must be an integer literal\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
let sub: *syntax.tinfo = tinfofornode(c, n.lhs);
|
||||
@@ -3352,6 +3586,13 @@ fn binoptype(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
let op: syntax.tkind = e.op;
|
||||
let ltn: *syntax.node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *syntax.node = exprtype(c, e.rhs, nil);
|
||||
// Both operands were checked. Preserve their causal error on the parent and
|
||||
// do not manufacture an operator-type recovery diagnostic or nil stamp.
|
||||
if ((e.lhs != nil && e.lhs.type_ == c.tc.tyerr: *void)
|
||||
|| (e.rhs != nil && e.rhs.type_ == c.tc.tyerr: *void)) {
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
// #120 (B): a binop/compare with one f32 operand lowers an untyped-
|
||||
// float peer to f32 — harec unifies both operands to the operand type
|
||||
// (ref/harec/src/check.c:1347-1348). A comparison's result is bool, so
|
||||
@@ -3485,6 +3726,10 @@ fn binoptype(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
fn unoptype(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
let op: syntax.tkind = e.op;
|
||||
let opt: *syntax.node = exprtype(c, e.lhs, nil);
|
||||
if (e.lhs != nil && e.lhs.type_ == c.tc.tyerr: *void) {
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
// #38/F2 (review item 5): unop operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cunop (cmd/wcc/check.c:1224-1238): unary +/-
|
||||
// want a numeric operand, ~ wants an integer, ! wants a bool. A nil
|
||||
@@ -3820,6 +4065,10 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// resolvewalk visits the dot receiver before its parent. Preserve the
|
||||
// first causal undefined error just as cstage's cached cexpr does.
|
||||
if (e.type_ == c.tc.tyerr: *void) { return nil; };
|
||||
if (lookupbareimportbinding(c, e.str) != nil) {
|
||||
bareimportvalueerr(c, e);
|
||||
return nil;
|
||||
};
|
||||
// #55: bare-leaf value-ident must prefer curmod. Flat-scope
|
||||
// scopelookup bucket-walks and can bind a same-leaf symbol from
|
||||
// the wrong module under a foreign curmod, dragging its decl's
|
||||
@@ -3848,6 +4097,14 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
return nil;
|
||||
};
|
||||
if (s.decl == nil) { return nil; };
|
||||
// Propagate an errored inferred initializer through its binding. Without
|
||||
// this, a later `bad.field` is left nil-typed and asserttyped obscures the
|
||||
// causal checker diagnostic emitted for the initializer.
|
||||
if (s.decl.kind == syntax.nkind.N_LET && s.decl.lhs == nil
|
||||
&& s.decl.rhs != nil && s.decl.rhs.type_ == c.tc.tyerr: *void) {
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
e.refdecl = s.decl;
|
||||
// #34: a bare fn-name rvalue types as its FN TYPE, not its return
|
||||
// type. decl.lhs is the RETURN type for an N_FNDECL, so synthesize
|
||||
@@ -3906,6 +4163,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
};
|
||||
if (k == syntax.nkind.N_BIN) {
|
||||
let tn: *syntax.node = binoptype(c, e);
|
||||
if (e.type_ == c.tc.tyerr: *void) { return nil; };
|
||||
// #59.9: checkisas pre-stamps an enum OR-fold (`(m.A|m.B) as
|
||||
// u32`) TY_ENUM and folds the member N_DOTs to int literals;
|
||||
// this post-order revisit re-derives from those now-untyped
|
||||
@@ -3927,6 +4185,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
};
|
||||
if (k == syntax.nkind.N_UN) {
|
||||
let tn: *syntax.node = unoptype(c, e);
|
||||
if (e.type_ == c.tc.tyerr: *void) { return nil; };
|
||||
e.type_ = tinfofornode(c, tn): *void;
|
||||
return tn;
|
||||
};
|
||||
@@ -3945,6 +4204,14 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
if (e.type_ == c.tc.tyerr: *void) { return nil; };
|
||||
let callee: *syntax.node = e.lhs;
|
||||
if (callee == nil) { return nil; };
|
||||
// Package qualifiers with builtin spelling remain package objects. Reject
|
||||
// before alloc/size/len/etc. can rewrite away the callee identifier.
|
||||
if (callee.kind == syntax.nkind.N_IDENT
|
||||
&& lookupbareimportbinding(c, callee.str) != nil) {
|
||||
bareimportvalueerr(c, callee);
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
// A module-qualified leaf may already have been rejected while the
|
||||
// N_DOT callee was checked on an earlier resolve walk. cstage caches
|
||||
// that failure on the call; mirror its once-only diagnostic here rather
|
||||
@@ -4534,6 +4801,9 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
// Raw unresolved external selectors retain selector semantics;
|
||||
// never reinterpret the package receiver as a bare value below.
|
||||
if (fs == nil && ms.skind == syntax.skind.SK_USE) { return nil; };
|
||||
};
|
||||
// Fold case 2 inner: bare `EnumT.MEMBER` where EnumT
|
||||
// is an SK_TYPE in the flat scope. Mirror cstage
|
||||
@@ -4683,6 +4953,11 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// style anonymous struct lit we don't yet parse — bail.
|
||||
if (e.lhs == nil) { return nil; };
|
||||
if (e.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
if (lookupbareimportbinding(c, e.lhs.str) != nil) {
|
||||
bareimporttypeerr(c, e.lhs);
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
let ms: *syntax.sym = lookupvisible(c, e.lhs.str);
|
||||
if (ms != nil) { if (ms.skind == syntax.skind.SK_TYPE) { if (ms.decl != nil) {
|
||||
let tn: *syntax.node = ms.decl.lhs;
|
||||
@@ -7578,8 +7853,10 @@ fn markimportusesnode(c: *checker, n: *syntax.node, owner: str,
|
||||
source: i32) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.kind == syntax.nkind.N_TNAME) {
|
||||
let (head, leaf) = strings.rcut(n.str, ".");
|
||||
if (head.len > 0) {
|
||||
// strings.rcut returns the whole input as its head on a miss. Require
|
||||
// a real qualifier separator so a bare package TNAME never counts as use.
|
||||
if (strings.contains(n.str, ".")) {
|
||||
let (head, leaf) = strings.rcut(n.str, ".");
|
||||
findusepath(c.file, owner, source, head, true);
|
||||
};
|
||||
} else { if (n.kind == syntax.nkind.N_DOT && n.lhs != nil
|
||||
|
||||
Reference in New Issue
Block a user