w6c+w6c_ww: loud-stop non-ident/non-local append spread source (#34 review)
A spread whose source was not a local ident fell PAST the spread arm: cstage continued into the single-value stores with the N_SPREAD node (garbage store), wwstage silently SKIPPED the value entirely — a silent, cs!=ww-divergent miscompile (append(ys, f()...): cs exit 0 / ww exit 144, want 3), reachable for every element kind and predating #34 for scalars. Both stages now rule-7 loud-stop the shape (deferred, task #37). Also pins the widener's already-tagged single-value source path (tagged_ident_src row, i64 + bool members) and adds the spread_call_loudstop BUILD_FAIL row — test 800 is now 15 rows / 43 fixtures.
This commit is contained in:
@@ -6357,6 +6357,20 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
fatal("#34: append() element kind "
|
fatal("#34: append() element kind "
|
||||||
"unsupported (rule-7)");
|
"unsupported (rule-7)");
|
||||||
for (Node *vn = sn->next; vn; vn = vn->next) {
|
for (Node *vn = sn->next; vn; vn = vn->next) {
|
||||||
|
/* #34 review: a spread whose source is not a
|
||||||
|
* local ident used to fall PAST the spread arm
|
||||||
|
* into the single-value stores with the
|
||||||
|
* N_SPREAD node (cstage garbage store; wwstage
|
||||||
|
* silently SKIPPED it — divergent). Deferred
|
||||||
|
* source shape, task #37. */
|
||||||
|
if (vn->kind == N_SPREAD) {
|
||||||
|
if (!vn->lhs || vn->lhs->kind != N_IDENT)
|
||||||
|
fatal("#34: append() spread source "
|
||||||
|
"shape unsupported (rule-7)");
|
||||||
|
if (localfind(locals, vn->lhs->str) == 0)
|
||||||
|
fatal("#34: append() spread source "
|
||||||
|
"ident is not a local (rule-7)");
|
||||||
|
}
|
||||||
if (vn->kind == N_SPREAD &&
|
if (vn->kind == N_SPREAD &&
|
||||||
vn->lhs && vn->lhs->kind == N_IDENT) {
|
vn->lhs && vn->lhs->kind == N_IDENT) {
|
||||||
int it_off = localfind(locals, vn->lhs->str);
|
int it_off = localfind(locals, vn->lhs->str);
|
||||||
|
|||||||
@@ -23942,11 +23942,23 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
let vn: *node = sn.next;
|
let vn: *node = sn.next;
|
||||||
for (vn != nil) {
|
for (vn != nil) {
|
||||||
if (vn.kind == nkind.N_SPREAD) {
|
if (vn.kind == nkind.N_SPREAD) {
|
||||||
|
// #34 review: a non-ident/non-local spread source used
|
||||||
|
// to be silently SKIPPED here while cstage fell past
|
||||||
|
// its spread arm into the single-value stores with the
|
||||||
|
// N_SPREAD node (garbage store) — divergent. Deferred
|
||||||
|
// source shape, task #37.
|
||||||
let it: *node = vn.lhs;
|
let it: *node = vn.lhs;
|
||||||
if (it == nil) { vn = vn.next; continue; };
|
if (it == nil || it.kind != nkind.N_IDENT) {
|
||||||
if (it.kind != nkind.N_IDENT) { vn = vn.next; continue; };
|
let m34p: str = "#34: append() spread source shape unsupported (rule-7)\n";
|
||||||
|
os.write(2, m34p.ptr, m34p.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
let itlocal: *local = localfindnode(c, it.str);
|
let itlocal: *local = localfindnode(c, it.str);
|
||||||
if (itlocal == nil) { vn = vn.next; continue; };
|
if (itlocal == nil) {
|
||||||
|
let m34q: str = "#34: append() spread source ident is not a local (rule-7)\n";
|
||||||
|
os.write(2, m34q.ptr, m34q.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
let it_off: i32 = itlocal.off;
|
let it_off: i32 = itlocal.off;
|
||||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
|||||||
@@ -4149,11 +4149,23 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
let vn: *node = sn.next;
|
let vn: *node = sn.next;
|
||||||
for (vn != nil) {
|
for (vn != nil) {
|
||||||
if (vn.kind == nkind.N_SPREAD) {
|
if (vn.kind == nkind.N_SPREAD) {
|
||||||
|
// #34 review: a non-ident/non-local spread source used
|
||||||
|
// to be silently SKIPPED here while cstage fell past
|
||||||
|
// its spread arm into the single-value stores with the
|
||||||
|
// N_SPREAD node (garbage store) — divergent. Deferred
|
||||||
|
// source shape, task #37.
|
||||||
let it: *node = vn.lhs;
|
let it: *node = vn.lhs;
|
||||||
if (it == nil) { vn = vn.next; continue; };
|
if (it == nil || it.kind != nkind.N_IDENT) {
|
||||||
if (it.kind != nkind.N_IDENT) { vn = vn.next; continue; };
|
let m34p: str = "#34: append() spread source shape unsupported (rule-7)\n";
|
||||||
|
os.write(2, m34p.ptr, m34p.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
let itlocal: *local = localfindnode(c, it.str);
|
let itlocal: *local = localfindnode(c, it.str);
|
||||||
if (itlocal == nil) { vn = vn.next; continue; };
|
if (itlocal == nil) {
|
||||||
|
let m34q: str = "#34: append() spread source ident is not a local (rule-7)\n";
|
||||||
|
os.write(2, m34q.ptr, m34q.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
let it_off: i32 = itlocal.off;
|
let it_off: i32 = itlocal.off;
|
||||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
|||||||
@@ -23942,11 +23942,23 @@ fn cgappend(c: *cgen, n: *node) void = {
|
|||||||
let vn: *node = sn.next;
|
let vn: *node = sn.next;
|
||||||
for (vn != nil) {
|
for (vn != nil) {
|
||||||
if (vn.kind == nkind.N_SPREAD) {
|
if (vn.kind == nkind.N_SPREAD) {
|
||||||
|
// #34 review: a non-ident/non-local spread source used
|
||||||
|
// to be silently SKIPPED here while cstage fell past
|
||||||
|
// its spread arm into the single-value stores with the
|
||||||
|
// N_SPREAD node (garbage store) — divergent. Deferred
|
||||||
|
// source shape, task #37.
|
||||||
let it: *node = vn.lhs;
|
let it: *node = vn.lhs;
|
||||||
if (it == nil) { vn = vn.next; continue; };
|
if (it == nil || it.kind != nkind.N_IDENT) {
|
||||||
if (it.kind != nkind.N_IDENT) { vn = vn.next; continue; };
|
let m34p: str = "#34: append() spread source shape unsupported (rule-7)\n";
|
||||||
|
os.write(2, m34p.ptr, m34p.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
let itlocal: *local = localfindnode(c, it.str);
|
let itlocal: *local = localfindnode(c, it.str);
|
||||||
if (itlocal == nil) { vn = vn.next; continue; };
|
if (itlocal == nil) {
|
||||||
|
let m34q: str = "#34: append() spread source ident is not a local (rule-7)\n";
|
||||||
|
os.write(2, m34q.ptr, m34q.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
let it_off: i32 = itlocal.off;
|
let it_off: i32 = itlocal.off;
|
||||||
let load_op: str = tnodeloadop(c, etnode, esz);
|
let load_op: str = tnodeloadop(c, etnode, esz);
|
||||||
emitline("\tSUBQ\t$8, SP\n");
|
emitline("\tSUBQ\t$8, SP\n");
|
||||||
|
|||||||
@@ -60,10 +60,18 @@
|
|||||||
* | scratch slots where wwstage dedups) |
|
* | scratch slots where wwstage dedups) |
|
||||||
* spread_str | append(ys, xs...) of []str + single | 7
|
* spread_str | append(ys, xs...) of []str + single | 7
|
||||||
* spread_tagged | append(ys, xs...) of [](i64|bool) | 42
|
* spread_tagged | append(ys, xs...) of [](i64|bool) | 42
|
||||||
|
* tagged_ident_src | append of ALREADY-TAGGED locals | 42
|
||||||
|
* | (i64 + bool members) — the widener's |
|
||||||
|
* | already-tagged source path. |
|
||||||
* struct_call_loudstop | append(xs, f()) struct-from-call is | BUILD_FAIL
|
* struct_call_loudstop | append(xs, f()) struct-from-call is | BUILD_FAIL
|
||||||
* | the deferred source shape — must |
|
* | the deferred source shape — must |
|
||||||
* | fail LOUD on both stages (rule-7), |
|
* | fail LOUD on both stages (rule-7), |
|
||||||
* | never silently store one word. |
|
* | never silently store one word. |
|
||||||
|
* spread_call_loudstop | append(ys, f()...) non-ident spread | BUILD_FAIL
|
||||||
|
* | source — pre-review this fell PAST |
|
||||||
|
* | the spread arm (cstage garbage store |
|
||||||
|
* | vs wwstage silent skip, divergent); |
|
||||||
|
* | now a rule-7 loud-stop (task #37). |
|
||||||
*
|
*
|
||||||
* Every non-BUILD_FAIL row also asserts cstage/wwstage asm byte-id,
|
* Every non-BUILD_FAIL row also asserts cstage/wwstage asm byte-id,
|
||||||
* which subsumes the frame-size canary (TEXT main,$N) — the @tagbase/
|
* which subsumes the frame-size canary (TEXT main,$N) — the @tagbase/
|
||||||
@@ -251,6 +259,28 @@ static const struct row rows[] = {
|
|||||||
"};\n",
|
"};\n",
|
||||||
42 },
|
42 },
|
||||||
|
|
||||||
|
{ "tagged_ident_src",
|
||||||
|
"package main;\n"
|
||||||
|
"type cell = (i64 | bool);\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet xs: []cell = [];\n"
|
||||||
|
"\tlet c: cell = 40i64;\n"
|
||||||
|
"\tlet b: cell = true;\n"
|
||||||
|
"\tappend(xs, c);\n"
|
||||||
|
"\tappend(xs, b);\n"
|
||||||
|
"\tlet r: i32 = 0;\n"
|
||||||
|
"\tmatch (xs[0]) {\n"
|
||||||
|
"\tcase let v: i64 => r += v: i32;\n"
|
||||||
|
"\tcase bool => r = 99;\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\tmatch (xs[1]) {\n"
|
||||||
|
"\tcase i64 => r = 98;\n"
|
||||||
|
"\tcase let w: bool => { if (w) { r += 2; }; };\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\treturn r;\n"
|
||||||
|
"};\n",
|
||||||
|
42 },
|
||||||
|
|
||||||
{ "struct_call_loudstop",
|
{ "struct_call_loudstop",
|
||||||
"package main;\n"
|
"package main;\n"
|
||||||
"type pt = struct { x: i32, y: i32, z: i64 };\n"
|
"type pt = struct { x: i32, y: i32, z: i64 };\n"
|
||||||
@@ -263,6 +293,21 @@ static const struct row rows[] = {
|
|||||||
"\treturn 0;\n"
|
"\treturn 0;\n"
|
||||||
"};\n",
|
"};\n",
|
||||||
BUILD_FAIL },
|
BUILD_FAIL },
|
||||||
|
|
||||||
|
{ "spread_call_loudstop",
|
||||||
|
"package main;\n"
|
||||||
|
"fn mks() []str = {\n"
|
||||||
|
"\tlet xs: []str = [];\n"
|
||||||
|
"\tlet a: str = \"abc\";\n"
|
||||||
|
"\tappend(xs, a);\n"
|
||||||
|
"\treturn xs;\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet ys: []str = [];\n"
|
||||||
|
"\tappend(ys, mks()...);\n"
|
||||||
|
"\treturn ys[0].len: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
BUILD_FAIL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user