ww: finish initialization validation parity
This commit is contained in:
@@ -7731,6 +7731,11 @@ fn initfloatstatic(n: *syntax.node) bool = {
|
||||
|
||||
fn inittaggedrawstatic(u: *syntax.tinfo, n: *syntax.node) bool = {
|
||||
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
|
||||
|| r == nil || !variantpresent(u.params, 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) {
|
||||
if (!variantpresent(u.params, r.type_: *syntax.tinfo)) { return false; };
|
||||
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
|
||||
|| ru.kind == syntax.tykind.TY_UNTYPED_STR
|
||||
|| ru.kind == syntax.tykind.TY_SLICE)) {
|
||||
return r.kind == syntax.nkind.N_STRLIT;
|
||||
return false;
|
||||
};
|
||||
return inittaggedrawstatic(u, r);
|
||||
};
|
||||
@@ -7882,6 +7891,78 @@ fn initexprstatic(t: *syntax.tinfo, n: *syntax.node) bool = {
|
||||
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 {
|
||||
node: *syntax.node,
|
||||
next: *initwalkitem,
|
||||
@@ -8266,6 +8347,10 @@ fn initlowerpackage(c: *checker, file: *syntax.node) void = {
|
||||
else { if (d.rhs != nil) {
|
||||
dt = d.rhs.type_: *syntax.tinfo;
|
||||
}; };
|
||||
if (!initvalidateslicerepeats(c, dt, d.rhs)) {
|
||||
d = d.next;
|
||||
continue;
|
||||
};
|
||||
if (!initexprstatic(dt, d.rhs)) {
|
||||
d.runtimeinit = 1;
|
||||
nruntime += 1u64;
|
||||
@@ -8906,8 +8991,22 @@ fn checkfile(c: *checker, file: *syntax.node) void = {
|
||||
// rhs returns false silently and is left untouched).
|
||||
// Stamp LAST — checkletassign consumes the pre-stamp type.
|
||||
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;
|
||||
if (!foldintliteral(d.rhs, &dv)) {
|
||||
if (!preservetaggedcast && !foldintliteral(d.rhs, &dv)) {
|
||||
if (evaldefconst(c, d.rhs, &dv, 0)) {
|
||||
stampintlit(d.rhs, dv);
|
||||
};
|
||||
|
||||
@@ -5959,11 +5959,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
&& !g.pkg[products[0].root].failed
|
||||
&& !seprootiscommand(&g.pkg[products[0].root]);
|
||||
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
|
||||
&& sepvalidateworkdirowners(g, scratch) < 0) { return 1; };
|
||||
let ci: i32 = 0;
|
||||
@@ -6064,6 +6059,11 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
producti += 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
|
||||
// coordinator completion markers or persistent vouchers are changed.
|
||||
let createdoutput: sepcreateddirs;
|
||||
|
||||
Reference in New Issue
Block a user