From 0a16811ab11fecc219c8361006eb1af984f5bdaf Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sun, 9 Aug 2026 03:31:40 +0900 Subject: [PATCH] w6c: preserve grouped and qualified types in wwi --- cmd/w6c/wwi.c | 21 ++++++++++++++++----- selfhost/cmd/wcc/wwi.ww | 42 ++++++++++++++++++++++++++++------------- test/byteid/wwi_test.ww | 42 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 18 deletions(-) diff --git a/cmd/w6c/wwi.c b/cmd/w6c/wwi.c index 2a0b5b04..17f6e1d9 100644 --- a/cmd/w6c/wwi.c +++ b/cmd/w6c/wwi.c @@ -45,11 +45,20 @@ static Sym * wwi_typesym(Checker *c, const char *nm) { if (nm == NULL) return NULL; - Sym *s = scope_lookup_type(c->cur, NULL, nm); - if (s == NULL) { - const char *dot = strrchr(nm, '.'); - if (dot) - s = scope_lookup_type(c->cur, NULL, dot + 1); + const char *dot = strrchr(nm, '.'); + Sym *s = NULL; + if (dot) { + size_t n = (size_t)(dot - nm); + for (Node *u = c->file->list; u; u = u->next) { + if (u->kind != N_USE || !wwi_primary(u) || u->str == NULL + || strlen(u->str) != n || strncmp(u->str, nm, n) != 0) + continue; + const char *mod = u->usepath ? u->usepath : u->str; + s = scope_lookup_in_module(c->cur, mod, dot + 1); + break; + } + } else { + s = scope_lookup_type(c->cur, NULL, nm); } if (s && s->kind == SK_TYPE) return s; @@ -338,9 +347,11 @@ wwi_expr(FILE *of, Node *e) * as the plain int 1114111). */ case N_RUNELIT: wwi_rune(of, e->uval); break; case N_BIN: + fputc('(', of); wwi_expr(of, e->lhs); fprintf(of, " %s ", tokname(e->op)); wwi_expr(of, e->rhs); + fputc(')', of); break; case N_UN: fputs(tokname(e->op), of); diff --git a/selfhost/cmd/wcc/wwi.ww b/selfhost/cmd/wcc/wwi.ww index f8341c7c..71cf73d8 100644 --- a/selfhost/cmd/wcc/wwi.ww +++ b/selfhost/cmd/wcc/wwi.ww @@ -74,20 +74,34 @@ fn wquote(fd: i32, s: str) void = { fn wwitypesym(c: *checker, nm: str) *syntax.sym = { let empty: str; - let s: *syntax.sym = syntax.scopelookuptype(c.cur, empty, nm); - if (s == nil) { - let dotidx: i32 = -1; - let i: i32 = 0; - for (i < nm.len) { - if (nm[i] == 46u8) { dotidx = i; }; - i += 1; - }; - if (dotidx >= 0) { - let leaf: str; - leaf.ptr = nm.ptr + ((dotidx + 1): u64); - leaf.len = nm.len - dotidx - 1; - s = syntax.scopelookuptype(c.cur, empty, leaf); + let dotidx: i32 = -1; + let i: i32 = 0; + for (i < nm.len) { + if (nm[i] == 46u8) { dotidx = i; }; + i += 1; + }; + let s: *syntax.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; + let u: *syntax.node = c.file.list; + for (u != nil) { + if (u.kind == syntax.nkind.N_USE && wwiprimary(u)) { + if (syntax.streq(u.str, head)) { + let mod: str = u.usepath; + if (mod.len == 0) { mod = u.str; }; + s = syntax.scopelookupinmodule(c.cur, mod, leaf); + break; + }; + }; + u = u.next; }; + } else { + s = syntax.scopelookuptype(c.cur, empty, nm); }; if (s == nil) { return nil; }; if (s.skind != syntax.skind.SK_TYPE) { return nil; }; @@ -279,11 +293,13 @@ fn wwiexpr(fd: i32, e: *syntax.node) void = { // `0x10ffff: rune` emits as the plain int 1114111). wwirune(fd, e.uval); } else { if (e.kind == syntax.nkind.N_BIN) { + wputb(fd, '('); wwiexpr(fd, e.lhs); wputb(fd, 32u8); wputs(fd, syntax.tokname(e.op)); wputb(fd, 32u8); wwiexpr(fd, e.rhs); + wputb(fd, ')'); } else { if (e.kind == syntax.nkind.N_UN) { wputs(fd, syntax.tokname(e.op)); wwiexpr(fd, e.lhs); diff --git a/test/byteid/wwi_test.ww b/test/byteid/wwi_test.ww index 954b6747..cc67db16 100644 --- a/test/byteid/wwi_test.ww +++ b/test/byteid/wwi_test.ww @@ -132,6 +132,7 @@ fn m2positive(pkg: str) void = { "export def NEG: i32 = -7;\n", "export let counter: i32;\n", "export let grid: [4]i32;\n", + "export let grouped: [(1 + 2) * 3]u8;\n", "export fn apply(f: fn(x: i32) i32, n: i32) i32;\n", "export fn risky() !i32;\n", "export fn matrix() [LIMIT]u8;\n", @@ -152,6 +153,10 @@ fn m2positive(pkg: str) void = { fail("synth", "cs.wwi != ww.wwi (const-expr / decl-kind unparse diverges)"); }; + if (!testenv.has(testenv.readfile(cs), + "export let grouped: [((1 + 2) * 3)]u8;")) { + fail("synth", ".wwi changed grouped array-dimension semantics"); + }; // #47: the @symbol attribute must survive the round-trip verbatim. if (!testenv.has(testenv.readfile(cs), "@symbol(\"rt_ext\") export fn ext")) { @@ -164,6 +169,43 @@ fn m2positive(pkg: str) void = { testenv.clean(td); }; +@test fn m2qualified() void = { + let td: str = testenv.fresh(); + let src: str = strings.concat( + "//ww:module a.dep\n", + "package dep;\n", + "export type Clash = struct { x: i32 };\n", + "//ww:module-reset\n", + "package root;\n", + "import a.dep;\n", + "type Clash = struct { y: i32 };\n", + "export fn use(x: dep.Clash) i32;\n"); + let p: str = strings.concat(td, "/qualified.ww"); + testenv.writefile(p, src); + let cs: str = strings.concat(td, "/cs.wwi"); + let ws: str = strings.concat(td, "/ww.wwi"); + let cav: []str = [testenv.driver("w6c"), "-I", cs, p]; + if (!runok(td, "w6c", cav)) { + fail("qualified", "w6c confused dep.Clash with private Clash"); + }; + let wav: []str = [testenv.driver("w6c_ww"), "-I", ws, p]; + if (!runok(td, "w6c_ww", wav)) { + fail("qualified", "w6c_ww confused dep.Clash with private Clash"); + }; + if (!testenv.same(testenv.readfile(cs), testenv.readfile(ws))) { + fail("qualified", "cs.wwi != ww.wwi"); + }; + if (!testenv.has(testenv.readfile(cs), + "export fn use(x: dep.Clash) i32;")) { + fail("qualified", ".wwi dropped the qualified signature"); + }; + let dav: []str = [testenv.driver("wwdump"), "-a", cs]; + if (!runok(td, "wwdump", dav)) { + fail("qualified", "emitted .wwi does not re-parse"); + }; + testenv.clean(td); +}; + // #48 gate: the lib/types limit constants are `export def`s. Produce // types.wwi on both stages directly from the real lib source, assert // byte-id + re-parse + that the exported limits actually appear (a