wcc/ww: reject compound *p OP= v through a *tagged, like cstage (#18)

The wwstage compound-deref arm narrowed the store for scalar pointees and
otherwise emitted a single MOVQ, so `*p OP= v` with p:*tagged clobbered
one word (the tag) and returned -- silently miscompiling what cstage
already rejects. A compound op on a whole union is nonsense. Gate the arm
on a scalar pointee size and let a tagged pointee fall through to the
existing assign-resolver reject, the byte-id twin of the cstage fatal.
cstage is unchanged.

This closes the deref member of the compound-on-tagged class; the index
and ident members (gs[i] OP= v, g OP= v) reject in a follow-up (#20/#21).
This commit is contained in:
2026-06-14 00:40:34 +09:00
parent 323607d1d0
commit 769be55905
5 changed files with 308 additions and 3 deletions

View File

@@ -31931,7 +31931,18 @@ fn cgassign(c: *cgen, n: *node) void = {
if (lhs != nil) {
if (lhs.kind == nkind.N_UN) {
if (lhs.op == tkind.TK_STAR) {
if (n.op != tkind.TK_ASSIGN) {
// Size gate (mirror cstage cgen.c handled=sz∈{1,2,4,8}):
// a tagged (or any non-scalar) pointee is not a
// meaningful compound target — skip this single-word
// store-and-return arm so `*p OP= v` on *tagged falls
// through to the assign-resolver's loud TY_TAGGED reject
// (#18). Without it the MOVQ default below clobbers the
// tag word and returns: a silent miscompile.
let psz: i32 = 8;
let lt: *tinfo = lhs.type_: *tinfo;
if (lt != nil) { psz = lt.size: i32; };
let scalarpointee: bool = (psz == 1 || psz == 2 || psz == 4 || psz == 8);
if (n.op != tkind.TK_ASSIGN && scalarpointee) {
let inner: *node = lhs.lhs;
let loadop: str = "MOVQ";
let storeop: str = "MOVQ";

View File

@@ -8585,7 +8585,18 @@ fn cgassign(c: *cgen, n: *node) void = {
if (lhs != nil) {
if (lhs.kind == nkind.N_UN) {
if (lhs.op == tkind.TK_STAR) {
if (n.op != tkind.TK_ASSIGN) {
// Size gate (mirror cstage cgen.c handled=sz∈{1,2,4,8}):
// a tagged (or any non-scalar) pointee is not a
// meaningful compound target — skip this single-word
// store-and-return arm so `*p OP= v` on *tagged falls
// through to the assign-resolver's loud TY_TAGGED reject
// (#18). Without it the MOVQ default below clobbers the
// tag word and returns: a silent miscompile.
let psz: i32 = 8;
let lt: *tinfo = lhs.type_: *tinfo;
if (lt != nil) { psz = lt.size: i32; };
let scalarpointee: bool = (psz == 1 || psz == 2 || psz == 4 || psz == 8);
if (n.op != tkind.TK_ASSIGN && scalarpointee) {
let inner: *node = lhs.lhs;
let loadop: str = "MOVQ";
let storeop: str = "MOVQ";

View File

@@ -31931,7 +31931,18 @@ fn cgassign(c: *cgen, n: *node) void = {
if (lhs != nil) {
if (lhs.kind == nkind.N_UN) {
if (lhs.op == tkind.TK_STAR) {
if (n.op != tkind.TK_ASSIGN) {
// Size gate (mirror cstage cgen.c handled=sz∈{1,2,4,8}):
// a tagged (or any non-scalar) pointee is not a
// meaningful compound target — skip this single-word
// store-and-return arm so `*p OP= v` on *tagged falls
// through to the assign-resolver's loud TY_TAGGED reject
// (#18). Without it the MOVQ default below clobbers the
// tag word and returns: a silent miscompile.
let psz: i32 = 8;
let lt: *tinfo = lhs.type_: *tinfo;
if (lt != nil) { psz = lt.size: i32; };
let scalarpointee: bool = (psz == 1 || psz == 2 || psz == 4 || psz == 8);
if (n.op != tkind.TK_ASSIGN && scalarpointee) {
let inner: *node = lhs.lhs;
let loadop: str = "MOVQ";
let storeop: str = "MOVQ";