ww: finish initialization validation parity

This commit is contained in:
2026-08-14 20:03:11 +09:00
parent 25da54936d
commit c59a7c11de
26 changed files with 528 additions and 143 deletions

View File

@@ -3193,6 +3193,10 @@ static int
init_tagged_raw_static(Type *u, Node *n) init_tagged_raw_static(Type *u, Node *n)
{ {
Node *r = init_strip_cast(n); Node *r = init_strip_cast(n);
/* Cstage keeps true/false as untyped-bool, distinct from the concrete
* bool variant selected by runtime boxing. Keep boolean payloads on that
* runtime path in both stages so package artifacts remain identical. */
if (r != NULL && (r->kind == N_TRUE || r->kind == N_FALSE)) return 0;
Type *ru = type_chase_named(r ? r->type : NULL); Type *ru = type_chase_named(r ? r->type : NULL);
u64 ignored; u64 ignored;
return u != NULL && u->kind == TY_TAGGED && !u->nullable return u != NULL && u->kind == TY_TAGGED && !u->nullable
@@ -3336,8 +3340,13 @@ init_expr_static(Type *t, Node *n)
if (u != NULL && u->kind == TY_TAGGED && !u->nullable) { if (u != NULL && u->kind == TY_TAGGED && !u->nullable) {
Type *ru = type_chase_named(r->type); Type *ru = type_chase_named(r->type);
if (!variant_present(u->params, r->type)) return 0; if (!variant_present(u->params, r->type)) return 0;
if (ru != NULL && (ru->kind == TY_STR || ru->kind == TY_SLICE)) /* String carriers need a relocation-bearing tagged row. Cstage's
return r->kind == N_STRLIT; * untyped string is not the concrete str variant here; keep every
* such mutable value on the common runtime path rather than let one
* stage classify the same spelling as static data. */
if (ru != NULL && (ru->kind == TY_STR
|| ru->kind == TY_UNTYPED_STR || ru->kind == TY_SLICE))
return 0;
return init_tagged_raw_static(u, r); return init_tagged_raw_static(u, r);
} }
if (init_fnptr_static(r)) return 1; if (init_fnptr_static(r)) return 1;
@@ -3345,6 +3354,67 @@ init_expr_static(Type *t, Node *n)
return fold_int_literal(r, &ignored); return fold_int_literal(r, &ignored);
} }
/* A slice literal has no declared element count for WW's trailing `...`
* repeat to fill. Keep this a checker-owned semantic rejection when a
* mutable package let moves from static data to runtime initialization;
* otherwise the backing counter drops the marker and silently publishes the
* explicit prefix. Array repeats remain valid because their target length is
* known. Recurse through the aggregate shapes package-init lowering owns so
* a nested slice cannot bypass the same rule. */
static int
init_validate_slice_repeats(Checker *c, Type *want, Node *expr)
{
Node *r = init_strip_cast(expr);
Type *u = type_chase_named(want);
if (r == NULL || u == NULL) return 0;
if (u->kind == TY_SLICE && r->kind == N_ARRLIT) {
for (Node *e = r->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0) {
err(c, e->pos, "'...' repeat has no target length "
"in a slice literal");
return -1;
}
if (init_validate_slice_repeats(c, u->sub, e) < 0)
return -1;
}
return 0;
}
if (u->kind == TY_ARRAY && r->kind == N_ARRLIT) {
for (Node *e = r->list; e; e = e->next) {
if (e->kind == N_FIELD && e->str != NULL
&& strcmp(e->str, "...") == 0)
continue;
if (init_validate_slice_repeats(c, u->sub, e) < 0)
return -1;
}
return 0;
}
if (u->kind == TY_STRUCT && r->kind == N_STRUCTLIT) {
for (Node *e = r->list; e; e = e->next) {
Tfield *field = NULL;
for (Tfield *f = u->fields; f; f = f->next)
if (e->str != NULL && f->name != NULL
&& strcmp(e->str, f->name) == 0) {
field = f;
break;
}
if (field != NULL
&& init_validate_slice_repeats(c, field->type,
e->lhs) < 0)
return -1;
}
return 0;
}
if (u->kind == TY_TUPLE && r->kind == N_TUPLE) {
Tparam *p = u->params;
for (Node *e = r->list; e && p; e = e->next, p = p->next)
if (init_validate_slice_repeats(c, p->type, e) < 0)
return -1;
}
return 0;
}
struct initwalkitem { struct initwalkitem {
Node *node; Node *node;
struct initwalkitem *next; struct initwalkitem *next;
@@ -3665,6 +3735,8 @@ init_lower_package(Checker *c, Node *file)
return -1; return -1;
} }
nvar++; nvar++;
if (init_validate_slice_repeats(c, d->type, d->rhs) < 0)
continue;
if (!init_expr_static(d->type, d->rhs)) { if (!init_expr_static(d->type, d->rhs)) {
d->runtimeinit = 1; d->runtimeinit = 1;
nruntime++; nruntime++;
@@ -4601,8 +4673,19 @@ check_file(Checker *c, Node *file)
* returns 0 silently and is left untouched). * returns 0 silently and is left untouched).
* Stamp LAST — the assignability check above * Stamp LAST — the assignability check above
* consumes the pre-stamp cexpr type. */ * consumes the pre-stamp cexpr type. */
/* An explicit scalar-to-tagged cast is also the carrier
* selection. Folding it to an integer literal while keeping
* the tagged result type makes cgen consume scalar registers
* as an already-wide tagged ABI value. Mutable package lets
* preserve the cast so the concrete carrier is boxed correctly;
* const remains on its established static-only path. */
Type *foldt = type_chase_named(d->type);
int preserve_tagged_cast = d->op != TK_CONST && d->rhs
&& d->rhs->kind == N_CAST && foldt != NULL
&& foldt->kind == TY_TAGGED && !foldt->nullable;
u64 dv; u64 dv;
if (d->rhs && !fold_int_literal(d->rhs, &dv) if (d->rhs && !preserve_tagged_cast
&& !fold_int_literal(d->rhs, &dv)
&& eval_def_const(c, d->rhs, &dv, 0)) && eval_def_const(c, d->rhs, &dv, 0))
stamp_intlit(c, d->rhs, dv); stamp_intlit(c, d->rhs, dv);
} else if (d->type && d->type->kind == TY_ARRAY } else if (d->type && d->type->kind == TY_ARRAY

View File

@@ -4648,11 +4648,6 @@ build_one_sep_impl(const char *src, int entry_is_dir,
&& !sep_root_is_command(&g->pkg[products[0].root]); && !sep_root_is_command(&g->pkg[products[0].root]);
if (sep_validate_artifact_paths(g, scratch) < 0) if (sep_validate_artifact_paths(g, scratch) < 0)
return 1; return 1;
if (sep_validate_request_staging(g, scratch, warm, products, nproducts,
root_package, publish_package, emit_asm, is_test) < 0) {
sep_free_product_staging(products, nproducts);
return 1;
}
if (warm && workdir_exists && sep_validate_workdir_owners(g, scratch) < 0) if (warm && workdir_exists && sep_validate_workdir_owners(g, scratch) < 0)
return 1; return 1;
int *order = calloc((size_t)g->n, sizeof *order); int *order = calloc((size_t)g->n, sizeof *order);
@@ -4737,6 +4732,12 @@ build_one_sep_impl(const char *src, int entry_is_dir,
free(order); free(order);
return 1; return 1;
} }
if (sep_validate_request_staging(g, scratch, warm, products, nproducts,
root_package, publish_package, emit_asm, is_test) < 0) {
sep_free_product_staging(products, nproducts);
free(order);
return 1;
}
/* Product completion and persistent package state remain untouched until /* Product completion and persistent package state remain untouched until
* all source-derived imports, contextual legality, cycles, command kind, * all source-derived imports, contextual legality, cycles, command kind,
* output paths, and action closures have passed their pre-tool checks. */ * output paths, and action closures have passed their pre-tool checks. */

View File

@@ -2,10 +2,10 @@ package wwfixture;
def protocolversion: i32 = 1; def protocolversion: i32 = 1;
def corpuscount: i32 = 1759; def corpuscount: i32 = 1759;
def errorcount: i32 = 351; def errorcount: i32 = 338;
def compilecount: i32 = 22; def compilecount: i32 = 22;
def runcount: i32 = 209; def runcount: i32 = 214;
def runexitcount: i32 = 1177; def runexitcount: i32 = 1185;
def nativecount: i32 = 3518; def nativecount: i32 = 3518;
def corpushash: str = "47d731a8fd089ecdef9a1b9b94f3dfc06a733b7e03236014f095b068a67ce176"; def corpushash: str = "47d731a8fd089ecdef9a1b9b94f3dfc06a733b7e03236014f095b068a67ce176";

View File

@@ -7731,6 +7731,11 @@ fn initfloatstatic(n: *syntax.node) bool = {
fn inittaggedrawstatic(u: *syntax.tinfo, n: *syntax.node) bool = { fn inittaggedrawstatic(u: *syntax.tinfo, n: *syntax.node) bool = {
let r: *syntax.node = initstripcast(n); let r: *syntax.node = initstripcast(n);
// Cstage keeps true/false as untyped-bool, distinct from the concrete
// bool variant selected by runtime boxing. Keep boolean payloads on that
// runtime path in both stages so package artifacts remain identical.
if (r != nil && (r.kind == syntax.nkind.N_TRUE
|| r.kind == syntax.nkind.N_FALSE)) { return false; };
if (u == nil || u.kind != syntax.tykind.TY_TAGGED || u.nullable != 0 if (u == nil || u.kind != syntax.tykind.TY_TAGGED || u.nullable != 0
|| r == nil || !variantpresent(u.params, r.type_: *syntax.tinfo) || r == nil || !variantpresent(u.params, r.type_: *syntax.tinfo)
|| syntax.typeisstr(r.type_: *syntax.tinfo) || syntax.typeisstr(r.type_: *syntax.tinfo)
@@ -7871,9 +7876,13 @@ fn initexprstatic(t: *syntax.tinfo, n: *syntax.node) bool = {
&& u.nullable == 0) { && u.nullable == 0) {
if (!variantpresent(u.params, r.type_: *syntax.tinfo)) { return false; }; if (!variantpresent(u.params, r.type_: *syntax.tinfo)) { return false; };
let ru: *syntax.tinfo = tichase(r.type_: *syntax.tinfo); let ru: *syntax.tinfo = tichase(r.type_: *syntax.tinfo);
// Relocation-bearing string carriers stay on the common runtime
// path: Cstage's untyped string is not the concrete str variant at
// this classifier, so a static wwstage row would change artifacts.
if (ru != nil && (ru.kind == syntax.tykind.TY_STR if (ru != nil && (ru.kind == syntax.tykind.TY_STR
|| ru.kind == syntax.tykind.TY_UNTYPED_STR
|| ru.kind == syntax.tykind.TY_SLICE)) { || ru.kind == syntax.tykind.TY_SLICE)) {
return r.kind == syntax.nkind.N_STRLIT; return false;
}; };
return inittaggedrawstatic(u, r); return inittaggedrawstatic(u, r);
}; };
@@ -7882,6 +7891,78 @@ fn initexprstatic(t: *syntax.tinfo, n: *syntax.node) bool = {
return foldintliteral(r, &ignored); return foldintliteral(r, &ignored);
}; };
// A slice literal has no declared element count for WW's trailing `...`
// repeat to fill. Keep this a checker-owned semantic rejection when a mutable
// package let moves from static data to runtime initialization; otherwise the
// backing counter drops the marker and publishes only the explicit prefix.
// Arrays retain the repeat because their target length is known. Recurse
// through the aggregate shapes package-init lowering owns.
fn initvalidateslicerepeats(c: *checker, want: *syntax.tinfo,
expr: *syntax.node) bool = {
let r: *syntax.node = initstripcast(expr);
let u: *syntax.tinfo = tichase(want);
if (r == nil || u == nil) { return true; };
if (u.kind == syntax.tykind.TY_SLICE
&& r.kind == syntax.nkind.N_ARRLIT) {
let e: *syntax.node = r.list;
for (e != nil) {
if (e.kind == syntax.nkind.N_FIELD
&& syntax.streq(e.str, "...")) {
importdiagprefix(e);
cerr("'...' repeat has no target length in a slice literal\n");
c.errs += 1;
return false;
};
if (!initvalidateslicerepeats(c, u.sub, e)) { return false; };
e = e.next;
};
return true;
};
if (u.kind == syntax.tykind.TY_ARRAY
&& r.kind == syntax.nkind.N_ARRLIT) {
let e: *syntax.node = r.list;
for (e != nil) {
if (!(e.kind == syntax.nkind.N_FIELD
&& syntax.streq(e.str, "..."))) {
if (!initvalidateslicerepeats(c, u.sub, e)) {
return false;
};
};
e = e.next;
};
return true;
};
if (u.kind == syntax.tykind.TY_STRUCT
&& r.kind == syntax.nkind.N_STRUCTLIT) {
let e: *syntax.node = r.list;
for (e != nil) {
let field: *syntax.tfield = u.fields;
for (field != nil) {
if (syntax.streq(e.str, field.name)) { break; };
field = field.tnext;
};
if (field != nil) {
if (!initvalidateslicerepeats(c, field.type_, e.lhs)) {
return false;
};
};
e = e.next;
};
return true;
};
if (u.kind == syntax.tykind.TY_TUPLE
&& r.kind == syntax.nkind.N_TUPLE) {
let te: *syntax.ttupleelem = u.tupleelems;
let e: *syntax.node = r.list;
for (e != nil && te != nil) {
if (!initvalidateslicerepeats(c, te.type_, e)) { return false; };
e = e.next;
te = te.tnext;
};
};
return true;
};
type initwalkitem = struct { type initwalkitem = struct {
node: *syntax.node, node: *syntax.node,
next: *initwalkitem, next: *initwalkitem,
@@ -8266,6 +8347,10 @@ fn initlowerpackage(c: *checker, file: *syntax.node) void = {
else { if (d.rhs != nil) { else { if (d.rhs != nil) {
dt = d.rhs.type_: *syntax.tinfo; dt = d.rhs.type_: *syntax.tinfo;
}; }; }; };
if (!initvalidateslicerepeats(c, dt, d.rhs)) {
d = d.next;
continue;
};
if (!initexprstatic(dt, d.rhs)) { if (!initexprstatic(dt, d.rhs)) {
d.runtimeinit = 1; d.runtimeinit = 1;
nruntime += 1u64; nruntime += 1u64;
@@ -8906,8 +8991,22 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
// rhs returns false silently and is left untouched). // rhs returns false silently and is left untouched).
// Stamp LAST — checkletassign consumes the pre-stamp type. // Stamp LAST — checkletassign consumes the pre-stamp type.
if (d.rhs != nil) { if (d.rhs != nil) {
// An explicit scalar-to-tagged cast selects the carrier.
// Folding it while retaining the tagged result type makes
// cstage consume scalar registers as an already-wide ABI
// value. Mutable package lets preserve the cast so the concrete
// carrier is boxed correctly; const keeps static-only behavior.
let foldt: *syntax.tinfo = nil;
if (d.lhs != nil) {
foldt = tichase(d.lhs.type_: *syntax.tinfo);
};
let preservetaggedcast: bool = d.op != syntax.tkind.TK_CONST
&& d.rhs.kind == syntax.nkind.N_CAST
&& foldt != nil
&& foldt.kind == syntax.tykind.TY_TAGGED
&& foldt.nullable == 0;
let dv: u64 = 0u64; let dv: u64 = 0u64;
if (!foldintliteral(d.rhs, &dv)) { if (!preservetaggedcast && !foldintliteral(d.rhs, &dv)) {
if (evaldefconst(c, d.rhs, &dv, 0)) { if (evaldefconst(c, d.rhs, &dv, 0)) {
stampintlit(d.rhs, dv); stampintlit(d.rhs, dv);
}; };

View File

@@ -5959,11 +5959,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
&& !g.pkg[products[0].root].failed && !g.pkg[products[0].root].failed
&& !seprootiscommand(&g.pkg[products[0].root]); && !seprootiscommand(&g.pkg[products[0].root]);
if (sepvalidateartifactpaths(g, scratch) < 0) { return 1; }; if (sepvalidateartifactpaths(g, scratch) < 0) { return 1; };
if (sepvalidaterequeststaging(g, scratch, warm, products, nproducts,
rootpackage, publishpackage, emitasm, istest) < 0) {
sepfreeproductstaging(products, nproducts);
return 1;
};
if (warm && workdirexists if (warm && workdirexists
&& sepvalidateworkdirowners(g, scratch) < 0) { return 1; }; && sepvalidateworkdirowners(g, scratch) < 0) { return 1; };
let ci: i32 = 0; let ci: i32 = 0;
@@ -6064,6 +6059,11 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
producti += 1; producti += 1;
}; };
if (!viableproduct) { return 1; }; if (!viableproduct) { return 1; };
if (sepvalidaterequeststaging(g, scratch, warm, products, nproducts,
rootpackage, publishpackage, emitasm, istest) < 0) {
sepfreeproductstaging(products, nproducts);
return 1;
};
// Source-derived resolution and contextual legality are complete before // Source-derived resolution and contextual legality are complete before
// coordinator completion markers or persistent vouchers are changed. // coordinator completion markers or persistent vouchers are changed.
let createdoutput: sepcreateddirs; let createdoutput: sepcreateddirs;

View File

@@ -3,8 +3,9 @@
// #156). emit_array_lit_bytes recurses on a TY_ARRAY element (esz=etype->size, // #156). emit_array_lit_bytes recurses on a TY_ARRAY element (esz=etype->size,
// rule 13); cgindex leaves the sub-array ADDRESS for an array element so the // rule 13); cgindex leaves the sub-array ADDRESS for an array element so the
// outer index dereferences the right cell (#135 sister). cstage build+run was // outer index dereferences the right cell (#135 sister). cstage build+run was
// T1; cs==ww .s byte-id rides T2 (test-lang-byteid). The nested-array `...` // T1; cs==ww .s byte-id rides T2 (test-lang-byteid). A nested-array `...`
// repeat reject is the runww carrier test/wcc/data/arr_nested_ellipsis_reject. // repeat now runs through package initialization when static emission cannot
// represent it, as pinned by test/wcc/data/arr_nested_ellipsis_reject.
// //
// Float-element rows assert bit-exact incl negative elements (drew: exercise the // Float-element rows assert bit-exact incl negative elements (drew: exercise the
// sign-XOR byte-loop per element). The struct-with-array-field rows read the // sign-XOR byte-loop per element). The struct-with-array-field rows read the

View File

@@ -93,17 +93,26 @@ fn samefile(a: str, b: str, why: str) void = {
"example.foo.unit.ww")); "example.foo.unit.ww"));
let wantfoo: str = strings.concat("//ww:module-reset example.foo\n", afoosrc, let wantfoo: str = strings.concat("//ww:module-reset example.foo\n", afoosrc,
"\n//ww:module-reset example.foo\n", zfoosrc, "\n"); "\n//ww:module-reset example.foo\n", zfoosrc, "\n");
if (!testenv.same(foounit, wantfoo) let foovoucher: str = "//ww:direct-export example.bar ";
if (!strings.hasprefix(foounit, wantfoo)
|| testenv.occurrences(foounit, "//ww:direct-export ") != 1
|| testenv.occurrences(foounit, foovoucher) != 1
|| testenv.pos(foounit, foovoucher) != wantfoo.len
|| testenv.has(foounit, "DECOY_FILE") || testenv.has(foounit, "DECOY_FILE")
|| testenv.has(foounit, "//ww:module ")) { || testenv.has(foounit, "//ww:module ")) {
fail("foo unit is not exactly its sorted, owned source set"); fail("foo owner prefix/direct-export voucher mismatch");
}; };
let wantroot: str = strings.concat("//ww:module-reset ", appidentity, let wantroot: str = strings.concat("//ww:module-reset ", appidentity,
"\n", appsrc, "\n", appsrc,
"\n"); "\n");
if (!testenv.same(wantroot, testenv.readfile(strings.concat(cwork, let rootunit: str = testenv.readfile(strings.concat(cwork,
appidentity, ".unit.ww")))) { appidentity, ".unit.ww"));
fail("root unit is not exactly its owned source"); let rootvoucher: str = "//ww:direct-export example.foo ";
if (!strings.hasprefix(rootunit, wantroot)
|| testenv.occurrences(rootunit, "//ww:direct-export ") != 1
|| testenv.occurrences(rootunit, rootvoucher) != 1
|| testenv.pos(rootunit, rootvoucher) != wantroot.len) {
fail("root owner prefix/direct-export voucher mismatch");
}; };
let keys: []str = ["example.base", "example.bar", "example.foo", let keys: []str = ["example.base", "example.bar", "example.foo",
appidentity]; appidentity];

View File

@@ -288,10 +288,14 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = {
}; };
let expectedunit: str = strings.concat("//ww:module-reset main\n", let expectedunit: str = strings.concat("//ww:module-reset main\n",
mainsrc, "\n"); mainsrc, "\n");
if (!testenv.same(expectedunit, testenv.readfile(strings.concat(cwork, let cunit: str = testenv.readfile(strings.concat(cwork, "main.unit.ww"));
"main.unit.ww"))) || !testenv.same(expectedunit, let cunit2: str = testenv.readfile(strings.concat(cwork2, "main.unit.ww"));
testenv.readfile(strings.concat(wwork, "main.unit.ww")))) { let wunit: str = testenv.readfile(strings.concat(wwork, "main.unit.ww"));
fail("self-contained", "consumer unit was not exactly its owned source"); if (!strings.hasprefix(cunit, expectedunit)
|| testenv.occurrences(cunit, "//ww:direct-export api ") != 1
|| testenv.pos(cunit, "//ww:direct-export api ") != expectedunit.len
|| !testenv.same(cunit, cunit2) || !testenv.same(cunit, wunit)) {
fail("self-contained", "consumer owner prefix/direct-export voucher mismatch");
}; };
if (!testenv.exists(strings.concat(cwork, "implementation.a")) if (!testenv.exists(strings.concat(cwork, "implementation.a"))
|| !testenv.exists(strings.concat(cwork, "dimensions.a"))) { || !testenv.exists(strings.concat(cwork, "dimensions.a"))) {
@@ -408,9 +412,16 @@ fn writediamond(td: str, reverse: bool) str = {
let rootbody: str = testenv.readfile(strings.concat(main, "/main.ww")); let rootbody: str = testenv.readfile(strings.concat(main, "/main.ww"));
let wantroot: str = strings.concat("//ww:module-reset main\n", rootbody, let wantroot: str = strings.concat("//ww:module-reset main\n", rootbody,
"\n"); "\n");
if (!testenv.same(unit, wantroot) || testenv.has(unit, let leftvoucher: str = "//ww:direct-export left ";
"//ww:module ")) { let rightvoucher: str = "//ww:direct-export right ";
fail("diamond", "root unit contains non-owned export text"); if (!strings.hasprefix(unit, wantroot) || testenv.has(unit,
"//ww:module ")
|| testenv.occurrences(unit, "//ww:direct-export ") != 2
|| testenv.occurrences(unit, leftvoucher) != 1
|| testenv.occurrences(unit, rightvoucher) != 1
|| testenv.pos(unit, leftvoucher) != wantroot.len
|| testenv.pos(unit, leftvoucher) >= testenv.pos(unit, rightvoucher)) {
fail("diamond", "root owner prefix/sorted direct-export vouchers changed");
}; };
let sharedunit: str = testenv.readfile(strings.concat(scratch, let sharedunit: str = testenv.readfile(strings.concat(scratch,
"shared.unit.ww")); "shared.unit.ww"));
@@ -534,6 +545,9 @@ fn writediamond(td: str, reverse: bool) str = {
let referencearchive: str = ""; let referencearchive: str = "";
let referencerootwwi: str = ""; let referencerootwwi: str = "";
let referencerootarchive: str = ""; let referencerootarchive: str = "";
let referenceinitunit: str = "";
let referenceinitasm: str = "";
let referenceinitobj: str = "";
let referencebin: str = ""; let referencebin: str = "";
let referencecompiler: str = ""; let referencecompiler: str = "";
let referenceassembler: str = ""; let referenceassembler: str = "";
@@ -583,9 +597,21 @@ fn writediamond(td: str, reverse: bool) str = {
|| !testenv.exists(strings.concat(work, "dep.a")) || !testenv.exists(strings.concat(work, "dep.a"))
|| !testenv.exists(strings.concat(work, "main.o")) || !testenv.exists(strings.concat(work, "main.o"))
|| !testenv.exists(strings.concat(work, "main.wwi")) || !testenv.exists(strings.concat(work, "main.wwi"))
|| !testenv.exists(strings.concat(work, "main.a"))) { || !testenv.exists(strings.concat(work, "main.a"))
|| !testenv.exists(strings.concat(work, "main.init.unit.ww"))
|| !testenv.exists(strings.concat(work, "main.init.s"))
|| !testenv.exists(strings.concat(work, "main.init.o"))) {
fail("library-roots", "package artifacts do not match root ownership"); fail("library-roots", "package artifacts do not match root ownership");
}; };
let initunit: str = testenv.readfile(strings.concat(work,
"main.init.unit.ww"));
if (!testenv.same(initunit, strings.concat(
"//ww:init-root __ww..pkg.p.main.v0.r0.init\n",
"//ww:init-call __ww..pkg.p.leaf.v0.r0.init\n",
"//ww:init-call __ww..pkg.p.dep.v0.r0.init\n",
"//ww:init-call __ww..pkg.p.main.v0.r0.init\n"))) {
fail("library-roots", "root dispatcher unit order changed");
};
let ctrace: str = testenv.readfile(compilertrace); let ctrace: str = testenv.readfile(compilertrace);
let atrace: str = testenv.readfile(assemblertrace); let atrace: str = testenv.readfile(assemblertrace);
let ltrace: str = testenv.readfile(linkertrace); let ltrace: str = testenv.readfile(linkertrace);
@@ -604,20 +630,29 @@ fn writediamond(td: str, reverse: bool) str = {
fail("library-roots", "compiler actions were not deterministic postorder"); fail("library-roots", "compiler actions were not deterministic postorder");
}; };
if (!testenv.has(ctrace, strings.concat( if (!testenv.has(ctrace, strings.concat(
"BEGIN<-c><--import><leaf><", work, "leaf.wwi><-I><", work, "BEGIN<--package-init-symbol><__ww..pkg.p.leaf.v0.r0.init>",
"<-c><-I><", work, "leaf.wwi><-o><", work,
"leaf.s><", work, "leaf.unit.ww>"))
|| !testenv.has(ctrace, strings.concat(
"BEGIN<--package-init-symbol><__ww..pkg.p.dep.v0.r0.init>",
"<-c><--import><leaf><", work, "leaf.wwi><-I><", work,
"dep.wwi><-o><", work, "dep.wwi><-o><", work,
"dep.s><", work, "dep.unit.ww>")) "dep.s><", work, "dep.unit.ww>"))
|| !testenv.has(ctrace, strings.concat( || !testenv.has(ctrace, strings.concat(
"BEGIN<--entry><-c><--import><dep><", work, "BEGIN<--entry><--package-init-symbol>",
"<__ww..pkg.p.main.v0.r0.init><--init-dispatch-symbol>",
"<__ww..dispatch><-c><--import><dep><", work,
"dep.wwi><-I><", work, "main.wwi><-o><", work, "dep.wwi><-I><", work, "main.wwi><-o><", work,
"main.s><", work, "main.unit.ww>")) "main.s><", work, "main.unit.ww>"))
|| testenv.occurrences(atrace, "\n") != 3 || testenv.occurrences(atrace, "\n") != 4
|| !testenv.has(atrace, strings.concat("BEGIN<-o><", work, || !testenv.has(atrace, strings.concat("BEGIN<-o><", work,
"dep.o><", work, "dep.s>"))) { "dep.o><", work, "dep.s>"))
|| !testenv.has(atrace, strings.concat("BEGIN<-o><", work,
"main.init.o><", work, "main.init.s>"))) {
fail("library-roots", "compiler or assembler argv changed"); fail("library-roots", "compiler or assembler argv changed");
}; };
if (testenv.occurrences(ltrace, "\n") != 1 if (testenv.occurrences(ltrace, "\n") != 1
|| !testenv.has(ltrace, strings.concat("BEGIN<-o><", bin, "><", || !testenv.has(ltrace, strings.concat("BEGIN<-o><", bin, ".new><",
work, "main.a>")) work, "main.a>"))
|| testenv.pos(ltrace, strings.concat("<", work, "dep.a>")) < 0 || testenv.pos(ltrace, strings.concat("<", work, "dep.a>")) < 0
|| testenv.pos(ltrace, strings.concat("<", work, "leaf.a>")) || testenv.pos(ltrace, strings.concat("<", work, "leaf.a>"))
@@ -655,6 +690,11 @@ fn writediamond(td: str, reverse: bool) str = {
"main.wwi"))); "main.wwi")));
referencerootarchive = strings.dup(testenv.readfile(strings.concat(work, referencerootarchive = strings.dup(testenv.readfile(strings.concat(work,
"main.a"))); "main.a")));
referenceinitunit = strings.dup(initunit);
referenceinitasm = strings.dup(testenv.readfile(strings.concat(work,
"main.init.s")));
referenceinitobj = strings.dup(testenv.readfile(strings.concat(work,
"main.init.o")));
referencebin = strings.dup(testenv.readfile(bin)); referencebin = strings.dup(testenv.readfile(bin));
referencecompiler = strings.dup(ctrace); referencecompiler = strings.dup(ctrace);
referenceassembler = strings.dup(atrace); referenceassembler = strings.dup(atrace);
@@ -669,6 +709,11 @@ fn writediamond(td: str, reverse: bool) str = {
testenv.readfile(strings.concat(work, "main.wwi"))) testenv.readfile(strings.concat(work, "main.wwi")))
|| !testenv.same(referencerootarchive, || !testenv.same(referencerootarchive,
testenv.readfile(strings.concat(work, "main.a"))) testenv.readfile(strings.concat(work, "main.a")))
|| !testenv.same(referenceinitunit, initunit)
|| !testenv.same(referenceinitasm,
testenv.readfile(strings.concat(work, "main.init.s")))
|| !testenv.same(referenceinitobj,
testenv.readfile(strings.concat(work, "main.init.o")))
|| !testenv.same(referencebin, testenv.readfile(bin)) || !testenv.same(referencebin, testenv.readfile(bin))
|| !testenv.same(referencecompiler, ctrace) || !testenv.same(referencecompiler, ctrace)
|| !testenv.same(referenceassembler, atrace) || !testenv.same(referenceassembler, atrace)
@@ -747,7 +792,7 @@ fn writediamond(td: str, reverse: bool) str = {
|| !testenv.exists(strings.concat(emptywork, "types.a")) || !testenv.exists(strings.concat(emptywork, "types.a"))
|| testenv.occurrences(ctrace, strings.concat("<", emptywork, || testenv.occurrences(ctrace, strings.concat("<", emptywork,
"types.unit.ww>")) != 1 "types.unit.ww>")) != 1
|| testenv.occurrences(atrace, "\n") != 2) { || testenv.occurrences(atrace, "\n") != 3) {
fail("library-roots", "empty WW_SRCLIB did not use the default source root"); fail("library-roots", "empty WW_SRCLIB did not use the default source root");
}; };
if (testenv.occurrences(ltrace, "\n") != 1 if (testenv.occurrences(ltrace, "\n") != 1
@@ -900,6 +945,9 @@ fn writediamond(td: str, reverse: bool) str = {
|| !testenv.exists(strings.concat(work, "/dep.a")) || !testenv.exists(strings.concat(work, "/dep.a"))
|| !testenv.exists(strings.concat(work, "/main.wwi")) || !testenv.exists(strings.concat(work, "/main.wwi"))
|| !testenv.exists(strings.concat(work, "/main.a")) || !testenv.exists(strings.concat(work, "/main.a"))
|| !testenv.exists(strings.concat(work, "/main.init.unit.ww"))
|| !testenv.exists(strings.concat(work, "/main.init.s"))
|| !testenv.exists(strings.concat(work, "/main.init.o"))
|| !testenv.same(testenv.readfile(strings.concat(work, || !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.ww")), testenv.readfile(copied[si])) "/.wwtool.ww")), testenv.readfile(copied[si]))
|| !testenv.same(testenv.readfile(strings.concat(work, || !testenv.same(testenv.readfile(strings.concat(work,
@@ -908,7 +956,7 @@ fn writediamond(td: str, reverse: bool) str = {
"/.wwtool.w6a")), testenv.readfile(assembler)) "/.wwtool.w6a")), testenv.readfile(assembler))
|| !testenv.same(testenv.readfile(strings.concat(work, || !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.stamp")), "/.wwtool.stamp")),
"ww workdir fmt 15 mode build asm 0\n")) { "ww workdir fmt 17 mode build asm 0\n")) {
fail("driver-identity", "persistent artifacts or identities are incomplete"); fail("driver-identity", "persistent artifacts or identities are incomplete");
}; };
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi")); let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
@@ -919,28 +967,38 @@ fn writediamond(td: str, reverse: bool) str = {
let coldlinker: str = testenv.readfile(linkertrace); let coldlinker: str = testenv.readfile(linkertrace);
if (testenv.occurrences(coldcompiler, "\n") != 3 if (testenv.occurrences(coldcompiler, "\n") != 3
|| !testenv.has(coldcompiler, strings.concat( || !testenv.has(coldcompiler, strings.concat(
"BEGIN<-c><--import><leaf><", work, "/leaf.wwi><-I><", work, "BEGIN<--package-init-symbol><__ww..pkg.p.leaf.v0.r0.init>",
"<-c><-I><", work, "/leaf.wwi.new><-o><", work,
"/leaf.s.new><", work, "/leaf.unit.new>"))
|| !testenv.has(coldcompiler, strings.concat(
"BEGIN<--package-init-symbol><__ww..pkg.p.dep.v0.r0.init>",
"<-c><--import><leaf><", work, "/leaf.wwi.new><-I><", work,
"/dep.wwi.new><-o><", work, "/dep.wwi.new><-o><", work,
"/dep.s.new><", work, "/dep.unit.new>")) "/dep.s.new><", work, "/dep.unit.new>"))
|| !testenv.has(coldcompiler, strings.concat( || !testenv.has(coldcompiler, strings.concat(
"BEGIN<--entry><-c><--import><dep><", work, "BEGIN<--entry><--package-init-symbol>",
"/dep.wwi><-I><", work, "/main.wwi.new><-o><", work, "<__ww..pkg.p.main.v0.r0.init><--init-dispatch-symbol>",
"<__ww..dispatch><-c><--import><dep><", work,
"/dep.wwi.new><-I><", work, "/main.wwi.new><-o><", work,
"/main.s.new><", work, "/main.s.new><", work,
"/main.unit.new>")) "/main.unit.new>"))
|| testenv.occurrences(coldassembler, "\n") != 3 || testenv.occurrences(coldassembler, "\n") != 4
|| !testenv.has(coldassembler, strings.concat( || !testenv.has(coldassembler, strings.concat(
"BEGIN<-o><", work, "/dep.o.new><", work, "BEGIN<-o><", work, "/dep.o.new><", work,
"/dep.s.new>"))) { "/dep.s.new>"))
|| !testenv.has(coldassembler, strings.concat(
"BEGIN<-o><", work, "/main.init.o.new><", work,
"/main.init.s.new>"))) {
fail("driver-identity", "cold compiler or assembler argv changed"); fail("driver-identity", "cold compiler or assembler argv changed");
}; };
if (testenv.occurrences(coldlinker, "\n") != 1 if (testenv.occurrences(coldlinker, "\n") != 1
|| !testenv.has(coldlinker, strings.concat("BEGIN<-o><", bin, || !testenv.has(coldlinker, strings.concat("BEGIN<-o><", bin,
"><", work, "/main.a>")) ".new><", work, "/main.a.new>"))
|| testenv.pos(coldlinker, strings.concat("<", work, || testenv.pos(coldlinker, strings.concat("<", work,
"/dep.a>")) < 0 "/dep.a.new>")) < 0
|| testenv.pos(coldlinker, strings.concat("<", work, || testenv.pos(coldlinker, strings.concat("<", work,
"/leaf.a>")) < testenv.pos(coldlinker, strings.concat("<", "/leaf.a.new>")) < testenv.pos(coldlinker, strings.concat("<",
work, "/dep.a>")) work, "/dep.a.new>"))
|| !testenv.has(coldlinker, strings.concat("<", runtime, || !testenv.has(coldlinker, strings.concat("<", runtime,
"/libwwrt.a>")) "/libwwrt.a>"))
|| testenv.has(coldlinker, ".wwi>")) { || testenv.has(coldlinker, ".wwi>")) {
@@ -970,7 +1028,7 @@ fn writediamond(td: str, reverse: bool) str = {
|| testenv.occurrences(changedexportcompiler, strings.concat("<", work, || testenv.occurrences(changedexportcompiler, strings.concat("<", work,
"/main.unit.new>")) != 1 "/main.unit.new>")) != 1
|| testenv.occurrences(changedexportcompiler, "\n") != 5 || testenv.occurrences(changedexportcompiler, "\n") != 5
|| testenv.occurrences(changedexportassembler, "\n") != 5) { || testenv.occurrences(changedexportassembler, "\n") != 6) {
fail("driver-identity", "changed export did not stop at an unchanged importer export"); fail("driver-identity", "changed export did not stop at an unchanged importer export");
}; };
if (code(cwd, strings.concat("driver-export-change-run-", tags[si]), if (code(cwd, strings.concat("driver-export-change-run-", tags[si]),
@@ -1004,7 +1062,7 @@ fn writediamond(td: str, reverse: bool) str = {
|| testenv.occurrences(testenv.readfile(compilertrace), strings.concat( || testenv.occurrences(testenv.readfile(compilertrace), strings.concat(
"<", work, "/main.unit.new>")) != 1 "<", work, "/main.unit.new>")) != 1
|| testenv.occurrences(testenv.readfile(compilertrace), "\n") != 7 || testenv.occurrences(testenv.readfile(compilertrace), "\n") != 7
|| testenv.occurrences(testenv.readfile(assemblertrace), "\n") != 7 || testenv.occurrences(testenv.readfile(assemblertrace), "\n") != 8
|| !testenv.same(coldbin, testenv.readfile(bin))) { || !testenv.same(coldbin, testenv.readfile(bin))) {
fail("driver-identity", "restored export did not rebuild only its direct importer"); fail("driver-identity", "restored export did not rebuild only its direct importer");
}; };
@@ -1029,7 +1087,7 @@ fn writediamond(td: str, reverse: bool) str = {
|| testenv.occurrences(changedcompiler, strings.concat("<", work, || testenv.occurrences(changedcompiler, strings.concat("<", work,
"/main.unit.new>")) != 2 "/main.unit.new>")) != 2
|| testenv.occurrences(changedcompiler, "\n") != 10 || testenv.occurrences(changedcompiler, "\n") != 10
|| testenv.occurrences(changedassembler, "\n") != 10 || testenv.occurrences(changedassembler, "\n") != 12
|| testenv.occurrences(changedlinker, "\n") != 5) { || testenv.occurrences(changedlinker, "\n") != 5) {
fail("driver-identity", "driver change reused stale package actions"); fail("driver-identity", "driver change reused stale package actions");
}; };
@@ -1339,9 +1397,11 @@ fn writediamond(td: str, reverse: bool) str = {
let linkers: []str = ["w6l", "w6l_ww"]; let linkers: []str = ["w6l", "w6l_ww"];
let tags: []str = ["c", "ww"]; let tags: []str = ["c", "ww"];
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"]; let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
let initsuffixes: []str = [".init.unit.ww", ".init.s", ".init.o"];
let rootreference: []str = ["", "", "", "", ""]; let rootreference: []str = ["", "", "", "", ""];
let depreference: []str = ["", "", "", "", ""]; let depreference: []str = ["", "", "", "", ""];
let commandreference: []str = ["", "", "", "", ""]; let commandreference: []str = ["", "", "", "", ""];
let commandinitreference: []str = ["", "", ""];
let nooutrootreference: []str = ["", "", "", "", ""]; let nooutrootreference: []str = ["", "", "", "", ""];
let nooutdepreference: []str = ["", "", "", "", ""]; let nooutdepreference: []str = ["", "", "", "", ""];
let publishreference: str = ""; let publishreference: str = "";
@@ -1382,11 +1442,14 @@ fn writediamond(td: str, reverse: bool) str = {
let ctrace: str = testenv.readfile(compilertrace); let ctrace: str = testenv.readfile(compilertrace);
let atrace: str = testenv.readfile(assemblertrace); let atrace: str = testenv.readfile(assemblertrace);
let wantcompiler: str = strings.concat( let wantcompiler: str = strings.concat(
"BEGIN<-c><-I><", work, "BEGIN<--package-init-symbol>",
"<__ww..pkg.p.foo.dep.v0.r0.init><-c><-I><", work,
"/foo.dep.wwi.new><-o><", work, "/foo.dep.wwi.new><-o><", work,
"/foo.dep.s.new><", work, "/foo.dep.unit.new>\n", "/foo.dep.s.new><", work, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", work, "BEGIN<--package-init-symbol>",
"/foo.dep.wwi><-I><", work, "<__ww..pkg.p.foo.bar.v0.r0.init><-c>",
"<--import><foo.dep><", work,
"/foo.dep.wwi.new><-I><", work,
"/foo.bar.wwi.new><-o><", work, "/foo.bar.wwi.new><-o><", work,
"/foo.bar.s.new><", work, "/foo.bar.unit.new>\n"); "/foo.bar.s.new><", work, "/foo.bar.unit.new>\n");
let wantassembler: str = strings.concat( let wantassembler: str = strings.concat(
@@ -1461,15 +1524,20 @@ fn writediamond(td: str, reverse: bool) str = {
atrace = testenv.readfile(assemblertrace); atrace = testenv.readfile(assemblertrace);
let commandlink: str = testenv.readfile(linkertrace); let commandlink: str = testenv.readfile(linkertrace);
let wantcommandcompiler: str = strings.concat( let wantcommandcompiler: str = strings.concat(
"BEGIN<-c><-I><", commandwork, "BEGIN<--package-init-symbol>",
"<__ww..pkg.p.foo.dep.v0.r0.init><-c><-I><", commandwork,
"/foo.dep.wwi.new><-o><", commandwork, "/foo.dep.wwi.new><-o><", commandwork,
"/foo.dep.s.new><", commandwork, "/foo.dep.unit.new>\n", "/foo.dep.s.new><", commandwork, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", commandwork, "BEGIN<--package-init-symbol>",
"/foo.dep.wwi><-I><", commandwork, "<__ww..pkg.p.foo.bar.v0.r0.init><-c>",
"<--import><foo.dep><", commandwork,
"/foo.dep.wwi.new><-I><", commandwork,
"/foo.bar.wwi.new><-o><", commandwork, "/foo.bar.wwi.new><-o><", commandwork,
"/foo.bar.s.new><", commandwork, "/foo.bar.unit.new>\n", "/foo.bar.s.new><", commandwork, "/foo.bar.unit.new>\n",
"BEGIN<--entry><-c><--import><foo.bar><", commandwork, "BEGIN<--entry><--package-init-symbol>",
"/foo.bar.wwi><-I><", commandwork, "<__ww..pkg.p.foo.cmd.v0.r0.init><--init-dispatch-symbol>",
"<__ww..dispatch><-c><--import><foo.bar><", commandwork,
"/foo.bar.wwi.new><-I><", commandwork,
"/foo.cmd.wwi.new><-o><", commandwork, "/foo.cmd.wwi.new><-o><", commandwork,
"/foo.cmd.s.new><", commandwork, "/foo.cmd.unit.new>\n"); "/foo.cmd.s.new><", commandwork, "/foo.cmd.unit.new>\n");
let wantcommandassembler: str = strings.concat( let wantcommandassembler: str = strings.concat(
@@ -1478,10 +1546,12 @@ fn writediamond(td: str, reverse: bool) str = {
"BEGIN<-o><", commandwork, "/foo.bar.o.new><", commandwork, "BEGIN<-o><", commandwork, "/foo.bar.o.new><", commandwork,
"/foo.bar.s.new>\n", "/foo.bar.s.new>\n",
"BEGIN<-o><", commandwork, "/foo.cmd.o.new><", commandwork, "BEGIN<-o><", commandwork, "/foo.cmd.o.new><", commandwork,
"/foo.cmd.s.new>\n"); "/foo.cmd.s.new>\n",
"BEGIN<-o><", commandwork, "/foo.cmd.init.o.new><", commandwork,
"/foo.cmd.init.s.new>\n");
let wantcommandlink: str = strings.concat("BEGIN<-o><", commandbin, let wantcommandlink: str = strings.concat("BEGIN<-o><", commandbin,
"><", commandwork, "/foo.cmd.a><", commandwork, ".new><", commandwork, "/foo.cmd.a.new><", commandwork,
"/foo.bar.a><", commandwork, "/foo.dep.a><", runtime, "/foo.bar.a.new><", commandwork, "/foo.dep.a.new><", runtime,
"/libwwrt.a>\n"); "/libwwrt.a>\n");
if (out.termination != exec.termination.EXIT || out.code != 0 if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0 || out.stderr.len != 0
@@ -1510,6 +1580,18 @@ fn writediamond(td: str, reverse: bool) str = {
}; }; }; };
ai += 1; ai += 1;
}; };
ai = 0;
for (ai < initsuffixes.len) {
let initbytes: str = testenv.readfile(strings.concat(commandwork,
"/foo.cmd", initsuffixes[ai]));
if (si == 0) {
commandinitreference[ai] = strings.dup(initbytes);
} else { if (!testenv.same(commandinitreference[ai], initbytes)) {
fail("package-autoaction",
"Cstage and WWstage dispatcher artifacts differ");
}; };
ai += 1;
};
let commandrun: []str = [commandbin]; let commandrun: []str = [commandbin];
if (code(td, strings.concat("auto-command-run-", tags[si]), if (code(td, strings.concat("auto-command-run-", tags[si]),
commandrun) != 42) { commandrun) != 42) {
@@ -1544,11 +1626,14 @@ fn writediamond(td: str, reverse: bool) str = {
ctrace = testenv.readfile(compilertrace); ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace); atrace = testenv.readfile(assemblertrace);
let wantnooutcompiler: str = strings.concat( let wantnooutcompiler: str = strings.concat(
"BEGIN<-c><-I><", nooutwork, "BEGIN<--package-init-symbol>",
"<__ww..pkg.p.foo.dep.v0.r0.init><-c><-I><", nooutwork,
"/foo.dep.wwi.new><-o><", nooutwork, "/foo.dep.wwi.new><-o><", nooutwork,
"/foo.dep.s.new><", nooutwork, "/foo.dep.unit.new>\n", "/foo.dep.s.new><", nooutwork, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", nooutwork, "BEGIN<--package-init-symbol>",
"/foo.dep.wwi><-I><", nooutwork, "<__ww..pkg.p.foo.bar.v0.r0.init><-c>",
"<--import><foo.dep><", nooutwork,
"/foo.dep.wwi.new><-I><", nooutwork,
"/foo.bar.wwi.new><-o><", nooutwork, "/foo.bar.wwi.new><-o><", nooutwork,
"/foo.bar.s.new><", nooutwork, "/foo.bar.unit.new>\n"); "/foo.bar.s.new><", nooutwork, "/foo.bar.unit.new>\n");
let wantnooutassembler: str = strings.concat( let wantnooutassembler: str = strings.concat(
@@ -1660,11 +1745,14 @@ fn writediamond(td: str, reverse: bool) str = {
testenv.runcommandenv(td, td, strings.concat("auto-asm-long-output-", testenv.runcommandenv(td, td, strings.concat("auto-asm-long-output-",
tags[si]), asmav, env, tmo(), &out); tags[si]), asmav, env, tmo(), &out);
let wantasmcompiler: str = strings.concat( let wantasmcompiler: str = strings.concat(
"BEGIN<-c><-I><", asmwork, "BEGIN<--package-init-symbol>",
"<__ww..pkg.p.foo.dep.v0.r0.init><-c><-I><", asmwork,
"/foo.dep.wwi.new><-o><", asmwork, "/foo.dep.wwi.new><-o><", asmwork,
"/foo.dep.s.new><", asmwork, "/foo.dep.unit.new>\n", "/foo.dep.s.new><", asmwork, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", asmwork, "BEGIN<--package-init-symbol>",
"/foo.dep.wwi><-I><", asmwork, "<__ww..pkg.p.foo.bar.v0.r0.init><-c>",
"<--import><foo.dep><", asmwork,
"/foo.dep.wwi.new><-I><", asmwork,
"/foo.bar.wwi.new><-o><", asmwork, "/foo.bar.wwi.new><-o><", asmwork,
"/foo.bar.s.new><", asmwork, "/foo.bar.unit.new>\n"); "/foo.bar.s.new><", asmwork, "/foo.bar.unit.new>\n");
if (out.termination != exec.termination.EXIT || out.code != 0 if (out.termination != exec.termination.EXIT || out.code != 0
@@ -1752,7 +1840,8 @@ fn writediamond(td: str, reverse: bool) str = {
ctrace = testenv.readfile(compilertrace); ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace); atrace = testenv.readfile(assemblertrace);
let wantprivatecompiler: str = strings.concat( let wantprivatecompiler: str = strings.concat(
"BEGIN<-c><-I><", work, "BEGIN<--package-init-symbol>",
"<__ww..pkg.p.foo.dep.v0.r0.init><-c><-I><", work,
"/foo.dep.wwi.new><-o><", work, "/foo.dep.wwi.new><-o><", work,
"/foo.dep.s.new><", work, "/foo.dep.unit.new>\n"); "/foo.dep.s.new><", work, "/foo.dep.unit.new>\n");
let wantprivateassembler: str = strings.concat( let wantprivateassembler: str = strings.concat(
@@ -1778,11 +1867,14 @@ fn writediamond(td: str, reverse: bool) str = {
ctrace = testenv.readfile(compilertrace); ctrace = testenv.readfile(compilertrace);
atrace = testenv.readfile(assemblertrace); atrace = testenv.readfile(assemblertrace);
let wantexportcompiler: str = strings.concat( let wantexportcompiler: str = strings.concat(
"BEGIN<-c><-I><", work, "BEGIN<--package-init-symbol>",
"<__ww..pkg.p.foo.dep.v0.r0.init><-c><-I><", work,
"/foo.dep.wwi.new><-o><", work, "/foo.dep.wwi.new><-o><", work,
"/foo.dep.s.new><", work, "/foo.dep.unit.new>\n", "/foo.dep.s.new><", work, "/foo.dep.unit.new>\n",
"BEGIN<-c><--import><foo.dep><", work, "BEGIN<--package-init-symbol>",
"/foo.dep.wwi><-I><", work, "<__ww..pkg.p.foo.bar.v0.r0.init><-c>",
"<--import><foo.dep><", work,
"/foo.dep.wwi.new><-I><", work,
"/foo.bar.wwi.new><-o><", work, "/foo.bar.wwi.new><-o><", work,
"/foo.bar.s.new><", work, "/foo.bar.unit.new>\n"); "/foo.bar.s.new><", work, "/foo.bar.unit.new>\n");
let wantexportassembler: str = strings.concat( let wantexportassembler: str = strings.concat(

View File

@@ -101,7 +101,8 @@ fn samefile(label: str, what: str, a: str, b: str) void = {
i += 1; i += 1;
}; };
// cs==ww (rule 10): per-package artifacts + the final binary // cs==ww (rule 10): per-source-action artifacts + the final binary;
// sepinit owns the command root's additional dispatcher artifacts.
let sufs: []str = [".s", ".wwi", ".a", ".unit.ww"]; let sufs: []str = [".s", ".wwi", ".a", ".unit.ww"];
let p: i32 = 0; let p: i32 = 0;
for (p < pkgs.len) { for (p < pkgs.len) {
@@ -164,7 +165,8 @@ fn samefile(label: str, what: str, a: str, b: str) void = {
s += 1; s += 1;
}; };
// cs==ww for the complete package artifacts, including the root archive. // cs==ww for every source-action artifact, including the root archive;
// sepinit owns the command root's additional dispatcher artifacts.
let pkgs: []str = ["c", "__root"]; let pkgs: []str = ["c", "__root"];
let sufs: []str = [".s", ".wwi", ".a", ".unit.ww"]; let sufs: []str = [".s", ".wwi", ".a", ".unit.ww"];
let p: i32 = 0; let p: i32 = 0;

View File

@@ -209,11 +209,12 @@ fn depmainrow(label: str, entry: str, want: i32, deps: str,
l += 1; l += 1;
}; };
// Both the dependency and raw explicit root emit complete package artifacts. // Both the dependency and raw explicit root emit complete source-action
// artifacts; sepinit owns command-dispatcher artifact parity.
let parts: []str = ["aa.s", "aa.wwi", "aa.a", "aa.unit.ww", let parts: []str = ["aa.s", "aa.wwi", "aa.a", "aa.unit.ww",
"__root.s", "__root.wwi", "__root.a", "__root.unit.ww"]; "__root.s", "__root.wwi", "__root.a", "__root.unit.ww"];
// cs==ww (rule 10) per layout over the complete package-artifact table // cs==ww (rule 10) per layout over the source-action artifact table
let l2: i32 = 0; let l2: i32 = 0;
for (l2 < 2) { for (l2 < 2) {
let p: i32 = 0; let p: i32 = 0;

View File

@@ -4,8 +4,9 @@ package seplink_test;
// retired native carriers test/wcc/989_separchive_run.c and // retired native carriers test/wcc/989_separchive_run.c and
// 989_sepcycle_dup.c; every assertion preserved. // 989_sepcycle_dup.c; every assertion preserved.
// //
// archive — `ww build` wraps every package action's .o in the existing // archive — `ww build` wraps every library package action's .o in the existing
// deterministic single-member .a and links the root archive first: // deterministic single-member .a; the command root also owns its fixed
// dispatcher member, and the linker receives that root archive first:
// build+run exit 7 both stages; __root.a + helper.a present; both archives // build+run exit 7 both stages; __root.a + helper.a present; both archives
// are byte-identical across stages; 3 cold cstage rebuilds emit // are byte-identical across stages; 3 cold cstage rebuilds emit
// byte-identical archives (zeroed mtime/uid/gid, fixed mode/member — // byte-identical archives (zeroed mtime/uid/gid, fixed mode/member —

View File

@@ -81,14 +81,14 @@ main(void)
} }
/* #93 sep layout: build an explicit command source via `ww build`, /* #93 sep layout: build an explicit command source via `ww build`,
* then independently link <stem>.sepwork/__root.o with both linkers. * then independently link <stem>.sepwork/__root.a with both linkers.
* The direct getpid call guarantees one dynamic libc relocation. */ * The direct getpid call guarantees one dynamic libc relocation. */
char cmd[4096], src[256], stem[256], scratch[320]; char cmd[4096], src[256], stem[256], scratch[320];
char obj[384], co[256], wo[256]; char archive[384], co[256], wo[256];
snprintf(src, sizeof src, "%s/dyn.ww", td); snprintf(src, sizeof src, "%s/dyn.ww", td);
snprintf(stem, sizeof stem, "%s/built", td); snprintf(stem, sizeof stem, "%s/built", td);
snprintf(scratch, sizeof scratch, "%s.sepwork", stem); snprintf(scratch, sizeof scratch, "%s.sepwork", stem);
snprintf(obj, sizeof obj, "%s/__root.o", scratch); snprintf(archive, sizeof archive, "%s/__root.a", scratch);
snprintf(co, sizeof co, "%s/c", td); snprintf(co, sizeof co, "%s/c", td);
snprintf(wo, sizeof wo, "%s/w", td); snprintf(wo, sizeof wo, "%s/w", td);
@@ -125,7 +125,7 @@ main(void)
snprintf(cmd, sizeof cmd, snprintf(cmd, sizeof cmd,
"%s/w6l -o %s %s -L %s " "%s/w6l -o %s %s -L %s "
"-l c %s/out/lib/libwwrt.a 2>/dev/null", "-l c %s/out/lib/libwwrt.a 2>/dev/null",
bin, co, obj, libdir, cwd); bin, co, archive, libdir, cwd);
if (runwait(cmd) != 0) { if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: C w6l errored\n"); fprintf(stderr, "w6l_ww-dyn FAIL: C w6l errored\n");
rc = 1; rc = 1;
@@ -135,7 +135,7 @@ main(void)
snprintf(cmd, sizeof cmd, snprintf(cmd, sizeof cmd,
"%s/w6l_ww -o %s %s -L %s " "%s/w6l_ww -o %s %s -L %s "
"-l c %s/out/lib/libwwrt.a 2>/dev/null", "-l c %s/out/lib/libwwrt.a 2>/dev/null",
bin, wo, obj, libdir, cwd); bin, wo, archive, libdir, cwd);
if (runwait(cmd) != 0) { if (runwait(cmd) != 0) {
fprintf(stderr, "w6l_ww-dyn FAIL: ww w6l errored\n"); fprintf(stderr, "w6l_ww-dyn FAIL: ww w6l errored\n");
rc = 1; rc = 1;

View File

@@ -1,4 +1,4 @@
//ww:error "#129 A.2 scope" //ww:run
package main; package main;
type s1t = str; type s1t = str;
type s2t = s1t; type s2t = s1t;

View File

@@ -1,10 +1,6 @@
//ww:error "runtime initializer unsupported (alloc/call; rule 7)" //ww:run
// Module-scope alloc initializer: no DATAW slot exists for a // A module-scope allocation is evaluated once by the package task before
// runtime-computed global, so pre-reject every reference died at // main; its pointer remains live in the zero-backed package variable.
// LINK time ("undefined reference to 'main.gp'") — the checker now
// rejects at the declaration on both frontends (Hare model:
// ref/harec/src/check.c:4360 rejects non-compile-time-evaluable
// global initializers; ww has no @init path).
package main; package main;
type box = struct { s: str, n: int }; type box = struct { s: str, n: int };
let gp = alloc(box { s = "hi", n = 1 })!; let gp = alloc(box { s = "hi", n = 1 })!;

View File

@@ -1,7 +1,9 @@
//ww:error "slice-of-{str,slice,tagged} literal static-init unsupported" //ww:run
package main; package main;
let G: []str = ["aa", "bbb"]; let G: []str = ["aa", "bbb"];
export fn main() i32 = { export fn main() i32 = {
if (len(G[0]) != 2) { return 1; }; if (G.len != 2 || G.cap != 2) { return 1; };
if (len(G[0]) != 2) { return 2; };
if (len(G[1]) != 3) { return 3; };
return 0; return 0;
}; };

View File

@@ -1,7 +1,12 @@
//ww:error "repeat with nested-array elements" //ww:run
// #156/rule-7 carrier: a `...` repeat marker with a nested-array element must // A fixed outer array gives the nested-row repeat an exact target length.
// REJECT on both stages (no consumer needs it; powers_of_ten is fully // Package initialization evaluates the row once and fills every remaining row.
// enumerated). From test/wcc/919_array_static_init_run.c nested_ellipsis_reject.
package main; package main;
let A: [4][2]u64 = [[1u64, 2u64]...]; let A: [4][2]u64 = [[1u64, 2u64]...];
export fn main() i32 = { return 0; }; export fn main() i32 = {
if (A[0][0] != 1 || A[0][1] != 2) { return 1; };
if (A[1][0] != 1 || A[1][1] != 2) { return 2; };
if (A[2][0] != 1 || A[2][1] != 2) { return 3; };
if (A[3][0] != 1 || A[3][1] != 2) { return 4; };
return 0;
};

View File

@@ -1,6 +1,5 @@
//ww:error "runtime initializer unsupported (alloc/call; rule 7)" //ww:run-exit 7
// The call sibling of alias_infptr_global's alloc reject: a // A module-scope call initializer executes once in the package task.
// module-scope call initializer has no link-time data either.
package main; package main;
fn mk() i64 = { return 7; }; fn mk() i64 = { return 7; };
let gc: i64 = mk(); let gc: i64 = mk();

View File

@@ -1,4 +1,4 @@
//ww:error "slice-of-{str,slice,tagged} literal static-init unsupported" //ww:run-exit 2
package main; package main;
let g: []str = ["a", "b"]; let g: []str = ["a", "b"];
export fn main() i32 = { return g.len: i32; }; export fn main() i32 = { return g.len: i32; };

View File

@@ -1,5 +1,12 @@
//ww:error "unsupported variant init" //ww:run-exit 11
package main; package main;
type u = (int | bool | str); type u = (int | bool | str);
let g: u = true: u; let g: u = true: u;
export fn main() i32 = { return 0; }; export fn main() i32 = {
match (g) {
case let n: int => return n: i32;
case let b: bool => return 11;
case let s: str => return 22;
};
return 99;
};

View File

@@ -1,5 +1,12 @@
//ww:error "unsupported variant init" //ww:run-exit 7
package main; package main;
type u = (int | bool | str); type u = (int | bool | str);
let g: u = 7: u; let g: u = 7: u;
export fn main() i32 = { return 0; }; export fn main() i32 = {
match (g) {
case let n: int => return n: i32;
case let b: bool => return 11;
case let s: str => return 22;
};
return 99;
};

View File

@@ -1,4 +1,18 @@
//ww:error "tagged-union array element static-init needs a zero/int payload" //ww:run-exit 2
package main; package main;
let gs: [2](int | str) = ["hi", 0]; let gs: [2](int | str) = ["hi", 0];
export fn main() int = { return 0; }; export fn main() int = {
match (gs[0]) {
case let n: int => return 10;
case let s: str => {
if (s.len != 2 || s[0] != 104u8 || s[1] != 105u8) {
return 11;
};
};
};
match (gs[1]) {
case let n: int => { if (n != 0) { return 12; }; };
case let s: str => return 13;
};
return 2;
};

View File

@@ -1,5 +1,15 @@
//ww:error "tagged-union struct-field" //ww:run-exit 2
package main; package main;
type sbox = struct { s: (int | str) }; type sbox = struct { s: (int | str) };
let g: sbox = sbox { s = "hi" }; let g: sbox = sbox { s = "hi" };
export fn main() int = { return 0; }; export fn main() int = {
match (g.s) {
case let n: int => return 10;
case let s: str => {
if (s.len != 2 || s[0] != 104u8 || s[1] != 105u8) {
return 11;
};
};
};
return 2;
};

View File

@@ -1,9 +1,11 @@
//ww:error "slice-of-{str,slice,tagged} literal static-init unsupported" //ww:run-exit 0
package main; package main;
type ms0 = str; type ms0 = str;
type ms = ms0; type ms = ms0;
let G: []ms = ["aa", "bbb"]; let G: []ms = ["aa", "bbb"];
export fn main() i32 = { export fn main() i32 = {
if (len(G[0]) != 2) { return 1; }; if (G.len != 2 || G.cap != 2) { return 1; };
if (len(G[0]) != 2) { return 2; };
if (len(G[1]) != 3) { return 3; };
return 0; return 0;
}; };

View File

@@ -1,4 +1,4 @@
//ww:error "unsupported element init (int/str literals only; rule 7)" //ww:run-exit 4
package main; package main;
let g: ((void | size), i64) = (5, 4); let g: ((void | size), i64) = (5, 4);
export fn main() i32 = { return g.1: i32; }; export fn main() i32 = { return g.1: i32; };

View File

@@ -1,4 +1,4 @@
//ww:error "unsupported element init (int/str literals only; rule 7)" //ww:run
package main; package main;
let g: (f64, i64) = (2.5, 4); let g: (f64, i64) = (2.5, 4);
export fn main() i32 = { export fn main() i32 = {

View File

@@ -2,15 +2,15 @@ package collide_test;
// Cross-module leaf-name collision observers on both driver stages. // Cross-module leaf-name collision observers on both driver stages.
// Ports of the retired native carriers test/wcc/989_fnptrcollide_run.c // Ports of the retired native carriers test/wcc/989_fnptrcollide_run.c
// and 989_barefn_collide_run.c; every assertion preserved. // and 989_barefn_collide_run.c; their collision teeth remain explicit under
// the package-initialization contract.
// //
// fnptrcollide (#14 F7-c7) — a data global `slot: i64` whose LEAF // fnptrcollide (#14 F7-c7) — a data global `slot: i64` whose leaf
// collides with the imported fn bar.slot must FAIL to build on BOTH // collides with imported fn bar.slot. `let fp: *i64 = &slot` is valid
// stages: type-keyed nodefnptr refuses to fold `&slot` into the fn's // runtime package initialization: both stages must address main.slot,
// TEXT reloc. Pre-fix wwstage was name-keyed and silently BUILT it // initialize fp before main, and run 7. The old loud-reject expectation
// (a DATAR to the fn symbol — cat-A: cs loud-fails, ww builds). The // predated runtime package lets. The collision still requires an import,
// collision REQUIRES the import path (imported fn leaf vs local data // and name-keyed lowering to bar.slot is rejected by exact asm/runtime pins.
// global), so a single-file fixture cannot express it.
// //
// barefn (#84/#24a) — a `package main;` root `fn run` coexists with // barefn (#84/#24a) — a `package main;` root `fn run` coexists with
// imported aa.run: build+run exit 9 on BOTH stages while an explicit // imported aa.run: build+run exit 9 on BOTH stages while an explicit
@@ -52,32 +52,86 @@ fn runcode(dir: str, name: str, argv: []str) i32 = {
}; };
@test fn fnptrcollide() void = { @test fn fnptrcollide() void = {
let td: str = testenv.fresh();
let bar: str = strings.concat(td, "/bar");
let main: str = strings.concat(td, "/main");
assert(os.mkdir(bar, 493) == 0);
assert(os.mkdir(main, 493) == 0);
testenv.writefile(strings.concat(bar, "/bar.ww"), strings.concat(
"package bar;\n",
"export fn slot() i64 = { return 99; };\n"));
testenv.writefile(strings.concat(main, "/main.ww"), strings.concat(
"package main;\n",
"import bar;\n",
"let slot: i64 = 7;\n",
"let fp: *i64 = &slot;\n",
"export fn main() int = { let _ = bar.slot(); ",
"return (*fp): int; };\n"));
let drvs: []str = ["ww", "ww_ww"]; let drvs: []str = ["ww", "ww_ww"];
let tags: []str = ["cs", "ww"];
let works: []str = [strings.concat(td, "/work-c"),
strings.concat(td, "/work-ww")];
let progs: []str = [strings.concat(td, "/prog.cs"),
strings.concat(td, "/prog.ww")];
let i: i32 = 0; let i: i32 = 0;
for (i < 2) { for (i < 2) {
let td: str = testenv.fresh(); assert(os.mkdir(works[i], 493) == 0);
assert(os.mkdir(strings.concat(td, "/bar"), 493) == 0); let av: []str = [testenv.driver(drvs[i]), "build", "-w", works[i],
testenv.writefile(strings.concat(td, "/bar/bar.ww"), "-I", td, "-o", progs[i], "main"];
strings.concat( if (runcode(td, strings.concat("build_", tags[i]), av) != 0) {
"package bar;\n", fail("fnptrcollide", strings.concat(drvs[i], " build failed"));
"export fn slot() i64 = { return 99; };\n")); };
testenv.writefile(strings.concat(td, "/main.ww"), strings.concat( let rav: []str = [progs[i]];
"package main;\n", if (runcode(td, strings.concat("run_", tags[i]), rav) != 7) {
"import bar;\n", fail("fnptrcollide", strings.concat(drvs[i], " exit != 7"));
"let slot: i64 = 7;\n", };
"let fp: *i64 = &slot;\n", let sourceasm: str = testenv.readfile(strings.concat(works[i],
"export fn main() int = { let _ = bar.slot(); ", "/main.s"));
"return (*fp): int; };\n")); let initasm: str = testenv.readfile(strings.concat(works[i],
let av: []str = [testenv.driver(drvs[i]), "build", "-I", "bar", "/main.init.s"));
"main.ww"]; let barasm: str = testenv.readfile(strings.concat(works[i], "/bar.s"));
if (runcode(td, strings.concat("build_", drvs[i]), av) == 0) { if (testenv.occurrences(sourceasm, "DATAW main.slot(SB)") != 1
fail("fnptrcollide", strings.concat(drvs[i], " built ok, ", || testenv.occurrences(sourceasm, "DATAW main.fp(SB)") != 1
"expected the leaf-name collision to be rejected (#14 -- ", || testenv.occurrences(sourceasm,
"&slot mis-folded to the fn TEXT reloc)")); "CALL\t__ww..dispatch(SB)") != 1
|| testenv.occurrences(sourceasm,
"LEAQ\tmain.slot(SB), AX") != 1
|| testenv.occurrences(sourceasm,
"MOVQ\tAX, main.fp(SB)") != 1
|| testenv.has(sourceasm, "LEAQ\tbar.slot(SB)")
|| testenv.has(sourceasm, "DATAR main.fp")
|| testenv.occurrences(initasm,
"CALL\t__ww..pkg.p.bar.v0.r0.init(SB)") != 1
|| testenv.occurrences(initasm,
"CALL\t__ww..pkg.p.main.v0.r0.init(SB)") != 1
|| testenv.occurrences(barasm, "TEXT bar.slot,") != 1) {
fail("fnptrcollide", "runtime initializer selected the colliding fn");
}; };
testenv.clean(td);
i += 1; i += 1;
}; };
let wantdispatch: str = strings.concat(
"//ww:init-root __ww..pkg.p.main.v0.r0.init\n",
"//ww:init-call __ww..pkg.p.bar.v0.r0.init\n",
"//ww:init-call __ww..pkg.p.main.v0.r0.init\n");
if (!testenv.same(wantdispatch, testenv.readfile(strings.concat(works[0],
"/main.init.unit.ww")))) {
fail("fnptrcollide", "dispatcher did not initialize dependency first");
};
let names: []str = ["bar.unit.ww", "bar.wwi", "bar.s", "bar.o", "bar.a",
"main.unit.ww", "main.wwi", "main.s", "main.o", "main.a",
"main.init.unit.ww", "main.init.s", "main.init.o"];
i = 0;
for (i < names.len) {
if (!testenv.same(testenv.readfile(strings.concat(works[0], "/", names[i])),
testenv.readfile(strings.concat(works[1], "/", names[i])))) {
fail("fnptrcollide", strings.concat(names[i], " differs by stage"));
};
i += 1;
};
if (!testenv.same(testenv.readfile(progs[0]), testenv.readfile(progs[1]))) {
fail("fnptrcollide", "Cstage/WWstage binaries differ");
};
testenv.clean(td);
}; };
// __root.s then aa.s: the carrier's fixed #93 sep concat order. // __root.s then aa.s: the carrier's fixed #93 sep concat order.