compiler: load imports and enforce package exports
This commit is contained in:
@@ -5,6 +5,7 @@ package wcc;
|
||||
import os;
|
||||
import syntax;
|
||||
import strconv;
|
||||
import strings;
|
||||
|
||||
type checker = struct {
|
||||
tc: *syntax.tctx,
|
||||
@@ -19,6 +20,10 @@ type checker = struct {
|
||||
// twin (c->matcharms)
|
||||
istest: i32, // #15: `w6c_ww -T` — collect @test fns +
|
||||
// synth the entry; loud-reject a user main.
|
||||
sepmode: i32, // -c package compilation: imported interfaces are
|
||||
// present, so absent members are hard export errors.
|
||||
synthtestrun: *syntax.node, // exact generated test.run DOT; its
|
||||
// unresolved external hook is intentional
|
||||
verbose: i32, // when non-zero, log each unresolved name
|
||||
fnret: *syntax.node, // enclosing fn's return type AST (for `?`)
|
||||
curmod: str, // importing-module bareword for the decl
|
||||
@@ -164,17 +169,31 @@ fn declmod(file: *syntax.node, d: *syntax.node) str = {
|
||||
// to the full dotted import path it binds (`encoding.utf8`), for the
|
||||
// module-qualified resolution and codegen hint (M1 #22). Single-level
|
||||
// packages have usepath == alias so the result is unchanged. The
|
||||
// current tree has one occurrence per leaf, so the map is unambiguous.
|
||||
fn usepathfor(file: *syntax.node, alias: str) str = {
|
||||
// Only an import owned by the referencing package is visible; a matching
|
||||
// alias carried by a transitive interface is deliberately ignored.
|
||||
fn usepathfor(file: *syntax.node, modtag: str, alias: str) str = {
|
||||
let empty: str;
|
||||
if (file == nil) { return empty; };
|
||||
if (alias.len == 0) { return empty; };
|
||||
if (modtag.len != 0) {
|
||||
let (prefix, suffix) = strings.rcut(modtag, ".");
|
||||
let leaf: str = suffix;
|
||||
if (leaf.len == 0) { leaf = modtag; };
|
||||
if (syntax.streq(alias, leaf)) { return modtag; };
|
||||
};
|
||||
let u: *syntax.node = file.list;
|
||||
for (u != nil) {
|
||||
if (u.kind == syntax.nkind.N_USE) {
|
||||
if (syntax.streq(u.str, alias)) {
|
||||
if (u.usepath.len != 0) { return u.usepath; };
|
||||
return u.str;
|
||||
let um: str = declmod(file, u);
|
||||
let same: bool = false;
|
||||
if (modtag.len == 0) {
|
||||
if (um.len == 0) { same = true; };
|
||||
} else { if (syntax.streq(um, modtag)) { same = true; }; };
|
||||
if (same) {
|
||||
if (u.usepath.len != 0) { return u.usepath; };
|
||||
return u.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
u = u.next;
|
||||
@@ -182,12 +201,10 @@ fn usepathfor(file: *syntax.node, alias: str) str = {
|
||||
return empty;
|
||||
};
|
||||
|
||||
// modkeyfor — usepathfor with leaf-alias fallback: the module key for a
|
||||
// path-keyed scopelookupinmodule given the alias the user wrote (M1 #22).
|
||||
// modkeyfor — the module key for a directly imported alias. Empty means
|
||||
// the referencing package did not itself declare that import.
|
||||
fn modkeyfor(c: *checker, alias: str) str = {
|
||||
let mk: str = usepathfor(c.file, alias);
|
||||
if (mk.len == 0) { return alias; };
|
||||
return mk;
|
||||
return usepathfor(c.file, c.curmod, alias);
|
||||
};
|
||||
|
||||
// srcimports — does the source file that contributed decl-module
|
||||
@@ -225,6 +242,132 @@ fn srcimports(file: *syntax.node, modtag: str, name: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Bare imported declarations remain WW source syntax, but only across a
|
||||
// direct edge. The syntax scope helpers resolve lexical locals, builtins and
|
||||
// same-package declarations first; this pass admits a flattened interface
|
||||
// symbol iff the referencing source package itself imported its module path.
|
||||
fn directmodvisible(c: *checker, mod: str) bool = {
|
||||
if (mod.len == 0) { return false; };
|
||||
let (prefix, suffix) = strings.rcut(mod, ".");
|
||||
let alias: str = suffix;
|
||||
if (alias.len == 0) { alias = mod; };
|
||||
let path: str = usepathfor(c.file, c.curmod, alias);
|
||||
return path.len != 0 && syntax.streq(path, mod);
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
// Flat scope installation coalesces same-leaf N_USE entries. The
|
||||
// source-owned alias map, not the retained marker's mod field, decides
|
||||
// whether this package can use the qualifier.
|
||||
if (usepathfor(c.file, c.curmod, name).len != 0) {
|
||||
let q: *syntax.scope = c.cur;
|
||||
for (q != nil) {
|
||||
let u: *syntax.sym = q.first;
|
||||
for (u != nil) {
|
||||
if (syntax.streq(u.name, name)
|
||||
&& (u.skind == syntax.skind.SK_USE
|
||||
|| u.use_alias != 0i32)) { return u; };
|
||||
u = u.snext;
|
||||
};
|
||||
q = q.parent;
|
||||
};
|
||||
};
|
||||
let p: *syntax.scope = c.cur;
|
||||
for (p != nil) {
|
||||
let b: *syntax.sym = p.first;
|
||||
for (b != nil) {
|
||||
if (syntax.streq(b.name, name) && directmodvisible(c, b.mod)) {
|
||||
return b;
|
||||
};
|
||||
b = b.snext;
|
||||
};
|
||||
p = p.parent;
|
||||
};
|
||||
return builtin;
|
||||
};
|
||||
|
||||
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
|
||||
// redefine a builtin for wwstage; size/opaque have no seed and remain
|
||||
// handled directly by tinfofornode.
|
||||
if (builtintypename(name)) {
|
||||
let empty: str;
|
||||
return syntax.scopelookuptype(c.cur, empty, name);
|
||||
};
|
||||
let found: *syntax.sym = syntax.scopelookuptype(c.cur, c.curmod, name);
|
||||
if (found != nil) { return found; };
|
||||
let p: *syntax.scope = c.cur;
|
||||
for (p != nil) {
|
||||
let b: *syntax.sym = p.first;
|
||||
for (b != nil) {
|
||||
if (b.skind == syntax.skind.SK_TYPE
|
||||
&& syntax.streq(b.name, name)
|
||||
&& directmodvisible(c, b.mod)) { return b; };
|
||||
b = b.snext;
|
||||
};
|
||||
p = p.parent;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
fn builtintypename(name: str) bool = {
|
||||
return syntax.streq(name, "void")
|
||||
|| syntax.streq(name, "bool")
|
||||
|| syntax.streq(name, "rune")
|
||||
|| syntax.streq(name, "i8")
|
||||
|| syntax.streq(name, "i16")
|
||||
|| syntax.streq(name, "i32")
|
||||
|| syntax.streq(name, "i64")
|
||||
|| syntax.streq(name, "u8")
|
||||
|| syntax.streq(name, "u16")
|
||||
|| syntax.streq(name, "u32")
|
||||
|| syntax.streq(name, "u64")
|
||||
|| syntax.streq(name, "int")
|
||||
|| syntax.streq(name, "uint")
|
||||
|| syntax.streq(name, "uintptr")
|
||||
|| syntax.streq(name, "size")
|
||||
|| syntax.streq(name, "opaque")
|
||||
|| syntax.streq(name, "f32")
|
||||
|| syntax.streq(name, "f64")
|
||||
|| syntax.streq(name, "str")
|
||||
|| syntax.streq(name, "never")
|
||||
|| syntax.streq(name, "nomem")
|
||||
|| syntax.streq(name, "untyped_int")
|
||||
|| syntax.streq(name, "untyped_float")
|
||||
|| syntax.streq(name, "untyped_str")
|
||||
|| syntax.streq(name, "untyped_rune")
|
||||
|| syntax.streq(name, "untyped_bool")
|
||||
|| syntax.streq(name, "untyped_nil");
|
||||
};
|
||||
|
||||
fn packageaccesserr(c: *checker, e: *syntax.node, pkg: str, member: str,
|
||||
missing: bool) void = {
|
||||
cerr(e.file); cerr(":");
|
||||
cerr(strconv.i32tos(e.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(e.col, strconv.base.DEC));
|
||||
cerr(": error: package '"); cerr(pkg);
|
||||
if (missing) {
|
||||
cerr("' has no exported declaration '"); cerr(member); cerr("'\n");
|
||||
} else {
|
||||
cerr("' is not directly imported\n");
|
||||
};
|
||||
c.errs += 1;
|
||||
};
|
||||
|
||||
// A bare `w6c -T` leaves the compiler-generated test.run hook external;
|
||||
// the ordinary driver supplies lib/test. No user-written missing member is
|
||||
// exempt from package export checking.
|
||||
fn synthesizedtestrun(c: *checker, e: *syntax.node) bool = {
|
||||
return c.synthtestrun != nil && c.synthtestrun == e;
|
||||
};
|
||||
|
||||
// checkmoduleshadow — enforce "value names and module names are
|
||||
// disjoint" at nested-scope binds. Mirrors cstage check_module_shadow
|
||||
// (cmd/wcc/check.c). Fires for fn params / lets / forrange iters /
|
||||
@@ -498,7 +641,7 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
if (k == syntax.nkind.N_IDENT) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, nm);
|
||||
let s: *syntax.sym = lookupvisible(c, nm);
|
||||
if (s == nil) {
|
||||
// Unshadowed abort/assert binds no sym BY
|
||||
// DESIGN (the EXPR_ASSERT family has no callee
|
||||
@@ -522,13 +665,14 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
if (k == syntax.nkind.N_TNAME) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, nm);
|
||||
let s: *syntax.sym = lookupvisibletype(c, nm);
|
||||
let builtin: bool = builtintypename(nm);
|
||||
// `pkg.Type` — strip the last dot prefix and look up
|
||||
// the leaf with a mod filter so same-leaf-name types
|
||||
// from different imports (`bufio.stream` vs
|
||||
// `io.stream`) disambiguate to the right one.
|
||||
// Mirrors cmd/wcc/check.c resolve_typename.
|
||||
if (s == nil) {
|
||||
if (s == nil && !builtin) {
|
||||
let dot: i32 = nm.len - 1;
|
||||
for (dot >= 0) {
|
||||
if (nm[dot] == 46u8) { break; };
|
||||
@@ -543,18 +687,39 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + (dot + 1): u64;
|
||||
leaf.len = nm.len - (dot + 1);
|
||||
s = syntax.scopelookupinmodule(c.cur, modkeyfor(c, head), leaf);
|
||||
let mk: str = modkeyfor(c, head);
|
||||
if (mk.len != 0) {
|
||||
s = syntax.scopelookupinmodule(c.cur, mk, leaf);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (s == nil) {
|
||||
if (s == nil && !builtin) {
|
||||
if (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));
|
||||
cerr(": error: unknown type '"); cerr(nm); cerr("'\n");
|
||||
c.errs += 1;
|
||||
n.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
c.nunresolved += 1;
|
||||
if (c.verbose != 0) {
|
||||
cerr(" unresolved tname: ");
|
||||
cerr(nm);
|
||||
cerr("\n");
|
||||
};
|
||||
} else { c.nresolved += 1; };
|
||||
} else {
|
||||
c.nresolved += 1;
|
||||
// Resolve and cache the type while c.curmod still names the
|
||||
// declaration that owns this syntax node. Exported structs may
|
||||
// contain a direct dependency's type; consumers need that cached
|
||||
// shape for structural field access, but must not gain source
|
||||
// visibility of the dependency qualifier. Cstage does the same
|
||||
// owner-scoped work in resolve_typedecl.
|
||||
let ti: *syntax.tinfo = tinfofornode(c, n);
|
||||
if (ti != nil) { n.type_ = ti: *void; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -991,6 +1156,25 @@ fn unwrapbang(n: *syntax.node) *syntax.node = {
|
||||
fn aliassym(c: *checker, n: *syntax.node) *syntax.sym = {
|
||||
if (n == nil) { return nil; };
|
||||
if (n.kind != syntax.nkind.N_TNAME) { return nil; };
|
||||
// An exported signature is resolved while its owning package is the
|
||||
// current module. Preserve that compiler-owned binding when the exact
|
||||
// same AST node is later inspected structurally by a consumer (for
|
||||
// example `os.filestat.atime.sec`, where atime is time.instant). This
|
||||
// does not make a consumer-written `time.instant` visible: such a node
|
||||
// has no owner-resolved named cache unless the consumer imports time.
|
||||
let bound: *syntax.tinfo = n.type_: *syntax.tinfo;
|
||||
if (bound != nil) { if (bound.kind == syntax.tykind.TY_NAMED) {
|
||||
let owner: *syntax.scope = c.cur;
|
||||
for (owner != nil) {
|
||||
let bs: *syntax.sym = owner.first;
|
||||
for (bs != nil) {
|
||||
if (bs.skind == syntax.skind.SK_TYPE
|
||||
&& bs.type_ == bound) { return bs; };
|
||||
bs = bs.snext;
|
||||
};
|
||||
owner = owner.parent;
|
||||
};
|
||||
}; };
|
||||
let nm: str = n.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
|
||||
@@ -1013,7 +1197,10 @@ fn aliassym(c: *checker, n: *syntax.node) *syntax.sym = {
|
||||
let leaf: str;
|
||||
leaf.ptr = nm.ptr + ((dotidx + 1): u64);
|
||||
leaf.len = nm.len - dotidx - 1;
|
||||
s = syntax.scopelookupinmodule(c.cur, modkeyfor(c, head), leaf);
|
||||
let mk: str = modkeyfor(c, head);
|
||||
if (mk.len != 0) {
|
||||
s = syntax.scopelookupinmodule(c.cur, mk, leaf);
|
||||
};
|
||||
} else {
|
||||
// #53: same-module preference. Mirrors cstage
|
||||
// cmd/wcc/check.c:66 scope_lookup_prefer. Without this,
|
||||
@@ -1026,7 +1213,7 @@ fn aliassym(c: *checker, n: *syntax.node) *syntax.sym = {
|
||||
// scopelookupprefer (the #56/#4/#11a wave). The only bare-leaf
|
||||
// type lookups left — varianterr (:1051) + scruttype (:1098) —
|
||||
// stay mod-blind but have no reproducible divergence; #58.
|
||||
s = syntax.scopelookupprefer(c.cur, c.curmod, nm);
|
||||
s = lookupvisibletype(c, nm);
|
||||
// #61 A.5: bare TNAME that collides with an imported
|
||||
// module bareword. Two shapes hit this:
|
||||
// - `let l: lex;` where `lex` struct lives in
|
||||
@@ -1049,7 +1236,7 @@ fn aliassym(c: *checker, n: *syntax.node) *syntax.sym = {
|
||||
// otherwise resolve install-order-dependent here
|
||||
// when a value binding shadows the leaf; cstage
|
||||
// passes c->cur_mod to scope_lookup_type (sym.c).
|
||||
let sm: *syntax.sym = syntax.scopelookuptype(c.cur, c.curmod, nm);
|
||||
let sm: *syntax.sym = lookupvisibletype(c, nm);
|
||||
if (sm != nil) { s = sm; };
|
||||
};
|
||||
};
|
||||
@@ -1271,7 +1458,9 @@ fn scruttype(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
if (e.kind == syntax.nkind.N_DOT) {
|
||||
if (e.lhs == nil) { return nil; };
|
||||
if (e.lhs.kind != syntax.nkind.N_IDENT) { return nil; };
|
||||
let s: *syntax.sym = syntax.scopelookupinmodule(c.cur, modkeyfor(c, e.lhs.str), e.str);
|
||||
let mk: str = modkeyfor(c, e.lhs.str);
|
||||
if (mk.len == 0) { return nil; };
|
||||
let s: *syntax.sym = syntax.scopelookupinmodule(c.cur, mk, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
@@ -1862,7 +2051,7 @@ fn evaldefconst(c: *checker, n: *syntax.node, out: *u64, depth: i32) bool = {
|
||||
return true;
|
||||
};
|
||||
if (k == syntax.nkind.N_IDENT) {
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, n.str);
|
||||
let s: *syntax.sym = lookupvisible(c, n.str);
|
||||
if (s == nil) { return false; };
|
||||
if (s.skind != syntax.skind.SK_DEF) { return false; };
|
||||
if (s.decl == nil) { return false; };
|
||||
@@ -1872,7 +2061,9 @@ fn evaldefconst(c: *checker, n: *syntax.node, out: *u64, depth: i32) bool = {
|
||||
if (k == syntax.nkind.N_DOT) {
|
||||
if (n.lhs == nil) { return false; };
|
||||
if (n.lhs.kind != syntax.nkind.N_IDENT) { return false; };
|
||||
let s: *syntax.sym = syntax.scopelookupinmodule(c.cur, modkeyfor(c, n.lhs.str), n.str);
|
||||
let mk: str = modkeyfor(c, n.lhs.str);
|
||||
if (mk.len == 0) { return false; };
|
||||
let s: *syntax.sym = syntax.scopelookupinmodule(c.cur, mk, n.str);
|
||||
if (s == nil) { return false; };
|
||||
if (s.skind != syntax.skind.SK_DEF) { return false; };
|
||||
if (s.decl == nil) { return false; };
|
||||
@@ -3287,7 +3478,7 @@ fn unoptype(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
// the foreign signature (scopedefineinmodule prepends);
|
||||
// cstage types a fn ident via scope_lookup_prefer with
|
||||
// cur_mod (cmd/wcc/check.c:1305).
|
||||
let fs: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, e.lhs.str);
|
||||
let fs: *syntax.sym = lookupvisible(c, e.lhs.str);
|
||||
if (fs != nil) {
|
||||
if (fs.skind == syntax.skind.SK_FN) {
|
||||
if (fs.decl != nil) {
|
||||
@@ -3313,7 +3504,11 @@ fn unoptype(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
// cstage's actual acceptance reason.
|
||||
if (e.lhs.kind == syntax.nkind.N_DOT) {
|
||||
if (e.lhs.lhs != nil && e.lhs.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
let fs: *syntax.sym = syntax.scopelookupinmodule(c.cur, modkeyfor(c, e.lhs.lhs.str), e.lhs.str);
|
||||
let fs: *syntax.sym = nil;
|
||||
let mk: str = modkeyfor(c, e.lhs.lhs.str);
|
||||
if (mk.len != 0) {
|
||||
fs = syntax.scopelookupinmodule(c.cur, mk, e.lhs.str);
|
||||
};
|
||||
if (fs != nil) {
|
||||
if (fs.skind == syntax.skind.SK_FN) {
|
||||
if (fs.decl != nil) {
|
||||
@@ -3388,7 +3583,7 @@ fn indexresult(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
// valid; only the SK_DEF scalar-str operand is
|
||||
// unindexable. cstage twin: check.c N_INDEX TY_STR arm.
|
||||
if (e.lhs != nil && e.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, e.lhs.str);
|
||||
let s: *syntax.sym = lookupvisible(c, e.lhs.str);
|
||||
if (s != nil && s.skind == syntax.skind.SK_DEF) {
|
||||
cerr("error: cannot index a def-constant str '");
|
||||
cerr(e.lhs.str);
|
||||
@@ -3536,8 +3731,22 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// bare `error` then binds strconv.error not io.error). Mirrors
|
||||
// cstage cmd/wcc/check.c:66 scope_lookup_prefer; sibling #56 at
|
||||
// L2439, #53 at L688. Tracked in the cluster note at L685-687.
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
let s: *syntax.sym = lookupvisible(c, e.str);
|
||||
if (s == 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) {
|
||||
cerr(e.file); cerr(":");
|
||||
cerr(strconv.i32tos(e.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(e.col, strconv.base.DEC));
|
||||
cerr(": error: undefined: "); cerr(e.str); cerr("\n");
|
||||
c.errs += 1;
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
if (s.decl == nil) { return nil; };
|
||||
// #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
|
||||
@@ -3632,6 +3841,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
return e.rhs;
|
||||
};
|
||||
if (k == syntax.nkind.N_CALL) {
|
||||
if (e.type_ == c.tc.tyerr: *void) { return nil; };
|
||||
let callee: *syntax.node = e.lhs;
|
||||
if (callee == nil) { return nil; };
|
||||
// #31: synthesize the `alloc(value)` / `alloc([], n)` builtin
|
||||
@@ -3809,7 +4019,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// The TY_ERR stamp on the callee is cgen's routing key
|
||||
// (cstage spells it `n->lhs->type = ty_err`).
|
||||
if (syntax.streq(callee.str, "abort")
|
||||
&& syntax.scopelookupprefer(c.cur, c.curmod, "abort") == nil) {
|
||||
&& lookupvisible(c, "abort") == nil) {
|
||||
if (e.list != nil) {
|
||||
let mt: *syntax.node = exprtype(c, e.list, nil);
|
||||
let conf: bool = false;
|
||||
@@ -3828,7 +4038,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
return tn;
|
||||
};
|
||||
if (syntax.streq(callee.str, "assert") && e.list != nil
|
||||
&& syntax.scopelookupprefer(c.cur, c.curmod, "assert") == nil) {
|
||||
&& lookupvisible(c, "assert") == nil) {
|
||||
let ct: *syntax.node = exprtype(c, e.list, nil);
|
||||
if (ct != nil) {
|
||||
// No alias peel: cstage compares ty_bool by
|
||||
@@ -4010,11 +4220,11 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
if (nm.len > 0) {
|
||||
let s: *syntax.sym = nil;
|
||||
if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
s = syntax.scopelookupprefer(c.cur, c.curmod, nm);
|
||||
s = lookupvisible(c, nm);
|
||||
} else {
|
||||
let ms: *syntax.sym = nil;
|
||||
if (callee.lhs != nil && callee.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
ms = syntax.scopelookupprefer(c.cur, c.curmod, callee.lhs.str);
|
||||
ms = lookupvisible(c, callee.lhs.str);
|
||||
if (ms != nil && ms.skind != syntax.skind.SK_USE) {
|
||||
let mu: *syntax.sym = syntax.scopelookupuselocal(ms.scope, callee.lhs.str);
|
||||
if (mu != nil) { ms = mu; };
|
||||
@@ -4036,7 +4246,17 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// global-leaf path) and harec check_autodereference
|
||||
// (ref/harec/src/check.c:1566-1581).
|
||||
if (ms != nil && (ms.skind == syntax.skind.SK_USE || ms.use_alias != 0i32)) {
|
||||
s = syntax.scopelookupinmodule(c.cur, modkeyfor(c, callee.lhs.str), nm);
|
||||
let mk: str = modkeyfor(c, callee.lhs.str);
|
||||
if (mk.len == 0) {
|
||||
if (ms.skind == syntax.skind.SK_USE) {
|
||||
packageaccesserr(c, callee, callee.lhs.str, nm, false);
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
callee.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
} else {
|
||||
s = syntax.scopelookupinmodule(c.cur, mk, nm);
|
||||
};
|
||||
// Module-qualified callee whose leaf isn't scope-keyed
|
||||
// under its module: align to cstage, which stamps ty_err
|
||||
// here and lets cgen emit the call (cmd/wcc/check.c:1834-
|
||||
@@ -4050,6 +4270,10 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// keys the CALL off run's `//ww:module test` directive
|
||||
// either way, so the resolution change is asm-neutral.
|
||||
if (s == nil) {
|
||||
if (c.sepmode != 0 && ms.skind == syntax.skind.SK_USE && mk.len != 0
|
||||
&& !synthesizedtestrun(c, callee)) {
|
||||
packageaccesserr(c, callee, callee.lhs.str, nm, true);
|
||||
};
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
callee.type_ = c.tc.tyerr: *void; // N_DOT node itself (asserttyped checks it)
|
||||
return nil;
|
||||
@@ -4106,6 +4330,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
return nil;
|
||||
};
|
||||
if (k == syntax.nkind.N_DOT) {
|
||||
if (e.type_ == c.tc.tyerr: *void) { return nil; };
|
||||
// A.6.1.5a — fold cases only. Mirrors cstage cmd/wcc/check.c
|
||||
// :740-832. Struct field + pseudo-field (.len/.cap/.ptr) lands
|
||||
// in A.6.1.5b. SK_USE gates case 1; a #6a-D dot-lhs collision
|
||||
@@ -4120,7 +4345,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// ref/harec/src/check.c:4419-4434.
|
||||
let lhsn: *syntax.node = e.lhs;
|
||||
if (lhsn != nil) { if (lhsn.kind == syntax.nkind.N_IDENT) {
|
||||
let ms: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, lhsn.str);
|
||||
let ms: *syntax.sym = lookupvisible(c, lhsn.str);
|
||||
if (ms != nil && ms.skind != syntax.skind.SK_USE) {
|
||||
let mu: *syntax.sym = syntax.scopelookupuselocal(ms.scope, lhsn.str);
|
||||
if (mu != nil) { ms = mu; };
|
||||
@@ -4133,7 +4358,16 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
// qualified resolution and the lenient checker policy
|
||||
// keeps the silent miss documented at scruttype L656.
|
||||
if (ms.skind == syntax.skind.SK_USE || ms.use_alias != 0i32) {
|
||||
let fs: *syntax.sym = syntax.scopelookupinmodule(c.cur, modkeyfor(c, lhsn.str), e.str);
|
||||
let mk: str = modkeyfor(c, lhsn.str);
|
||||
if (mk.len == 0 && ms.skind == syntax.skind.SK_USE) {
|
||||
packageaccesserr(c, e, lhsn.str, e.str, false);
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
let fs: *syntax.sym = nil;
|
||||
if (mk.len != 0) {
|
||||
fs = syntax.scopelookupinmodule(c.cur, mk, e.str);
|
||||
};
|
||||
if (fs != nil) { if (fs.decl != nil) {
|
||||
// #34: a module-qualified bare fn rvalue `mod.fn` types as
|
||||
// its FN TYPE (twin of the N_IDENT arm, :2688); decl.lhs is
|
||||
@@ -4165,6 +4399,12 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
return tn;
|
||||
};
|
||||
}; };
|
||||
if (c.sepmode != 0 && fs == nil && ms.skind == syntax.skind.SK_USE && mk.len != 0
|
||||
&& !synthesizedtestrun(c, e)) {
|
||||
packageaccesserr(c, e, lhsn.str, e.str, true);
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
return nil;
|
||||
};
|
||||
};
|
||||
// Fold case 2 inner: bare `EnumT.MEMBER` where EnumT
|
||||
// is an SK_TYPE in the flat scope. Mirror cstage
|
||||
@@ -4310,7 +4550,7 @@ 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) {
|
||||
let ms: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, e.lhs.str);
|
||||
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;
|
||||
if (tn != nil) {
|
||||
@@ -4906,7 +5146,7 @@ fn assignableaddrfn(c: *checker, dst: *syntax.node, rhs: *syntax.node) bool = {
|
||||
// silently admitting a *fn into a foreign-sig slot or rejecting a
|
||||
// valid same-module &fn. cstage uses scope_lookup_prefer with
|
||||
// cur_mod (cmd/wcc/check.c:410, assignable_addrfn).
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, id.str);
|
||||
let s: *syntax.sym = lookupvisible(c, id.str);
|
||||
if (s == nil) { return false; };
|
||||
if (s.skind != syntax.skind.SK_FN) { return false; };
|
||||
if (s.decl == nil) { return false; };
|
||||
@@ -6043,7 +6283,7 @@ fn desugararrayslice(c: *checker, dsttn: *syntax.node, srctn: *syntax.node, val:
|
||||
fn calleefndecl(c: *checker, callee: *syntax.node) *syntax.node = {
|
||||
if (callee == nil) { return nil; };
|
||||
if (callee.kind == syntax.nkind.N_IDENT) {
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, callee.str);
|
||||
let s: *syntax.sym = lookupvisible(c, callee.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == syntax.skind.SK_FN) { return s.decl; };
|
||||
};
|
||||
@@ -6052,14 +6292,18 @@ fn calleefndecl(c: *checker, callee: *syntax.node) *syntax.node = {
|
||||
if (callee.kind == syntax.nkind.N_DOT) {
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
let ms: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, callee.lhs.str);
|
||||
let ms: *syntax.sym = lookupvisible(c, callee.lhs.str);
|
||||
if (ms != nil && ms.skind != syntax.skind.SK_USE) {
|
||||
let mu: *syntax.sym = syntax.scopelookupuselocal(ms.scope, callee.lhs.str);
|
||||
if (mu != nil) { ms = mu; };
|
||||
};
|
||||
if (ms != nil) {
|
||||
if (ms.skind == syntax.skind.SK_USE || ms.use_alias != 0i32) {
|
||||
let fs: *syntax.sym = syntax.scopelookupinmodule(c.cur, modkeyfor(c, callee.lhs.str), callee.str);
|
||||
let fs: *syntax.sym = nil;
|
||||
let mk: str = modkeyfor(c, callee.lhs.str);
|
||||
if (mk.len != 0) {
|
||||
fs = syntax.scopelookupinmodule(c.cur, mk, callee.str);
|
||||
};
|
||||
if (fs != nil) {
|
||||
if (fs.skind == syntax.skind.SK_FN) { return fs.decl; };
|
||||
};
|
||||
@@ -6218,7 +6462,7 @@ fn checkassign(c: *checker, n: *syntax.node) void = {
|
||||
// cmd/wcc/check.c:1889-1896.
|
||||
if (n.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
if (n.lhs.str.len > 0) {
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, n.lhs.str);
|
||||
let s: *syntax.sym = lookupvisible(c, n.lhs.str);
|
||||
if (s != nil) {
|
||||
if (s.is_const != 0i32) {
|
||||
cerr("error: cannot assign to const binding\n");
|
||||
@@ -6763,7 +7007,7 @@ fn exprtypeoftry(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
// either spuriously rejecting valid `?` code or skipping the F8
|
||||
// reject. Mirrors exprtype's own N_IDENT arm (scopelookupprefer
|
||||
// at :2897); cstage types the operand via cexpr with cur_mod.
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, e.str);
|
||||
let s: *syntax.sym = lookupvisible(c, e.str);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
return s.decl.lhs;
|
||||
@@ -6791,7 +7035,7 @@ fn exprtypeoftry(c: *checker, e: *syntax.node) *syntax.node = {
|
||||
// N_CALL arm (scopelookupprefer at :3336). The N_DOT-callee leaf
|
||||
// (callee.str above) still resolves bare-leaf, not via the module
|
||||
// qualifier callee.lhs.str — that module-keyed fix rides task #51.
|
||||
let s: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, nm);
|
||||
let s: *syntax.sym = lookupvisible(c, nm);
|
||||
if (s == nil) { return nil; };
|
||||
if (s.skind != syntax.skind.SK_FN) { return nil; };
|
||||
if (s.decl == nil) { return nil; };
|
||||
@@ -7028,7 +7272,7 @@ fn isassertfam(c: *checker, id: *syntax.node) bool = {
|
||||
if (!syntax.streq(id.str, "abort") && !syntax.streq(id.str, "assert")) {
|
||||
return false;
|
||||
};
|
||||
return syntax.scopelookupprefer(c.cur, c.curmod, id.str) == nil;
|
||||
return lookupvisible(c, id.str) == nil;
|
||||
};
|
||||
|
||||
// asserttyped — post-checker invariant gate (#15, A.6.2.1e). Walks the
|
||||
@@ -7089,7 +7333,10 @@ fn asserttyped(c: *checker, n: *syntax.node, indot: bool) void = {
|
||||
if (isexpr && k == syntax.nkind.N_IDENT) {
|
||||
if (indot) { skip = true; };
|
||||
if (!skip) {
|
||||
let s: *syntax.sym = syntax.scopelookup(c.cur, n.str);
|
||||
// Use the same source-visibility rules as expression
|
||||
// resolution. A transitive same-leaf declaration must not hide
|
||||
// a decl-less pseudo-builtin from this invariant gate.
|
||||
let s: *syntax.sym = lookupvisible(c, n.str);
|
||||
if (s != nil) {
|
||||
if (s.skind == syntax.skind.SK_USE) { skip = true; };
|
||||
if (s.decl == nil) { skip = true; };
|
||||
@@ -7146,6 +7393,8 @@ export fn checkinit(c: *checker, tc: *syntax.tctx) void = {
|
||||
c.nunresolved = 0;
|
||||
c.errs = 0;
|
||||
c.istest = 0i32; // #15: caller (w6c main) sets it after init
|
||||
c.sepmode = 0i32; // caller (w6c main) sets it from -c
|
||||
c.synthtestrun = nil;
|
||||
c.verbose = 0;
|
||||
c.fnret = nil;
|
||||
let empty: str;
|
||||
@@ -7395,6 +7644,7 @@ export fn checkfile(c: *checker, file: *syntax.node) void = {
|
||||
did.str = "test";
|
||||
dot.lhs = did;
|
||||
dot.str = "run";
|
||||
c.synthtestrun = dot;
|
||||
call.lhs = dot;
|
||||
call.list = arg;
|
||||
let ret: *syntax.node = syntax.newnode(syntax.nkind.N_RETURN, pf, pl, pc);
|
||||
|
||||
@@ -668,10 +668,14 @@ export fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = {
|
||||
};
|
||||
wwisortdecls(upaths, unodes, nuse);
|
||||
let i: i32 = 0;
|
||||
let previous: str = "";
|
||||
for (i < nuse) {
|
||||
wputs(fd, "import ");
|
||||
wputs(fd, upaths[i]);
|
||||
wputs(fd, ";\n");
|
||||
if (previous.len == 0 || !syntax.streq(previous, upaths[i])) {
|
||||
wputs(fd, "import ");
|
||||
wputs(fd, upaths[i]);
|
||||
wputs(fd, ";\n");
|
||||
previous = upaths[i];
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user