ww: resolve self-named import to the dir-package, not a sibling file (M4 E3, #98)
The driver searchpath is srcd-first (srcd = the entry file's directory).
A co-located black-box test lib/<mod>/<mod>test.ww makes srcd=lib/<mod>,
so resolving `import <mod>` hit the sibling-FILE branch lib/<mod>/<mod>.ww
and folded it inline into the consumer unit under the wrong module tag
("package <mod> does not match import path <importer>") — 7 lib-run tests
fail under separate compilation. The combined amalgamator tolerated the
co-location; only sep surfaced it.
Resolve a package directory-first: walk ALL searchpath entries for a
directory match, and only fall back to a file match if no directory
exists anywhere. A dir-package now beats a same-named sibling file (fixes
the self-named shadow), while a leaf package with no directory (e.g.
lib/encoding/hex) still resolves via its file. This realizes the driver's
"a module is the directory" intent; the originally-specced per-directory
suppression was rejected because it broke leaf packages (rob-pike). Both
stages (cmd/ww/main.c + selfhost twin). The dir-beats-earlier-file
precedence change is latent and loud-failing (#101).
Move-set: ww + ww_ww (driver) only; w6c_ww/wwdump_ww/w6a_ww/w6l_ww HOLD.
Gate: test/wcc/989_coloimport_sep.c (table-driven, both stages).
This commit is contained in:
@@ -3112,14 +3112,14 @@ fn importpathform(name: *u8, namelen: u64) *u8 = {
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
// Try <dir>/<path>/ as a directory, then <dir>/<path>.ww as a file.
|
||||
// Sets *isdir on hit. Symmetric with cstage locate_import_in for
|
||||
// byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
||||
// Try <dir>/<path>/ as a directory (wantdir != 0), else <dir>/<path>.ww
|
||||
// as a file. Sets *isdir on hit. Symmetric with cstage locate_import_in
|
||||
// for byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
||||
// form was dropped in task #22 — directory-as-module enumeration
|
||||
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
||||
// `foo/foo.ha` fallback; a module IS the directory).
|
||||
fn locatein(dir: *u8, dirlen: u64,
|
||||
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
||||
pathform: *u8, pflen: u64, isdir: *i32, wantdir: i32) *u8 = {
|
||||
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
let off: u64 = 0u64;
|
||||
let i: u64 = 0u64;
|
||||
@@ -3129,63 +3129,71 @@ fn locatein(dir: *u8, dirlen: u64,
|
||||
i = 0u64;
|
||||
for (i < pflen) { buf[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
if (wantdir != 0i32) {
|
||||
buf[off] = 0u8;
|
||||
let fi: os.filestat;
|
||||
let r: (void | os.oserror) = os.stat(&fi, pathstr(buf.ptr));
|
||||
match (r) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) {
|
||||
*isdir = 1;
|
||||
return buf.ptr;
|
||||
};
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
buf[off] = 46u8; off += 1u64; // '.'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
buf[off] = 0u8;
|
||||
let fi: os.filestat;
|
||||
let r: (void | os.oserror) = os.stat(&fi, pathstr(buf.ptr));
|
||||
let isdirhit: bool = false;
|
||||
match (r) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) { isdirhit = true; };
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (isdirhit) {
|
||||
*isdir = 1;
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
let buf2: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
off = 0u64;
|
||||
i = 0u64;
|
||||
for (i < dirlen) { buf2[off + i] = dir[i]; i += 1u64; };
|
||||
off += dirlen;
|
||||
buf2[off] = 47u8; off += 1u64;
|
||||
i = 0u64;
|
||||
for (i < pflen) { buf2[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
buf2[off] = 46u8; off += 1u64; // '.'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 0u8;
|
||||
if (os.access(pathstr(buf2.ptr), 0i32) == 0) {
|
||||
if (os.access(pathstr(buf.ptr), 0i32) == 0) {
|
||||
*isdir = 0;
|
||||
return buf2.ptr;
|
||||
return buf.ptr;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
||||
// *isdir on hit.
|
||||
//
|
||||
// #98: "a module IS the directory" — a directory-package on ANY entry
|
||||
// wins over a same-named sibling FILE on an EARLIER entry. The driver
|
||||
// builds the searchpath srcd-first; a co-located `lib/<mod>/<mod>test.ww`
|
||||
// entry makes srcd = lib/<mod>, so a self-named `import <mod>` would
|
||||
// else file-hit the sibling lib/<mod>/<mod>.ww and (under --sep) fold
|
||||
// inline under the wrong module-reset. Two passes — directories first,
|
||||
// files only if no directory matches anywhere — let lib/<mod>/ resolve
|
||||
// as the dir while a genuine leaf package with no directory (e.g.
|
||||
// lib/encoding/hex imported bare as `hex`, reachable only via its file
|
||||
// in srcd) still resolves in the file pass. Latent: a dir-package now
|
||||
// beats an earlier-entry same-named sibling FILE — loud-failing, none
|
||||
// in the corpus; tracked as #101.
|
||||
fn locateimport(dirs: *u8, name: *u8, namelen: u64,
|
||||
isdir: *i32) *u8 = {
|
||||
let pathform: *u8 = importpathform(name, namelen);
|
||||
let pflen: u64 = cstrlen(pathform);
|
||||
let total: u64 = cstrlen(dirs);
|
||||
let p: u64 = 0u64;
|
||||
for (p < total) {
|
||||
let q: u64 = p;
|
||||
for (q < total) {
|
||||
if (dirs[q] == ':') { break; };
|
||||
q += 1u64;
|
||||
let wantdir: i32 = 1i32;
|
||||
for (wantdir >= 0i32) {
|
||||
let p: u64 = 0u64;
|
||||
for (p < total) {
|
||||
let q: u64 = p;
|
||||
for (q < total) {
|
||||
if (dirs[q] == ':') { break; };
|
||||
q += 1u64;
|
||||
};
|
||||
let seglen: u64 = q - p;
|
||||
if (seglen > 0u64) {
|
||||
let hit: *u8 = locatein(dirs + p, seglen,
|
||||
pathform, pflen, isdir, wantdir);
|
||||
if (hit != nil) { return hit; };
|
||||
};
|
||||
p = q + 1u64;
|
||||
};
|
||||
let seglen: u64 = q - p;
|
||||
if (seglen > 0u64) {
|
||||
let hit: *u8 = locatein(dirs + p, seglen,
|
||||
pathform, pflen, isdir);
|
||||
if (hit != nil) { return hit; };
|
||||
};
|
||||
p = q + 1u64;
|
||||
wantdir -= 1i32;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
@@ -238,14 +238,14 @@ fn importpathform(name: *u8, namelen: u64) *u8 = {
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
// Try <dir>/<path>/ as a directory, then <dir>/<path>.ww as a file.
|
||||
// Sets *isdir on hit. Symmetric with cstage locate_import_in for
|
||||
// byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
||||
// Try <dir>/<path>/ as a directory (wantdir != 0), else <dir>/<path>.ww
|
||||
// as a file. Sets *isdir on hit. Symmetric with cstage locate_import_in
|
||||
// for byte-id driver output (rule 10). The legacy <dir>/<name>/<name>.ww
|
||||
// form was dropped in task #22 — directory-as-module enumeration
|
||||
// replaces it, mirroring ref/hare/hare/module/srcs.ha (Hare has no
|
||||
// `foo/foo.ha` fallback; a module IS the directory).
|
||||
fn locatein(dir: *u8, dirlen: u64,
|
||||
pathform: *u8, pflen: u64, isdir: *i32) *u8 = {
|
||||
pathform: *u8, pflen: u64, isdir: *i32, wantdir: i32) *u8 = {
|
||||
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
let off: u64 = 0u64;
|
||||
let i: u64 = 0u64;
|
||||
@@ -255,63 +255,71 @@ fn locatein(dir: *u8, dirlen: u64,
|
||||
i = 0u64;
|
||||
for (i < pflen) { buf[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
if (wantdir != 0i32) {
|
||||
buf[off] = 0u8;
|
||||
let fi: os.filestat;
|
||||
let r: (void | os.oserror) = os.stat(&fi, pathstr(buf.ptr));
|
||||
match (r) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) {
|
||||
*isdir = 1;
|
||||
return buf.ptr;
|
||||
};
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
buf[off] = 46u8; off += 1u64; // '.'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
buf[off] = 119u8; off += 1u64; // 'w'
|
||||
buf[off] = 0u8;
|
||||
let fi: os.filestat;
|
||||
let r: (void | os.oserror) = os.stat(&fi, pathstr(buf.ptr));
|
||||
let isdirhit: bool = false;
|
||||
match (r) {
|
||||
case void => {
|
||||
let t: u32 = (fi.mode: u32) & 61440u32; // S_IFMT
|
||||
if (t == os.mode.DIR: u32) { isdirhit = true; };
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
if (isdirhit) {
|
||||
*isdir = 1;
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
let buf2: []u8 = alloc([], (os.PATH_MAX: u64))!;
|
||||
off = 0u64;
|
||||
i = 0u64;
|
||||
for (i < dirlen) { buf2[off + i] = dir[i]; i += 1u64; };
|
||||
off += dirlen;
|
||||
buf2[off] = 47u8; off += 1u64;
|
||||
i = 0u64;
|
||||
for (i < pflen) { buf2[off + i] = pathform[i]; i += 1u64; };
|
||||
off += pflen;
|
||||
buf2[off] = 46u8; off += 1u64; // '.'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 119u8; off += 1u64; // 'w'
|
||||
buf2[off] = 0u8;
|
||||
if (os.access(pathstr(buf2.ptr), 0i32) == 0) {
|
||||
if (os.access(pathstr(buf.ptr), 0i32) == 0) {
|
||||
*isdir = 0;
|
||||
return buf2.ptr;
|
||||
return buf.ptr;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// Walk a colon-separated dirlist, return first hit or nil. Sets
|
||||
// *isdir on hit.
|
||||
//
|
||||
// #98: "a module IS the directory" — a directory-package on ANY entry
|
||||
// wins over a same-named sibling FILE on an EARLIER entry. The driver
|
||||
// builds the searchpath srcd-first; a co-located `lib/<mod>/<mod>test.ww`
|
||||
// entry makes srcd = lib/<mod>, so a self-named `import <mod>` would
|
||||
// else file-hit the sibling lib/<mod>/<mod>.ww and (under --sep) fold
|
||||
// inline under the wrong module-reset. Two passes — directories first,
|
||||
// files only if no directory matches anywhere — let lib/<mod>/ resolve
|
||||
// as the dir while a genuine leaf package with no directory (e.g.
|
||||
// lib/encoding/hex imported bare as `hex`, reachable only via its file
|
||||
// in srcd) still resolves in the file pass. Latent: a dir-package now
|
||||
// beats an earlier-entry same-named sibling FILE — loud-failing, none
|
||||
// in the corpus; tracked as #101.
|
||||
fn locateimport(dirs: *u8, name: *u8, namelen: u64,
|
||||
isdir: *i32) *u8 = {
|
||||
let pathform: *u8 = importpathform(name, namelen);
|
||||
let pflen: u64 = cstrlen(pathform);
|
||||
let total: u64 = cstrlen(dirs);
|
||||
let p: u64 = 0u64;
|
||||
for (p < total) {
|
||||
let q: u64 = p;
|
||||
for (q < total) {
|
||||
if (dirs[q] == ':') { break; };
|
||||
q += 1u64;
|
||||
let wantdir: i32 = 1i32;
|
||||
for (wantdir >= 0i32) {
|
||||
let p: u64 = 0u64;
|
||||
for (p < total) {
|
||||
let q: u64 = p;
|
||||
for (q < total) {
|
||||
if (dirs[q] == ':') { break; };
|
||||
q += 1u64;
|
||||
};
|
||||
let seglen: u64 = q - p;
|
||||
if (seglen > 0u64) {
|
||||
let hit: *u8 = locatein(dirs + p, seglen,
|
||||
pathform, pflen, isdir, wantdir);
|
||||
if (hit != nil) { return hit; };
|
||||
};
|
||||
p = q + 1u64;
|
||||
};
|
||||
let seglen: u64 = q - p;
|
||||
if (seglen > 0u64) {
|
||||
let hit: *u8 = locatein(dirs + p, seglen,
|
||||
pathform, pflen, isdir);
|
||||
if (hit != nil) { return hit; };
|
||||
};
|
||||
p = q + 1u64;
|
||||
wantdir -= 1i32;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user