diff --git a/Makefile b/Makefile index 6ea54164..12fcd2cf 100644 --- a/Makefile +++ b/Makefile @@ -173,12 +173,12 @@ $(OBJ)/w6l/%.o: cmd/w6l/%.c cmd/w6l/l.h | $(OBJ)/w6l # ---- ww-side wwdump (the frontend dump tool; oracle for 950/994) ------- # Built via the user-facing ww driver. The frontend (lex, tok, ast, # parse, typ, sym) is the `syntax` package in lib/ww/syntax/; the -# compiler-only bits (check, cgen*) stay in selfhost/cmd/wcc/. Output named wwdump_ww -# to avoid colliding with the C-side wwdump in $(BIN). +# compiler-only bits are the `wcc` directory package in selfhost/cmd/wcc/. +# Output is named wwdump_ww to avoid colliding with the C-side wwdump. $(BIN)/wwdump_ww: selfhost/cmd/wwdump/main.ww \ lib/ww/syntax/lex.ww lib/ww/syntax/tok.ww lib/ww/syntax/ast.ww \ lib/ww/syntax/parse.ww lib/ww/syntax/expr.ww lib/ww/syntax/stmt.ww lib/ww/syntax/decl.ww lib/ww/syntax/typ.ww lib/ww/syntax/sym.ww \ - selfhost/cmd/wcc/check.ww \ + selfhost/cmd/wcc/api.ww selfhost/cmd/wcc/check.ww selfhost/cmd/wcc/wwi.ww \ selfhost/cmd/wcc/cgen.ww selfhost/cmd/wcc/cgenexpr.ww \ selfhost/cmd/wcc/cgenstmt.ww selfhost/cmd/wcc/cgenutil.ww \ selfhost/cmd/wcc/cgendecl.ww \ @@ -188,17 +188,18 @@ $(BIN)/wwdump_ww: selfhost/cmd/wwdump/main.ww \ @mkdir -p $(WWBUILD)/wwdump_ww @$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/wwdump_ww \ -o $(WWBUILD)/wwdump_ww/main \ - -I $(CURDIR)/lib/ww -I $(CURDIR)/selfhost/cmd/wcc \ + -I $(CURDIR)/lib/ww -I $(CURDIR)/selfhost/cmd \ $(CURDIR)/selfhost/cmd/wwdump/main.ww @mv $(WWBUILD)/wwdump_ww/main $@ # ---- ww-side w6c (compiler port, exercised by 994_w6c_ww) ------------- # Thin driver: parse + cgen. The frontend (lex/parse/ast/...) is the -# `syntax` package in lib/ww/syntax/; cgen + check live in selfhost/cmd/wcc/. +# `syntax` package in lib/ww/syntax/; cgen + check are behind the `wcc` +# directory package in selfhost/cmd/wcc/. $(BIN)/w6c_ww: selfhost/cmd/w6c/main.ww \ lib/ww/syntax/lex.ww lib/ww/syntax/tok.ww lib/ww/syntax/ast.ww \ lib/ww/syntax/parse.ww lib/ww/syntax/expr.ww lib/ww/syntax/stmt.ww lib/ww/syntax/decl.ww lib/ww/syntax/typ.ww lib/ww/syntax/sym.ww \ - selfhost/cmd/wcc/check.ww selfhost/cmd/wcc/wwi.ww \ + selfhost/cmd/wcc/api.ww selfhost/cmd/wcc/check.ww selfhost/cmd/wcc/wwi.ww \ selfhost/cmd/wcc/cgen.ww selfhost/cmd/wcc/cgenexpr.ww \ selfhost/cmd/wcc/cgenstmt.ww selfhost/cmd/wcc/cgenutil.ww \ selfhost/cmd/wcc/cgendecl.ww \ @@ -208,13 +209,13 @@ $(BIN)/w6c_ww: selfhost/cmd/w6c/main.ww \ @mkdir -p $(WWBUILD)/w6c_ww @$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6c_ww \ -o $(WWBUILD)/w6c_ww/main \ - -I $(CURDIR)/lib/ww -I $(CURDIR)/selfhost/cmd/wcc \ + -I $(CURDIR)/lib/ww -I $(CURDIR)/selfhost/cmd \ $(CURDIR)/selfhost/cmd/w6c/main.ww @mv $(WWBUILD)/w6c_ww/main $@ # ---- ww-side w6a (assembler port, exercised by 991_w6a_ww) ------------ -# Built like wwdump_ww. Needs -I selfhost/cmd/w6a for the local types/lex/ -# parse/asm/obj modules. +# The whole directory is one executable package; helper source files are not +# importable modules. $(BIN)/w6a_ww: selfhost/cmd/w6a/main.ww selfhost/cmd/w6a/opcodes.ww \ selfhost/cmd/w6a/lex.ww selfhost/cmd/w6a/parse.ww \ selfhost/cmd/w6a/asm.ww selfhost/cmd/w6a/obj.ww \ @@ -226,13 +227,12 @@ $(BIN)/w6a_ww: selfhost/cmd/w6a/main.ww selfhost/cmd/w6a/opcodes.ww \ @mkdir -p $(WWBUILD)/w6a_ww @$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6a_ww \ -o $(WWBUILD)/w6a_ww/main \ - -I $(CURDIR)/selfhost/cmd/w6a \ - $(CURDIR)/selfhost/cmd/w6a/main.ww + $(CURDIR)/selfhost/cmd/w6a @mv $(WWBUILD)/w6a_ww/main $@ # ---- ww-side w6l (linker port, exercised by 992_w6l_ww) --------------- -# Built like 6a_ww. Needs -I selfhost/cmd/w6l for the local sym/obj/pass/ -# out modules. +# The whole directory is one executable package; helper source files are not +# importable modules. $(BIN)/w6l_ww: selfhost/cmd/w6l/main.ww selfhost/cmd/w6l/sym.ww \ selfhost/cmd/w6l/obj.ww selfhost/cmd/w6l/dyn.ww \ selfhost/cmd/w6l/pass.ww selfhost/cmd/w6l/dynout.ww \ @@ -245,8 +245,7 @@ $(BIN)/w6l_ww: selfhost/cmd/w6l/main.ww selfhost/cmd/w6l/sym.ww \ @mkdir -p $(WWBUILD)/w6l_ww @$(CURDIR)/$(BIN)/ww build -w $(WWBUILD)/w6l_ww \ -o $(WWBUILD)/w6l_ww/main \ - -I $(CURDIR)/selfhost/cmd/w6l \ - $(CURDIR)/selfhost/cmd/w6l/main.ww + $(CURDIR)/selfhost/cmd/w6l @mv $(WWBUILD)/w6l_ww/main $@ # ---- ww-side ww driver (exercised by 993_ww_ww) ------------------------ @@ -794,19 +793,18 @@ test-incremental: $(BIN)/ww $(BIN)/ww_ww $(BIN)/w6c $(BIN)/w6c_ww \ $(LIB)/libwwrt.a @rm -rf $(INCR_DIR) @set -e; \ - src=$(CURDIR)/selfhost/cmd/w6a/main.ww; \ - inc=$(CURDIR)/selfhost/cmd/w6a; \ + src=$(CURDIR)/selfhost/cmd/w6a; \ for stage in cs ws; do \ drv=$(CURDIR)/$(BIN)/ww; \ if [ $$stage = ws ]; then drv=$(CURDIR)/$(BIN)/ww_ww; fi; \ w=$(INCR_DIR)/$$stage.w; mkdir -p $$w; \ - $$drv build -w $$w -o $(INCR_DIR)/$$stage.cold -I $$inc $$src; \ - $$drv build -w $$w -o $(INCR_DIR)/$$stage.warm -I $$inc $$src; \ + $$drv build -w $$w -o $(INCR_DIR)/$$stage.cold $$src; \ + $$drv build -w $$w -o $(INCR_DIR)/$$stage.warm $$src; \ rm $$w/strings.s $$w/strings.o $$w/strings.a; \ - $$drv build -w $$w -o $(INCR_DIR)/$$stage.surg -I $$inc $$src; \ + $$drv build -w $$w -o $(INCR_DIR)/$$stage.surg $$src; \ : > $$w/bytes.o; \ - $$drv build -w $$w -o $(INCR_DIR)/$$stage.heal -I $$inc $$src; \ - (cd $(INCR_DIR) && $$drv build -o $$stage.clean -I $$inc $$src); \ + $$drv build -w $$w -o $(INCR_DIR)/$$stage.heal $$src; \ + (cd $(INCR_DIR) && $$drv build -o $$stage.clean $$src); \ for v in cold warm surg heal; do \ cmp $(INCR_DIR)/$$stage.$$v $(INCR_DIR)/$$stage.clean || { \ echo "test-incremental: $$stage.$$v differs from clean" >&2; \ @@ -863,7 +861,7 @@ clean: # fixed, not merely a coincidental two-stage equilibrium. BS = $(OUT)/bootstrap W6CSRC = selfhost/cmd/w6c/main.ww -W6CINC = -I lib/ww -I selfhost/cmd/wcc +W6CINC = -I lib/ww -I selfhost/cmd bootstrap: all @echo "=== stage 0 (Cstage): C-built tools ===" @echo " $(BIN)/ww (sep orchestrator) $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l" @@ -961,14 +959,12 @@ nocc: @# Pass -I $(CURDIR)/lib so `use os` etc. resolve to source. @cd $(NOCC_BIN) && ./ww build -o main \ -I $(CURDIR)/lib/ww \ - -I $(CURDIR)/selfhost/cmd/wcc \ + -I $(CURDIR)/selfhost/cmd \ -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/w6c/main.ww && mv main w6c_ww1 - @cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/selfhost/cmd/w6a \ - -I $(CURDIR)/selfhost/cmd/wcc -I $(CURDIR)/lib \ - $(CURDIR)/selfhost/cmd/w6a/main.ww && mv main w6a_ww1 - @cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/selfhost/cmd/w6l \ - -I $(CURDIR)/selfhost/cmd/wcc -I $(CURDIR)/lib \ - $(CURDIR)/selfhost/cmd/w6l/main.ww && mv main w6l_ww1 + @cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/lib \ + $(CURDIR)/selfhost/cmd/w6a && mv main w6a_ww1 + @cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/lib \ + $(CURDIR)/selfhost/cmd/w6l && mv main w6l_ww1 @cd $(NOCC_BIN) && ./ww build -o main -I $(CURDIR)/selfhost/cmd/wcc \ -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/ww/main.ww && mv main ww_ww1 @echo "=== fixed-point gates: rebuilt tools == stage-0 binaries? ===" diff --git a/examples/lisp/CLAUDE.md b/examples/lisp/CLAUDE.md index 84bfd49f..8353782e 100644 --- a/examples/lisp/CLAUDE.md +++ b/examples/lisp/CLAUDE.md @@ -5,7 +5,7 @@ backend rather than the C bootstrap one. ## Layout -- `lispcore.ww` interpreter module: types, lexer, parser, env, +- `lispcore/` interpreter directory package: types, lexer, parser, env, eval, apply, printer, REPL. Everything the entry point and the test driver consume is `export`-ed. - `lisp.ww` entry point; `use lispcore;` + `main()`. @@ -15,12 +15,9 @@ backend rather than the C bootstrap one. runs the binary itself. - `test_*.lisp` demo source files (`cat test_X.lisp | ./lisp`). -The lispcore.ww + lisp.ww split exists so the tests can `use` the -interpreter functions. Both files combine under the same module name -(the directory's basename, `lisp`), because `ww`'s module resolver -takes the *containing-directory* basename when finding a sibling. -Cross-module type prefixes like `lispcore.value` don't resolve in the -test — use the bare names. +The lispcore/ + lisp.ww split exists so the entry point and tests import +one canonical interpreter directory package. Cross-module type prefixes +like `lispcore.value` don't resolve in the test — use the bare names. ## wwstage cgen workarounds at play diff --git a/examples/lisp/Makefile b/examples/lisp/Makefile index 2586e2fe..6a731a80 100644 --- a/examples/lisp/Makefile +++ b/examples/lisp/Makefile @@ -3,17 +3,14 @@ # # Layout # lisp.ww entry point. `use lispcore;` + `main()` = repl(). -# lispcore.ww interpreter module. Types/functions are `export`-ed +# lispcore/ interpreter package. Types/functions are `export`-ed # so both lisp.ww and lisp_test.ww consume them via # the same `use lispcore;`. # lisp_test.ww in-process test driver, executed by `make test` # (which invokes `ww test`). # -# `ww`'s module resolver uses the *directory's basename* as the module -# name when finding `lispcore.ww` through `-I `. Passing the -# absolute path of the current directory keeps that name stable -# (otherwise `-I.` collapses to ".", and the cgen mangles symbols as -# `..helper`, which then breaks the assembler). +# The absolute include root makes `import lispcore` resolve the canonical +# lispcore/ directory regardless of the caller's working directory. # Drive the wwstage cgen (w6c_ww) by default. The C-stage cgen has # unfixed silent-miscompilation traps that lispcore used to dodge by @@ -25,7 +22,7 @@ W6CWW := $(shell cd ../..; pwd)/out/bin/w6c_ww HERE := $(shell pwd) WWENV := WW_W6C=$(W6CWW) -lisp: lisp.ww lispcore.ww +lisp: lisp.ww lispcore/lispcore.ww $(WWENV) $(WW) build lisp.ww -I $(HERE) # `ww test ` in single-file mode discards extra args, so we @@ -34,7 +31,7 @@ lisp: lisp.ww lispcore.ww test: lisp_test lisp ./lisp_test -lisp_test: lisp_test.ww lispcore.ww +lisp_test: lisp_test.ww lispcore/lispcore.ww $(WWENV) $(WW) build lisp_test.ww -I $(HERE) # Demo programs in tree. `make demo` runs every test_*.lisp through diff --git a/examples/lisp/lisp.ww b/examples/lisp/lisp.ww index 9a9bffb9..2c241044 100644 --- a/examples/lisp/lisp.ww +++ b/examples/lisp/lisp.ww @@ -1,4 +1,4 @@ -// lisp — entry point. The interpreter lives in `lispcore.ww`; this +// lisp — entry point. The interpreter lives in the `lispcore` package; this // file only wires it to `main()` so `ww build lisp.ww` produces the // REPL binary. The matching tests live in `lisp_test.ww` and pull // the same module via `use lispcore;`. diff --git a/examples/lisp/lispcore.ww b/examples/lisp/lispcore/lispcore.ww similarity index 99% rename from examples/lisp/lispcore.ww rename to examples/lisp/lispcore/lispcore.ww index 0d23e74a..fe869837 100644 --- a/examples/lisp/lispcore.ww +++ b/examples/lisp/lispcore/lispcore.ww @@ -59,7 +59,7 @@ // - `acc /= d` / `acc += f` on f64 locals lower to `acc = d` (drop // the OP). Write the explicit form `acc = acc OP d`. -package lisp; +package lispcore; import os; import rt; @@ -1784,4 +1784,3 @@ export fn repl() i32 = { return 0; }; - diff --git a/lib/math/checked/checked_test.ww b/lib/math/checked/checked_test.ww index 4d7f0c63..dfb4a7f8 100644 --- a/lib/math/checked/checked_test.ww +++ b/lib/math/checked/checked_test.ww @@ -1,8 +1,8 @@ package checked_test; -// Directory import pulls both checked.ww and saturating.ww (the two -// files of this module); a bare `import checked` would resolve only to -// the checked.ww file. Referenced as `checked.*` (last path component). +// The canonical directory import pulls both checked.ww and saturating.ww; +// imported paths never select one source file. Referenced as `checked.*` +// (last path component). import math.checked; import types; diff --git a/lib/math/random/random_test.ww b/lib/math/random/random_test.ww index 0996cb2f..16cc7fb0 100644 --- a/lib/math/random/random_test.ww +++ b/lib/math/random/random_test.ww @@ -1,6 +1,6 @@ package random_test; -import random; +import math.random; import test; @test fn seq() void = { diff --git a/selfhost/cmd/w6a/asm.ww b/selfhost/cmd/w6a/asm.ww index a0c990d7..15003b99 100644 --- a/selfhost/cmd/w6a/asm.ww +++ b/selfhost/cmd/w6a/asm.ww @@ -1,11 +1,10 @@ // Port of cmd/w6a/asm.c. Encoding subset matches what w6c emits — see // cmd/w6a/asm.c for the authoritative list. -package w6a; +package main; import os; import rt; -import opcodes; import strings; export fn emitbyte(a: *asm_, b: u8) void = { diff --git a/selfhost/cmd/w6a/lex.ww b/selfhost/cmd/w6a/lex.ww index 2cfc9c4e..f7e9216d 100644 --- a/selfhost/cmd/w6a/lex.ww +++ b/selfhost/cmd/w6a/lex.ww @@ -1,6 +1,6 @@ // Port of cmd/w6a/lex.c. -package w6a; +package main; export fn isidstart(c: i32) bool = { if (c == 95) { return true; }; diff --git a/selfhost/cmd/w6a/main.ww b/selfhost/cmd/w6a/main.ww index 6036244a..04fd6b84 100644 --- a/selfhost/cmd/w6a/main.ww +++ b/selfhost/cmd/w6a/main.ww @@ -7,11 +7,6 @@ package main; import os; import rt; import strings; -import opcodes; -import lex; -import parse; -import asm; -import obj; fn cstreq(a: *u8, lit: str) bool = { let n: u64 = lit.len: u64; diff --git a/selfhost/cmd/w6a/obj.ww b/selfhost/cmd/w6a/obj.ww index 7f504cfd..ef11623a 100644 --- a/selfhost/cmd/w6a/obj.ww +++ b/selfhost/cmd/w6a/obj.ww @@ -11,10 +11,9 @@ // // Symtab indices: 0 = STN_UNDEF, 1.. = our syms. Only GLOBAL symbols. -package w6a; +package main; import os; -import opcodes; // Local wrappers around os.writeall's tagged return — collapse the // (i64 | oserror) back to a boolean / int sentinel for the diff --git a/selfhost/cmd/w6a/opcodes.ww b/selfhost/cmd/w6a/opcodes.ww index 950bd0cd..939a7a62 100644 --- a/selfhost/cmd/w6a/opcodes.ww +++ b/selfhost/cmd/w6a/opcodes.ww @@ -1,6 +1,6 @@ // Mirrors cmd/w6a/a.h and cmd/w6c/6.out.h. -package w6a; +package main; // Registers + operand kinds must stay numerically aligned with the // 6.out.h C enum so that ww-cgen output (which reads them via diff --git a/selfhost/cmd/w6a/parse.ww b/selfhost/cmd/w6a/parse.ww index 41caf4cd..663df0ea 100644 --- a/selfhost/cmd/w6a/parse.ww +++ b/selfhost/cmd/w6a/parse.ww @@ -10,12 +10,10 @@ // instr := \tMNEM\t[OP1[, OP2]] // OP := $NUM | REG | NUM(REG) | (REG) | name(SB) | label -package w6a; +package main; import os; import strings; -import lex; -import opcodes; fn streqlit(p: *u8, n: u64, lit: str) bool = { if (n != lit.len: u64) { return false; }; diff --git a/selfhost/cmd/w6c/main.ww b/selfhost/cmd/w6c/main.ww index 0194ef95..840949bf 100644 --- a/selfhost/cmd/w6c/main.ww +++ b/selfhost/cmd/w6c/main.ww @@ -6,9 +6,7 @@ import os; import rt; import strings; import syntax; -import check; -import cgen; -import wwi; +import wcc; fn cstreq(a: *u8, lit: str) bool = { let n: u64 = lit.len: u64; @@ -178,36 +176,9 @@ export fn main(argc: i32, argv: **u8) i32 = { // cgen and emits junk asm with a zero exit (silent miscompile). if (l.errs > 0 || ps.errs > 0) { return 1; }; - // #50: run check before cgen so AST mutations from #42 (size/align/ - // offset fold) and the audit §1.8 node.type_ population land before - // cgen walks the file. Mirrors cmd/w6c/main.c:73-75. Five precondition - // fixes for fixture cleanliness: #51 cross-module type refs, #52 - // enum↔int reinterpret, #53 N_BLOCK scoping, #55 nominal-first variant - // compare, #56 bare-leaf same-module preference. - let tc: tctx; - typesinit(&tc); - let ck: checker; - checkinit(&ck, &tc); - ck.istest = testmode; - if (testsupport != nil) { ck.testmodule = pathstr(testsupport); }; - ck.sepmode = sepmode; - checkfile(&ck, f); - if (ck.errs > 0) { return 1; }; - - // M2 export-data: write the `.wwi` after a clean check, before cgen. - // Dead on the live path (no existing invocation passes -I); the - // producer's check_exported_type may reject a dangling export. - if (wwiout != nil) { - if (wwiemit(&ck, f, pathstr(wwiout)) != 0) { return 1; }; - }; - - let cg: cgen; - cgeninit(&cg); - cg.sepmode = sepmode; - // #99: wwiout != nil <=> this is a sep DEP unit (the producer passes - // -I to deps only; the root's .wwi is stripped per #69). Gates the - // bare-`main` carve-out so only the root/link-entry main stays bare. - if (wwiout != nil) { cg.sepisdep = 1i32; }; - cgfile(&cg, f); - return 0; + let testmodule: str; + if (testsupport != nil) { testmodule = pathstr(testsupport); }; + let interfaceout: str; + if (wwiout != nil) { interfaceout = pathstr(wwiout); }; + return wcc.compilefile(f, testmode, testmodule, sepmode, interfaceout); }; diff --git a/selfhost/cmd/w6l/dyn.ww b/selfhost/cmd/w6l/dyn.ww index c3a24e81..27380542 100644 --- a/selfhost/cmd/w6l/dyn.ww +++ b/selfhost/cmd/w6l/dyn.ww @@ -4,12 +4,11 @@ // exports and which DT_NEEDED entry to record. We do not pull bytes // from the .so; the dynamic loader maps it at runtime. -package w6l; +package main; import os; import rt; import strings; -import sym; def ET_DYN_SO: u16 = 3u16; def EM_X86_64_SO: u16 = 62u16; diff --git a/selfhost/cmd/w6l/dynout.ww b/selfhost/cmd/w6l/dynout.ww index e565f01d..16178794 100644 --- a/selfhost/cmd/w6l/dynout.ww +++ b/selfhost/cmd/w6l/dynout.ww @@ -22,12 +22,11 @@ // [gotplt_off] .got.plt (writable; mapped by PT_LOAD #2) // [dynamic_off] .dynamic (writable; covered by PT_DYNAMIC) -package w6l; +package main; import os; import rt; import strings; -import sym; def ET_EXEC_D: u16 = 2u16; def EM_X86_64_D: u16 = 62u16; diff --git a/selfhost/cmd/w6l/main.ww b/selfhost/cmd/w6l/main.ww index 11906d49..aaeb92aa 100644 --- a/selfhost/cmd/w6l/main.ww +++ b/selfhost/cmd/w6l/main.ww @@ -7,11 +7,6 @@ package main; import os; import rt; import strings; -import sym; -import obj; -import dyn; -import pass; -import out; def BASE: u64 = 4194304u64; // 0x400000 def CODE_VA_OFF: u64 = 4096u64; // .text starts at base + 0x1000 diff --git a/selfhost/cmd/w6l/obj.ww b/selfhost/cmd/w6l/obj.ww index 735eb665..2258d20c 100644 --- a/selfhost/cmd/w6l/obj.ww +++ b/selfhost/cmd/w6l/obj.ww @@ -1,11 +1,10 @@ // Port of cmd/w6l/obj.c. -package w6l; +package main; import os; import rt; import strings; -import sym; def ET_REL: i32 = 1; def EM_X86_64: i32 = 62; diff --git a/selfhost/cmd/w6l/out.ww b/selfhost/cmd/w6l/out.ww index 96f9a317..dc9a57fb 100644 --- a/selfhost/cmd/w6l/out.ww +++ b/selfhost/cmd/w6l/out.ww @@ -1,11 +1,9 @@ // Port of cmd/w6l/out.c. -package w6l; +package main; import os; import rt; -import sym; -import dynout; def ET_EXEC: u16 = 2u16; def EM_X86_64_W: u16 = 62u16; diff --git a/selfhost/cmd/w6l/pass.ww b/selfhost/cmd/w6l/pass.ww index 493248a8..ffc79832 100644 --- a/selfhost/cmd/w6l/pass.ww +++ b/selfhost/cmd/w6l/pass.ww @@ -3,11 +3,9 @@ // Supported relocation kinds: PC32 (=2), PLT32 (=4); both are 32-bit // PC-relative displacements (PLT32 == PC32 for static). -package w6l; +package main; import os; -import sym; -import dyn; def R_X86_64_64: i32 = 1; def R_X86_64_PC32: i32 = 2; diff --git a/selfhost/cmd/w6l/sym.ww b/selfhost/cmd/w6l/sym.ww index 36d4fcad..66fae98f 100644 --- a/selfhost/cmd/w6l/sym.ww +++ b/selfhost/cmd/w6l/sym.ww @@ -3,7 +3,7 @@ // Singly-linked list, usually a few hundred entries; hashing isn't // worth it yet. -package w6l; +package main; import strings; diff --git a/selfhost/cmd/wcc/api.ww b/selfhost/cmd/wcc/api.ww new file mode 100644 index 00000000..0d39b17e --- /dev/null +++ b/selfhost/cmd/wcc/api.ww @@ -0,0 +1,40 @@ +package wcc; + +import syntax; + +export fn compilefile(file: *syntax.node, testmode: i32, testmodule: str, + sepmode: i32, wwiout: str) i32 = { + let tc: syntax.tctx; + syntax.typesinit(&tc); + let ck: checker; + checkinit(&ck, &tc); + ck.istest = testmode; + if (testmodule.len > 0) { ck.testmodule = testmodule; }; + ck.sepmode = sepmode; + checkfile(&ck, file); + if (ck.errs > 0) { return 1; }; + if (wwiout.len > 0) { + if (wwiemit(&ck, file, wwiout) != 0) { return 1; }; + }; + + let cg: cgen; + cgeninit(&cg); + cg.sepmode = sepmode; + if (wwiout.len > 0) { cg.sepisdep = 1i32; }; + cgfile(&cg, file); + return 0; +}; + +export fn resolvefile(file: *syntax.node, verbose: i32, + nresolved: *i32, nunresolved: *i32) i32 = { + let tc: syntax.tctx; + syntax.typesinit(&tc); + let ck: checker; + checkinit(&ck, &tc); + ck.verbose = verbose; + checkfile(&ck, file); + *nresolved = ck.nresolved; + *nunresolved = ck.nunresolved; + if (ck.errs > 0 || ck.nunresolved > 0) { return 1; }; + return 0; +}; diff --git a/selfhost/cmd/wcc/cgen.ww b/selfhost/cmd/wcc/cgen.ww index bc054c8e..e369fd7a 100644 --- a/selfhost/cmd/wcc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -11,11 +11,6 @@ import memio; // Split files. Bundler pulls these in transitively so consumers only // need `use cgen;`. Order matters for the flat-bundle concat — utils // first so cgenexpr/stmt/decl can reference helpers defined here. -import cgenutil; -import cgenexpr; -import cgenstmt; -import cgendecl; - // Only direct nkind.N_TNAME aliases are mapped; `type p = struct {...}` // is handled by collectstructs. @@ -1419,7 +1414,7 @@ fn preinternstrarray(c: *cgen, au: *syntax.tinfo, r: *syntax.node) void = { // emitdatasection emits the DATA row in the same .s file. Running // emitletdataw after emitdatasection would flip the (DATA strlits, // DATAW lets) section order and break byte-identity. -export fn letpreintern(c: *cgen, file: *syntax.node) void = { +fn letpreintern(c: *cgen, file: *syntax.node) void = { if (file == nil) { return; }; // #49: strlit labels allocated here (static-data initialisers) take // the OWNING decl's module prefix, not the stale last-fn curmod. @@ -4033,7 +4028,7 @@ fn argregname(i: i32) str = { // fargregname — XMM scalar-float arg registers (SysV: X0..X7). // Parallel to argregname / sysv_argregs; float args advance their // own counter so int and float arg slots don't conflict. -export fn fargregname(i: i32) str = { +fn fargregname(i: i32) str = { if (i == 0) { return "X0"; }; if (i == 1) { return "X1"; }; if (i == 2) { return "X2"; }; diff --git a/selfhost/cmd/wcc/cgendecl.ww b/selfhost/cmd/wcc/cgendecl.ww index 885da180..ea5937aa 100644 --- a/selfhost/cmd/wcc/cgendecl.ww +++ b/selfhost/cmd/wcc/cgendecl.ww @@ -612,7 +612,7 @@ fn cgfn(c: *cgen, fn_: *syntax.node) void = { cgout_flush(); }; -export fn cgfile(c: *cgen, file: *syntax.node) void = { +fn cgfile(c: *cgen, file: *syntax.node) void = { if (file == nil) { return; }; c.strlits = nil; c.strlitseq = 0; diff --git a/selfhost/cmd/wcc/cgenstmt.ww b/selfhost/cmd/wcc/cgenstmt.ww index ad294373..61ed82bd 100644 --- a/selfhost/cmd/wcc/cgenstmt.ww +++ b/selfhost/cmd/wcc/cgenstmt.ww @@ -133,7 +133,7 @@ fn tupsse(i: i32) str = { // predicates this absorbs were the #22 neighbor-slot/zeros miscompile. // Checker twin: check.ww tupleelemslot / check.c N_TTUPLE; cstage twin: // tuple_eslot (cmd/w6c/cgen.c). -export fn tupeslot(ti: *syntax.tinfo) i32 = { +fn tupeslot(ti: *syntax.tinfo) i32 = { let t: *syntax.tinfo = ti; t = tichase(t); if (t == nil) { return 8; }; @@ -148,7 +148,7 @@ export fn tupeslot(ti: *syntax.tinfo) i32 = { return 8; }; -export fn tupeslotn(n: *syntax.node) i32 = { +fn tupeslotn(n: *syntax.node) i32 = { if (n == nil) { return 8; }; return tupeslot(n.type_: *syntax.tinfo); }; diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index a10dda8a..e85846fa 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -1461,7 +1461,7 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool, // route a tagged-return call result through the AX/DX/CX/R8 high→low // push convention rather than the concrete-variant widening path // (which drops DX/CX/R8). See task #21. -export fn taggedcallslot(c: *cgen, n: *syntax.node) i32 = { +fn taggedcallslot(c: *cgen, n: *syntax.node) i32 = { if (n == nil) { return 0; }; if (n.kind != syntax.nkind.N_CALL) { return 0; }; // Stamped result type, never a callee-name lookup (#209/#211 @@ -1913,7 +1913,7 @@ fn storeopsz(sz: i32) str = { // reports, with TBANG / TENUM / TNAME-alias chains pre-folded by // tinfofornode (check.ww:1102-1153 TNAME, 1154-1161 TBANG, // 1196-1208 TENUM). -export fn localloadop(c: *cgen, tnode: *syntax.node) str = { +fn localloadop(c: *cgen, tnode: *syntax.node) str = { if (tnode == nil) { return "MOVQ"; }; let ti: *syntax.tinfo = tnode.type_: *syntax.tinfo; if (ti == nil) { return "MOVQ"; }; @@ -2298,7 +2298,7 @@ fn structabisizetn(t: *syntax.tinfo) i32 = { // cgdot / cgassign at every "field-walk on a struct-typed local" // site so a transitively-aliased struct name resolves to its // fieldinfo list regardless of chain depth. -export fn structlookupchain(c: *cgen, tn: *syntax.node) *structinfo = { +fn structlookupchain(c: *cgen, tn: *syntax.node) *structinfo = { if (tn == nil) { return nil; }; // #92: a struct LITERAL's type ref parses as N_IDENT (expression // position, lib/ww/parse/expr.ww) where type specs parse N_TNAME @@ -2325,7 +2325,7 @@ export fn structlookupchain(c: *cgen, tn: *syntax.node) *structinfo = { return si; }; -export fn sretretsize(c: *cgen, t: *syntax.node) i32 = { +fn sretretsize(c: *cgen, t: *syntax.node) i32 = { if (t == nil) { return 0; }; let r: *syntax.node = t; if (r.kind == syntax.nkind.N_TBANG) { @@ -2490,7 +2490,7 @@ fn sretretsizetn(c: *cgen, t: *syntax.tinfo) i32 = { // > 24B, return its natural size; else 0. Wraps sretretsize over the // callee's resolved return type, used by cglet / cgassign receive // sites and cgcall to detect sret at the receive / emit boundaries. -export fn callsretsize(c: *cgen, n: *syntax.node) i32 = { +fn callsretsize(c: *cgen, n: *syntax.node) i32 = { if (n == nil) { return 0; }; if (n.kind != syntax.nkind.N_CALL) { return 0; }; let callee: *syntax.node = n.lhs; @@ -2666,7 +2666,7 @@ fn aliasprimsize(c: *cgen, nm: str) i32 = { // enum, etc.). Mirrors cstage's `type_isint(t) ? t->size : 0` / // `type_isunsigned` recursion through TY_NAMED and TY_ENUM. Used by // cgcast's identity-width identity-sign clamp-skip predicate (#33). -export fn typenodeprimresolved(c: *cgen, t: *syntax.node, +fn typenodeprimresolved(c: *cgen, t: *syntax.node, sz_out: *i32, unsigned_out: *bool) void = { *sz_out = 0; *unsigned_out = false; @@ -2712,7 +2712,7 @@ export fn typenodeprimresolved(c: *cgen, t: *syntax.node, // caller treats sz=0 as "not identity", which conservatively keeps // the clamp. Mirror of cstage's `n->lhs->type` lookup with the same // TY_NAMED / TY_ENUM recursion through type_isint / type_isunsigned. -export fn exprprimresolved(c: *cgen, n: *syntax.node, +fn exprprimresolved(c: *cgen, n: *syntax.node, sz_out: *i32, unsigned_out: *bool) void = { *sz_out = 0; *unsigned_out = false; @@ -2885,7 +2885,7 @@ fn inferletcalltype(c: *cgen, rhs: *syntax.node) *syntax.node = { // not the default 8B. Without this, the AX:DX:CX spill in cglet's // tagged-init branch writes past the local and tramples the next // slot. -export fn letslotsize(c: *cgen, n: *syntax.node) i32 = { +fn letslotsize(c: *cgen, n: *syntax.node) i32 = { // `[_]T = arrlit;` inferred-length arrays no longer need a slot-size // intercept here: the checker (inferarraylen, check.ww) stamps the // real element count onto the array type's length child before cgen @@ -3157,7 +3157,7 @@ fn isslicetype(c: *cgen, t: *syntax.node) bool = { // `(invalid | overflow)` node. Use at sites that read variant lists // or detect nullable folding off a scrutinee — cgmatch, cgtypetest, // cgtypeassert — so aliased `!(A|B)` shapes still dispatch. -export fn resolvetagged(c: *cgen, t: *syntax.node) *syntax.node = { +fn resolvetagged(c: *cgen, t: *syntax.node) *syntax.node = { let r: *syntax.node = resolvetype(c, t); if (r == nil) { return nil; }; if (r.kind == syntax.nkind.N_TBANG) { @@ -3546,7 +3546,7 @@ fn tnodeisagg(t: *syntax.node) bool = { // cgen.c:117 `cg_isfloat`. Dispatches MOVSS/MOVSD-shaped paths across // cglet, cgident, cgassign, cgbin, cgcast, cgcall, cgreturn, fn- // prologue. Collapsed per A.6.3b (#46). -export fn isfloattype(c: *cgen, t: *syntax.node) bool = { +fn isfloattype(c: *cgen, t: *syntax.node) bool = { if (t == nil) { return false; }; return syntax.typeisfloat(t.type_: *syntax.tinfo); }; @@ -3554,7 +3554,7 @@ export fn isfloattype(c: *cgen, t: *syntax.node) bool = { // isf32type — narrower: true only for f32 (after alias chase). Cite // cstage cgen.c:188 `type_isf32`. Picks MOVSS vs MOVSD and the SS- // variant arithmetic / cast opcodes. Collapsed per A.6.3b (#46). -export fn isf32type(c: *cgen, t: *syntax.node) bool = { +fn isf32type(c: *cgen, t: *syntax.node) bool = { if (t == nil) { return false; }; return syntax.typeisf32(t.type_: *syntax.tinfo); }; @@ -3563,7 +3563,7 @@ export fn isf32type(c: *cgen, t: *syntax.node) bool = { // `(*T | null)` semantics. Cite cstage cgen.c:396 `type_isnullable`; // the .nullable flag lands on tinfo at check.ww:1309-1318 when the // two-variant shape matches. Collapsed per A.6.3b (#46). -export fn isnullabletype(t: *syntax.node) bool = { +fn isnullabletype(t: *syntax.node) bool = { if (t == nil) { return false; }; return syntax.typeisnullable(t.type_: *syntax.tinfo); }; @@ -3573,7 +3573,7 @@ export fn isnullabletype(t: *syntax.node) bool = { // scan ti.params, strip TY_NAMED on each variant, return idx of first // TY_PTR. Phase 1 (26724fe) populated the chain in tinfofornode's // TTAGGED arm so this walk could retire the AST-keyed predecessor. -export fn nullableptrtag(t: *syntax.node) i32 = { +fn nullableptrtag(t: *syntax.node) i32 = { if (t == nil) { return 0; }; let ti: *syntax.tinfo = t.type_: *syntax.tinfo; if (ti == nil) { return 0; }; @@ -5394,7 +5394,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *syntax.tinfo, src: *syntax.node, slot_of // offset), so this is offset-preserving. Leaf out-param is the field's // stamped *tinfo (was *fieldinfo); the str/slice pseudo-leaf leaves it // nil and the callers gate on slicedelta>=0 first. -export fn dotchainresolve(c: *cgen, n: *syntax.node, +fn dotchainresolve(c: *cgen, n: *syntax.node, outrootname: *str, outrootoff: *i32, outtotaloff: *i32, outleaftype: **syntax.tinfo, outslicedelta: *i32, outisglobal: *bool, outptrroot: *bool) bool = { diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 67869e5d..d32e3fe2 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -7473,7 +7473,7 @@ fn asserttyped(c: *checker, n: *syntax.node, indot: bool) void = { }; }; -export fn checkinit(c: *checker, tc: *syntax.tctx) void = { +fn checkinit(c: *checker, tc: *syntax.tctx) void = { c.tc = tc; c.top = syntax.newscope(nil); c.cur = c.top; @@ -7493,7 +7493,7 @@ export fn checkinit(c: *checker, tc: *syntax.tctx) void = { seedprimitives(c); }; -export fn checkfile(c: *checker, file: *syntax.node) void = { +fn checkfile(c: *checker, file: *syntax.node) void = { if (file == nil) { return; }; if (file.kind != syntax.nkind.N_FILE) { return; }; c.file = file; diff --git a/selfhost/cmd/wcc/wwi.ww b/selfhost/cmd/wcc/wwi.ww index 334f87dc..91609ae0 100644 --- a/selfhost/cmd/wcc/wwi.ww +++ b/selfhost/cmd/wcc/wwi.ww @@ -903,7 +903,7 @@ fn wwiemitfactimports(fd: i32, file: *syntax.node, owner: str) void = { }; }; -export fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = { +fn wwiemit(c: *checker, file: *syntax.node, path: str) i32 = { // §5: check_exported_type FIRST, before any byte — a producer // without it can emit a dangling `.wwi`. let bad: i32 = 0; diff --git a/selfhost/cmd/wwdump/main.ww b/selfhost/cmd/wwdump/main.ww index df898541..1a5686c5 100644 --- a/selfhost/cmd/wwdump/main.ww +++ b/selfhost/cmd/wwdump/main.ww @@ -6,8 +6,7 @@ package main; import os; import syntax; -import check; -import cgen; +import wcc; import strconv; // argv strings are always NUL-terminated (kernel-supplied), so the @@ -124,24 +123,20 @@ export fn main(argc: i32, argv: **u8) i32 = { // and the report is printed with rc=0. Mirrors w6c main.ww:162 // / cmd/w6c/main.c. if (l.errs > 0 || ps.errs > 0) { return 1; }; - let tc: tctx; - typesinit(&tc); - let ck: checker; - checkinit(&ck, &tc); - // Quiet by default; flip to 1 when debugging missing names. - ck.verbose = 0; - checkfile(&ck, f); + let nresolved: i32 = 0; + let nunresolved: i32 = 0; + let checkrc: i32 = wcc.resolvefile(f, 0, &nresolved, &nunresolved); // ": / resolved" os.write(1, argstr(path).ptr, argstrlen(path): u64); os.write(1, ": ".ptr, 2u64); - let rs: str = strconv.i64tos(ck.nresolved: i64, strconv.base.DEC); + let rs: str = strconv.i64tos(nresolved: i64, strconv.base.DEC); os.write(1, rs.ptr, rs.len: u64); os.write(1, "/".ptr, 1u64); - let total: i32 = ck.nresolved + ck.nunresolved; + let total: i32 = nresolved + nunresolved; let ts: str = strconv.i64tos(total: i64, strconv.base.DEC); os.write(1, ts.ptr, ts.len: u64); os.write(1, " resolved\n".ptr, 10u64); - if (ck.nunresolved > 0) { return 1; }; + if (checkrc != 0) { return 1; }; } else { if (mode == 99) { // '-c' — codegen / emit asm let ps: parser; parserinit(&ps, &l); @@ -152,20 +147,8 @@ export fn main(argc: i32, argv: **u8) i32 = { // miscompile, and the 994 byte-identity probe ships wrong asm // silently). Mirrors w6c main.ww:162 / cmd/w6c/main.c. if (l.errs > 0 || ps.errs > 0) { return 1; }; - // #50: mirror w6c — run check before cgen so AST mutations - // from #42 (size/align/offset fold) and audit §1.8 (node.type_ - // population) land before cgen walks. Without this, wwdump -c - // (the byte-identity probe for 994) would diverge from w6c_ww - // on any program that uses the size/align/offset typed builtins. - let tc: tctx; - typesinit(&tc); - let ck: checker; - checkinit(&ck, &tc); - checkfile(&ck, f); - if (ck.errs > 0) { return 1; }; - let cg: cgen; - cgeninit(&cg); - cgfile(&cg, f); + let empty: str; + if (wcc.compilefile(f, 0, empty, 0, empty) != 0) { return 1; }; };};};}; if (l.errs > 0) { return 1; }; diff --git a/test/wcc/991_w6a_ww.c b/test/wcc/991_w6a_ww.c index e15019ea..cd533980 100644 --- a/test/wcc/991_w6a_ww.c +++ b/test/wcc/991_w6a_ww.c @@ -112,13 +112,11 @@ main(void) if (getcwd(cwd, sizeof cwd) == NULL) return 1; /* #93 sep layout: the selfhost tools no longer leave a single - * next-to-source main.s. Self-build one real tool via the explicit - * main.ww compatibility root and feed EVERY emitted + * next-to-source main.s. Self-build one real directory package and + * feed EVERY emitted * .sepwork/.s as * the generated-asm corpus — the root tool code plus each dep - * package's .s (richer than the old 3 monolithic main.s). The directory - * intentionally contains package main plus legacy file-import packages, - * so it is not a directory-package root. */ + * package's .s (richer than the old 3 monolithic main.s). */ char stem[256], cmd[4096]; int fail = 0; int n = 0; @@ -126,11 +124,11 @@ main(void) snprintf(stem, sizeof stem, "/tmp/wwa_%d_sh", getpid()); snprintf(cmd, sizeof cmd, "timeout 400 %s/ww build -o %s " - "%s/selfhost/cmd/w6a/main.ww >/dev/null 2>&1", + "%s/selfhost/cmd/w6a >/dev/null 2>&1", bin, stem, cwd); if (runwait(cmd) != 0) { fprintf(stderr, - "w6a_ww FAIL: cannot sep-build selfhost/cmd/w6a/main.ww\n"); + "w6a_ww FAIL: cannot sep-build selfhost/cmd/w6a\n"); /* a failed build can leave a partial stem/.sepwork tree. */ fail = 1; goto cleanup; diff --git a/test/wcc/992_w6l_ww.c b/test/wcc/992_w6l_ww.c index 904b89e3..369f844f 100644 --- a/test/wcc/992_w6l_ww.c +++ b/test/wcc/992_w6l_ww.c @@ -57,9 +57,8 @@ slurp(const char *path, char **outbuf, size_t *outlen) * the linkable unit is now the per-package .o + reverse-topo .a set the * sep driver assembles (a raw `w6l .o *.a libwwrt.a` from the test * side fails — `undefined reference` — because it can't reproduce the - * driver's topo order). The tools are legacy inline-package directories, - * so their explicit main.ww files remain the compatibility roots. Drive - * the full sep build twice; both compile directly and differ only in the + * driver's topo order). Drive each real directory package through the + * full sep build twice; both compile directly and differ only in the * linker: once with the default C w6l, once with WW_W6L=w6l_ww. Diff * the two real tool binaries. Exercises the full archive two-pass loader * + reverse-topo .a resolution — a stronger link than the old single @@ -77,7 +76,7 @@ build_and_diff(const char *bin, const char *cwd, const char *tool, int id) snprintf(cmd, sizeof cmd, "timeout 300 %s/ww build -o %s " - "%s/selfhost/cmd/%s/main.ww >/dev/null 2>&1", + "%s/selfhost/cmd/%s >/dev/null 2>&1", bin, cstem, cwd, tool); if (runwait(cmd) != 0) { fprintf(stderr, "w6l_ww FAIL: C-link sep-build of %s\n", tool); @@ -86,7 +85,7 @@ build_and_diff(const char *bin, const char *cwd, const char *tool, int id) } snprintf(cmd, sizeof cmd, "WW_W6L=%s/w6l_ww timeout 300 %s/ww build " - "-o %s %s/selfhost/cmd/%s/main.ww >/dev/null 2>&1", + "-o %s %s/selfhost/cmd/%s >/dev/null 2>&1", bin, bin, wstem, cwd, tool); if (runwait(cmd) != 0) { fprintf(stderr, "w6l_ww FAIL: ww-link sep-build of %s\n", tool); diff --git a/test/wcc/993_ww_ww.c b/test/wcc/993_ww_ww.c index 871b3730..a2618aae 100644 --- a/test/wcc/993_ww_ww.c +++ b/test/wcc/993_ww_ww.c @@ -294,7 +294,7 @@ main(void) static char wwdump_src[2048], wwdump_incs[4096]; snprintf(wwdump_src, sizeof wwdump_src, "%s/selfhost/cmd/wwdump/main.ww", cwd); snprintf(wwdump_incs, sizeof wwdump_incs, - "%s/lib/ww:%s/lib/encoding/utf8:%s/selfhost/cmd/wcc", + "%s/lib/ww:%s/lib/encoding/utf8:%s/selfhost/cmd", cwd, cwd, cwd); cases[1].src = wwdump_src; cases[1].incs = wwdump_incs; diff --git a/test/wcc/994_w6c_ww.c b/test/wcc/994_w6c_ww.c index d45c9d6c..19478b49 100644 --- a/test/wcc/994_w6c_ww.c +++ b/test/wcc/994_w6c_ww.c @@ -361,7 +361,7 @@ main(void) { char inc[4096], cmd[16384]; snprintf(inc, sizeof inc, - "-I %s/lib -I %s/lib/ww -I %s/selfhost/cmd/wcc", cwd, cwd, cwd); + "-I %s/lib -I %s/lib/ww -I %s/selfhost/cmd", cwd, cwd, cwd); char root[2048]; snprintf(root, sizeof root, "%s/selfhost/cmd/w6c/main.ww", cwd); diff --git a/test/wcc/995_self_rebuild.c b/test/wcc/995_self_rebuild.c index d2492410..6f6e4ca3 100644 --- a/test/wcc/995_self_rebuild.c +++ b/test/wcc/995_self_rebuild.c @@ -63,18 +63,15 @@ slurp_eq(const char *a, const char *b) return rc; } -/* Each tool builds via `ww_ww build -I -I lib/ww -I selfhost/cmd/wcc src`. +/* Each tool builds via `ww_ww build -I lib/ww -I selfhost/cmd src`. * lib/ww holds the language introspection (lex/tok/ast/parse/typ/sym); - * selfhost/cmd/wcc holds the compiler internals (mem/check/cgen*). + * selfhost/cmd holds the compiler-internal `wcc` directory package. * Dotted `import encoding.utf8;` finds lib/encoding/utf8/ via the * driver's default srclib path post-task-#22 dir-enum. - * Some tools have a local module dir (w6a, w6l with sibling .ww files). - * inc_local is "" for tools without one (w6c, ww, wwdump). */ struct buildjob { const char *tool; const char *src_rel; - const char *inc_local; char workdir[64]; pid_t pid; }; @@ -99,20 +96,11 @@ spawn_build(const char *bin, const char *cwd, struct buildjob *j) } char cmd[4096]; - if (j->inc_local && j->inc_local[0]) { - snprintf(cmd, sizeof cmd, - "cd %s && timeout 180 %s/ww_ww build -o %s/main -I %s/%s " - "-I %s/lib/ww " - "-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err", - j->workdir, bin, j->workdir, cwd, j->inc_local, - cwd, cwd, cwd, j->src_rel, j->workdir); - } else { - snprintf(cmd, sizeof cmd, - "cd %s && timeout 180 %s/ww_ww build -o %s/main " - "-I %s/lib/ww " - "-I %s/selfhost/cmd/wcc %s/%s >/dev/null 2>%s/build.err", - j->workdir, bin, j->workdir, cwd, cwd, cwd, j->src_rel, j->workdir); - } + snprintf(cmd, sizeof cmd, + "cd %s && timeout 180 %s/ww_ww build -o %s/main " + "-I %s/lib/ww -I %s/selfhost/cmd " + "%s/%s >/dev/null 2>%s/build.err", + j->workdir, bin, j->workdir, cwd, cwd, cwd, j->src_rel, j->workdir); pid_t p = fork(); if (p < 0) { @@ -184,11 +172,11 @@ main(void) if (getcwd(cwd, sizeof cwd) == NULL) return 1; struct buildjob jobs[] = { - { "w6c", "selfhost/cmd/w6c/main.ww", "", {0}, 0 }, - { "w6a", "selfhost/cmd/w6a/main.ww", "selfhost/cmd/w6a", {0}, 0 }, - { "w6l", "selfhost/cmd/w6l/main.ww", "selfhost/cmd/w6l", {0}, 0 }, - { "ww", "selfhost/cmd/ww/main.ww", "", {0}, 0 }, - { "wwdump", "selfhost/cmd/wwdump/main.ww", "", {0}, 0 }, + { "w6c", "selfhost/cmd/w6c/main.ww", {0}, 0 }, + { "w6a", "selfhost/cmd/w6a", {0}, 0 }, + { "w6l", "selfhost/cmd/w6l", {0}, 0 }, + { "ww", "selfhost/cmd/ww/main.ww", {0}, 0 }, + { "wwdump", "selfhost/cmd/wwdump/main.ww", {0}, 0 }, }; const int n = (int)(sizeof jobs / sizeof jobs[0]); diff --git a/test/wcc/data/paramshadowmod/crossmod.ww b/test/wcc/data/paramshadowmod/crossmod.ww deleted file mode 100644 index 3174278e..00000000 --- a/test/wcc/data/paramshadowmod/crossmod.ww +++ /dev/null @@ -1,12 +0,0 @@ -// crossmod — sibling module whose probe() takes a param named like the -// module `shadowmod` that paramshadowmod (NOT crossmod) imports. The -// shadow rule is filtered by the BINDING's own module (src_imports -// cur_mod filter): crossmod carries no `import shadowmod`, so this param -// must NOT trip even though a sibling module in the same bundle imports -// that leaf. Legit-form survivor of the abolished self-import tolerance. - -package crossmod; - -export fn probe(shadowmod: str) i32 = { - return shadowmod.len; -}; diff --git a/test/wcc/data/paramshadowmod/crossmod/crossmod.ww b/test/wcc/data/paramshadowmod/crossmod/crossmod.ww new file mode 100644 index 00000000..a8dd2cf6 --- /dev/null +++ b/test/wcc/data/paramshadowmod/crossmod/crossmod.ww @@ -0,0 +1,11 @@ +// crossmod — directory package whose probe() takes a param named like the +// module `shadowmod` that paramshadowmod (NOT crossmod) imports. The +// shadow rule is filtered by the binding's own module (src_imports +// cur_mod filter): crossmod carries no `import shadowmod`, so this param +// must NOT trip even though a sibling package imports that leaf. + +package crossmod; + +export fn probe(shadowmod: str) i32 = { + return shadowmod.len; +}; diff --git a/test/xmod/collide_test.ww b/test/xmod/collide_test.ww index b6a84afa..cf6b8b14 100644 --- a/test/xmod/collide_test.ww +++ b/test/xmod/collide_test.ww @@ -133,60 +133,61 @@ fn textcount(s: str, sym: str) i32 = { testenv.clean(td); }; -// barevalue (#55 cgen-side sibling) — the migrated 794 carrier +// barevalue (#55 cgen-side sibling) — the migrated 794 compiler carrier // (test/wcc/794_xmod_ident_prefer.c, retired with this row; its header // documents both #55 halves). A bare VALUE ident read inside an // imported module must be classified by the checker-stamped type, not // a unit-wide leaf table: aa exports `v: i32 = 7` and getv() reads the -// bare `v`; the root declares a same-leaf `fn v() i64`. FLAT layout -// (file-keyed import) is REQUIRED — both files fold into one unit so -// the foreign fn lands in the leaf table; a dir-keyed aa/ compiles aa -// as its own sep unit where main.v is invisible and the collision -// cannot express. Pre-fix wwstage cgen took fnretlookup's leaf +// bare `v`; the root declares a same-leaf `fn v() i64`. This compiler +// collision requires one explicitly composed raw unit; directory packages +// correctly isolate the leaf tables and the driver no longer folds source +// imports. Pre-fix wwstage cgen took fnretlookup's leaf // fallback and emitted `LEAQ aa.v(SB)` (fn address, no load) — the -// binary exited 0; cstage loads 7 (LEAQ+MOVSXD). Both drivers must -// build, run 7, and the flat __root.s must be cs==ww byte-identical -// (rule 10) — the stronger assertion set the carrier deferred while -// the cgen side was open. +// binary exited 0; cstage loads 7 (LEAQ+MOVSXD). Both compilers must +// emit byte-identical assembly and the raw fixture must run 7. @test fn barevalue() void = { let td: str = testenv.fresh(); - testenv.writefile(strings.concat(td, "/aa.ww"), strings.concat( + let unit: str = strings.concat(td, "/barevalue.unit.ww"); + testenv.writefile(unit, strings.concat( + "//ww:module aa\n", "package aa;\n", "export let v: i32 = 7;\n", "export fn getv() i32 = {\n", " return v;\n", - "};\n")); - testenv.writefile(strings.concat(td, "/main.ww"), strings.concat( + "};\n", + "//ww:module-reset\n", "package main;\n", "import aa;\n", "fn v() i64 = { return 100; };\n", "export fn main() i32 = {\n", " return aa.getv();\n", "};\n")); - let drvs: []str = ["ww", "ww_ww"]; + let comps: []str = ["w6c", "w6c_ww"]; let tags: []str = ["cs", "ww"]; let asms: []str = ["", ""]; let s: i32 = 0; for (s < 2) { - let stem: str = strings.concat(td, "/prog.", tags[s]); - let av: []str = [testenv.driver(drvs[s]), "build", "-o", stem, - "-I", td, strings.concat(td, "/main.ww")]; + asms[s] = strings.concat(td, "/", tags[s], ".s"); + let av: []str = [testenv.driver(comps[s]), "-o", asms[s], unit]; if (runcode(td, strings.concat("build_", tags[s]), av) != 0) { - fail("barevalue", strings.concat(drvs[s], " build failed")); + fail("barevalue", strings.concat(comps[s], " compile failed")); }; - let rav: []str = [stem]; - if (runcode(td, strings.concat("run_", tags[s]), rav) != 7) { - fail("barevalue", strings.concat(drvs[s], " exit != 7 ", - "(bare v in aa.getv must LOAD aa.v, not take the ", - "foreign fn's address)")); - }; - asms[s] = testenv.readfile(strings.concat(stem, - ".sepwork/__root.s")); s += 1; }; - if (!testenv.same(asms[0], asms[1])) { + if (!testenv.same(testenv.readfile(asms[0]), testenv.readfile(asms[1]))) { fail("barevalue", "cs .s != ww .s (rule 10)"); }; + let obj: str = strings.concat(td, "/barevalue.o"); + let aav: []str = [testenv.driver("w6a"), "-o", obj, asms[0]]; + if (runcode(td, "assemble", aav) != 0) { fail("barevalue", "w6a failed"); }; + let prog: str = strings.concat(td, "/barevalue"); + let lav: []str = [testenv.driver("w6l"), "-o", prog, obj, + strings.concat(testenv.repo(), "/out/lib/libwwrt.a")]; + if (runcode(td, "link", lav) != 0) { fail("barevalue", "w6l failed"); }; + let rav: []str = [prog]; + if (runcode(td, "run", rav) != 7) { + fail("barevalue", "bare v loaded the foreign fn address"); + }; testenv.clean(td); }; @@ -198,7 +199,8 @@ fn textcount(s: str, sym: str) i32 = { // the mirror corner (d) in cgdot. Runtime pre-fix: 141, want 42. @test fn defshadow() void = { let td: str = testenv.fresh(); - testenv.writefile(strings.concat(td, "/p5aa.ww"), strings.concat( + assert(os.mkdir(strings.concat(td, "/p5aa"), 493) == 0); + testenv.writefile(strings.concat(td, "/p5aa/p5aa.ww"), strings.concat( "package p5aa;\n", "export def MSG: i32 = 3;\n")); testenv.writefile(strings.concat(td, "/main.ww"), strings.concat( @@ -241,7 +243,8 @@ fn textcount(s: str, sym: str) i32 = { // gate (proves the checker stamps the N_DOT fn rvalue as TY_FN). @test fn modqualfnval() void = { let td: str = testenv.fresh(); - testenv.writefile(strings.concat(td, "/p6aa.ww"), strings.concat( + assert(os.mkdir(strings.concat(td, "/p6aa"), 493) == 0); + testenv.writefile(strings.concat(td, "/p6aa/p6aa.ww"), strings.concat( "package p6aa;\n", "export fn hit(x: i32) i32 = { return x + 40; };\n")); testenv.writefile(strings.concat(td, "/main.ww"), strings.concat( diff --git a/test/xmod/typecheck_test.ww b/test/xmod/typecheck_test.ww index f6c3ccd6..f3f5d921 100644 --- a/test/xmod/typecheck_test.ww +++ b/test/xmod/typecheck_test.ww @@ -182,7 +182,8 @@ fn xrow(label: str, esrc: str, msrc: str, expectbuild: bool) void = { let i: i32 = 0; for (i < 2) { let td: str = testenv.fresh(); - testenv.writefile(strings.concat(td, "/e.ww"), esrc); + assert(os.mkdir(strings.concat(td, "/e"), 493) == 0); + testenv.writefile(strings.concat(td, "/e/e.ww"), esrc); testenv.writefile(strings.concat(td, "/main.ww"), msrc); let av: []str = [testenv.driver(drvs[i]), "build", "-I", td, strings.concat(td, "/main.ww")];