compiler: make package exports self-contained
This commit is contained in:
@@ -518,6 +518,21 @@ fn installdecl(c: *checker, file: *syntax.node, d: *syntax.node) void = {
|
||||
// checkinit-synthesized N_TYPEDECL with an empty .file) — user decls
|
||||
// always carry their parsed source file.
|
||||
fn installtop(c: *checker, d: *syntax.node, nm: str, mod: str, k: syntax.skind, kind: str) void = {
|
||||
// A self-contained interface can carry the same origin-owned type/def
|
||||
// fact through both arms of a dependency diamond. In -c package mode,
|
||||
// reuse the first exact compiler-export binding so all references obtain
|
||||
// one nominal tinfo identity. Raw w6c keeps strict duplicate diagnostics.
|
||||
if (c.sepmode != 0 && d.imported != 0 && d.exported != 0 && mod.len > 0) {
|
||||
if (k == syntax.skind.SK_TYPE || k == syntax.skind.SK_DEF) {
|
||||
let same: *syntax.sym = syntax.scopesamekeysym(c.top, nm, mod);
|
||||
if (same != nil) {
|
||||
if (same.skind == k && same.decl != nil && same.decl != d
|
||||
&& same.decl.imported != 0 && same.decl.exported != 0) {
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// #30: a top-level value/type decl whose leaf ALSO names an imported
|
||||
// module PROMOTES that same-leaf SK_USE in place — one correctly-kinded
|
||||
// sym carrying use_alias=1, so bare refs (call/structlit/var) resolve to
|
||||
@@ -638,6 +653,17 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
// `use IDENT;` — name is a module label, not a free ident.
|
||||
if (k == syntax.nkind.N_USE) { return; };
|
||||
|
||||
// `_ = rhs` is a discard assignment. The parser represents `_` as an
|
||||
// empty identifier; it is an lvalue marker, not an unresolved package
|
||||
// value. Evaluate the rhs for its normal checks and never send the blank
|
||||
// lhs through ordinary identifier resolution (cstage N_ASSIGN twin).
|
||||
if (k == syntax.nkind.N_ASSIGN && n.lhs != nil) {
|
||||
if (n.lhs.kind == syntax.nkind.N_IDENT && n.lhs.str.len == 0) {
|
||||
if (n.rhs != nil) { resolvewalk(c, n.rhs); };
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_IDENT) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
@@ -663,6 +689,12 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_TNAME) {
|
||||
// A declaration-owned type reference may be revisited when its AST
|
||||
// is propagated as an inferred consumer type. Its canonical tinfo was
|
||||
// resolved while c.curmod named the declaring package; retain that
|
||||
// compiler fact instead of reinterpreting the spelling as consumer
|
||||
// source (which would incorrectly require a transitive import).
|
||||
if (c.sepmode != 0 && n.type_ != nil) { c.nresolved += 1; return; };
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *syntax.sym = lookupvisibletype(c, nm);
|
||||
@@ -695,7 +727,7 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
};
|
||||
if (s == nil && !builtin) {
|
||||
if (syntax.scopelookup(c.cur, nm) != nil) {
|
||||
if (c.sepmode != 0 || syntax.scopelookup(c.cur, nm) != nil) {
|
||||
cerr(n.file); cerr(":");
|
||||
cerr(strconv.i32tos(n.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(n.col, strconv.base.DEC));
|
||||
@@ -981,7 +1013,15 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
};
|
||||
let l: *syntax.node = n.list;
|
||||
for (l != nil) { resolvewalk(c, l); l = l.next; };
|
||||
for (l != nil) {
|
||||
// A blank multi-assignment target is the same discard marker as
|
||||
// `_ = rhs`, not a value lookup. stamptuplebinds below still
|
||||
// stamps its slot from the rhs tuple for downstream invariants.
|
||||
if (l.kind != syntax.nkind.N_IDENT || l.str.len != 0) {
|
||||
resolvewalk(c, l);
|
||||
};
|
||||
l = l.next;
|
||||
};
|
||||
stamptuplebinds(c, n.list, pt, false, "");
|
||||
return;
|
||||
};
|
||||
@@ -1006,6 +1046,13 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
return;
|
||||
};
|
||||
|
||||
// Enum member value identifiers name prior siblings in the same enum;
|
||||
// they are not package-scope values. The enum-specific evaluator below
|
||||
// validates and folds the complete member list, then stampenumvals gives
|
||||
// every expression node its storage type. Do not feed those identifiers
|
||||
// through the ordinary package/local expression lookup.
|
||||
if (k == syntax.nkind.N_TENUMMEMBER) { return; };
|
||||
|
||||
// Walk children (mirroring ast.ww's printer descent order).
|
||||
if (n.attr != nil) { resolvewalk(c, n.attr); };
|
||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||
@@ -1724,6 +1771,22 @@ fn astunsized(c: *checker, t: *syntax.node) bool = {
|
||||
// call use it). The #241 `yield <binder>` fallback (the dominant
|
||||
// match-bind-then-yield idiom, Hare's parseint `case let t => yield t`)
|
||||
// stays, now resolving btype to a tinfo.
|
||||
fn exprusesname(n: *syntax.node, name: str) bool = {
|
||||
if (n == nil || name.len == 0) { return false; };
|
||||
if (n.kind == syntax.nkind.N_IDENT && syntax.streq(n.str, name)) {
|
||||
return true;
|
||||
};
|
||||
if (exprusesname(n.lhs, name) || exprusesname(n.rhs, name)
|
||||
|| exprusesname(n.cond, name) || exprusesname(n.body, name)
|
||||
|| exprusesname(n.els, name)) { return true; };
|
||||
let e: *syntax.node = n.list;
|
||||
for (e != nil) {
|
||||
if (exprusesname(e, name)) { return true; };
|
||||
e = e.next;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn matchyieldtype(c: *checker, body: *syntax.node, bname: str, btype: *syntax.node,
|
||||
nodeout: **syntax.node) *syntax.tinfo = {
|
||||
if (body == nil) { return nil; };
|
||||
@@ -1746,6 +1809,19 @@ fn matchyieldtype(c: *checker, body: *syntax.node, bname: str, btype: *syntax.no
|
||||
};
|
||||
return body.lhs.type_: *syntax.tinfo;
|
||||
};
|
||||
// The pre-walk runs before the N_MCASE scope exists. Resolve the
|
||||
// declared arm binder from the case node directly; calling exprtype
|
||||
// first would misclassify this valid local as an undefined package
|
||||
// name now that -c rejects genuinely absent transitive values.
|
||||
if (body.lhs.kind == syntax.nkind.N_IDENT && bname.len > 0
|
||||
&& syntax.streq(body.lhs.str, bname)) {
|
||||
*nodeout = btype;
|
||||
return tinfofornode(c, btype);
|
||||
};
|
||||
// A derived expression such as `yield sub.len` also depends on the
|
||||
// arm scope. The pre-walk cannot type it yet; defer to the normal
|
||||
// in-scope N_MCASE walk, whose cached type_ the post-walk reads.
|
||||
if (exprusesname(body.lhs, bname)) { return nil; };
|
||||
let t: *syntax.node = exprtype(c, body.lhs, nil);
|
||||
if (t != nil) {
|
||||
*nodeout = t;
|
||||
@@ -2602,7 +2678,14 @@ fn tinfofornode(c: *checker, n: *syntax.node) *syntax.tinfo = {
|
||||
let named: *syntax.tinfo = syntax.typenamed(s.name, nil);
|
||||
s.type_ = named;
|
||||
named.resolving = 1;
|
||||
// Resolve a named declaration's body in the package that
|
||||
// owns it. A direct dependency's fact may be demanded while
|
||||
// walking a consumer-owned type; keeping the consumer module
|
||||
// here can bind bare names in the fact to the wrong package.
|
||||
let savedmod: str = c.curmod;
|
||||
c.curmod = s.mod;
|
||||
let under: *syntax.tinfo = tinfofornode(c, body);
|
||||
c.curmod = savedmod;
|
||||
// #62/#69: alias-root cycle (`type a = b;
|
||||
// type b = a` / `type a = a`) — checked
|
||||
// BEFORE clearing the flag so self-aliases
|
||||
@@ -3733,11 +3816,15 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// L2439, #53 at L688. Tracked in the cluster note at L685-687.
|
||||
let s: *syntax.sym = lookupvisible(c, e.str);
|
||||
if (s == nil) {
|
||||
// abort/assert are dedicated compiler builtins and intentionally
|
||||
// have no callee symbol; the surrounding N_CALL arm validates and
|
||||
// stamps them. Package-mode undefined checks must not preempt it.
|
||||
if (isassertfam(c, e)) { return nil; };
|
||||
// A same-named flattened symbol that fails lookupvisible is a
|
||||
// transitive implementation fact, not an unresolved external.
|
||||
// Diagnose it like cstage's N_IDENT path and stamp tyerr so
|
||||
// later call checking does not obscure the causal error.
|
||||
if (syntax.scopelookup(c.cur, e.str) != nil) {
|
||||
if (c.sepmode != 0 || syntax.scopelookup(c.cur, e.str) != nil) {
|
||||
cerr(e.file); cerr(":");
|
||||
cerr(strconv.i32tos(e.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(e.col, strconv.base.DEC));
|
||||
|
||||
Reference in New Issue
Block a user