w6c: tagged GLOBAL reassign emits the store

cstage silently dropped the store on reassigning a module-global
tagged union; mirror the landed wwstage emission (tag+payload to
g(SB) via the widener). Closes the cs half of the F8 #263 pair; the
repro row flips to cs==ww==correct. Task #41.
This commit is contained in:
2026-06-12 22:31:09 +09:00
parent ef7c0c1675
commit 5ff5f4b4fe
2 changed files with 37 additions and 26 deletions

View File

@@ -3,29 +3,20 @@
* module-GLOBAL tagged-union ident (`g = expr`) must store the new tag and
* payload, not clobber only one word.
*
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG): cgenexpr.ww cgassign's
* tagged-union reassignment arm delegated to cgwidentaggedstore ONLY for a
* LOCAL ident (localfindnode != nil). A module-global tagged ident (lc==nil)
* had no arm and fell through to the generic scalar store, which wrote one
* word into the tag slot and left the payload stale. cstage is ALSO wrong:
* it drops the store entirely (the new value never reaches g) — so the repro
* is cs≠ww residual until the cstage half lands (ww-core TASK #41).
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* cgassign's tagged-union reassignment arm delegated to the widener ONLY for a
* LOCAL ident. A module-global tagged ident had no arm and fell through to the
* generic scalar store (wwstage clobbered the tag word; cstage dropped the
* store entirely). The ww half landed in F8-c5; the cstage half (LEAQ g(SB),BX
* then cg_widen_tagged_store off BX) landed in ww-core TASK #41 — both stages
* now store the full box and the .s is byte-identical.
*
* THE WWSTAGE FIX (ww-runtime-correct): add the lc==nil arm — LEAQ g(SB),BX
* then cgwidentaggedstore stores tag+payload off BX (mirrors the local arm
* and the global-struct-field tagged arm). ww now stores the full box.
* cstage stays wrong → the rows assert ww-correct AND pin cstage's
* documented-wrong value (store dropped → g keeps its init), so the residual
* is never misread as a regression.
*
* Rows (per-driver expected — want_cs / want_ww):
* row | shape | cs | ww
* ----------------+----------------------------------------+----+----
* reassign_i64 | g:(i64|void)=0; g=7; match i64 | 0 | 7
* reassign_str | g:(str|i64)=0; g="hello"; match str→len | 88 | 5
* reassign_union | g:(i64|bool)=0; g=5; match i64 | 0 | 5
* (cstage drops the store → g keeps its init → the i64/void/i64-tag arm;
* #41 closes the cs side, at which point these become cs==ww.)
* Rows (cs==ww after the cstage half landed):
* row | shape | exit
* ----------------+----------------------------------------+-----
* reassign_i64 | g:(i64|void)=0; g=7; match i64 | 7
* reassign_str | g:(str|i64)=0; g="hello"; match str→len | 5
* reassign_union | g:(i64|bool)=0; g=5; match i64 | 5
*/
#include <stdio.h>
#include <stdlib.h>
@@ -61,7 +52,7 @@ static const struct row rows[] = {
" case void => return 99;\n"
" };\n"
"};\n",
0, 7 },
7, 7 },
{ "reassign_str",
"package main;\n"
@@ -73,7 +64,7 @@ static const struct row rows[] = {
" case i64 => return 88;\n"
" };\n"
"};\n",
88, 5 },
5, 5 },
{ "reassign_union",
"package main;\n"
@@ -85,7 +76,7 @@ static const struct row rows[] = {
" case bool => return 77;\n"
" };\n"
"};\n",
0, 5 },
5, 5 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit