wcc_ww/check: inferred struct-lit let plants the synthesized TNAME — field(SB) name-leak + tagged-field assign bound (#24)

For an annotation-less `let p = pt{...}` checkletassign planted exprtype's
N_STRUCTLIT result — the struct decl's BODY node (N_TSTRUCT, per #66) — as
the let's type. Every cgen local-arm dispatch (cgdot read, cgassign
tagged-field store, the alias peel) is N_TNAME-keyed, so the body matched
no arm: field reads fell to the module-qualified fallback and emitted the
FIELD NAME as a global symbol (MOVQ f(SB) — link-fail, #211 name-leak
family; silent corruption if a same-named global exists), and a tagged-
field assign fell to the assign-resolver TY_TAGGED loud bound. Both PG5
wwstage symptoms, one root; plain structs leaked too. Normalizing the
inferred binding to the synthesized TNAME (mktname + tinfofornode stamp)
routes every consumer down the already-byte-id annotated path. cstage
needs no twin: check.c:1477 clet carries Sym.type (tinfo) and its
emission is annotation-invariant (probed). Test 811: 10 rows x 2 drivers
+ 10 asm-byte-id; pre-fix wwstage link-fails every unannotated row
(incl. the `...` autofill and parenthesized forms; nested s.f.g ran
but cs!=ww asm).
This commit is contained in:
2026-06-05 02:20:15 +09:00
parent 413aafa599
commit f88dbb01e2
5 changed files with 435 additions and 0 deletions

View File

@@ -14627,6 +14627,24 @@ fn checkletassign(c: *checker, n: *node) void = {
// Defaulting (untyped_int → i32, etc.) is exprtype's job at use
// sites, not the binding site.
if (n.lhs == nil) {
// #24 (fold-5 prereq): a struct-lit init's exprtype returns the
// decl's BODY node (N_TSTRUCT, L3103 per #66) — but every cgen
// local-arm dispatch (cgdot read, cgassign tagged-field store,
// cgun addr-of) is N_TNAME-keyed, so planting the body dropped
// `p.f` to the module-qualified `MOVQ f(SB)` fallback (link-fail
// name-leak; #211 family). Normalize to the synthesized TNAME so
// the inferred binding is indistinguishable from the annotated
// one downstream. cstage needs no twin: check.c:1477 clet carries
// Sym.type (tinfo), and its emission is annotation-invariant
// (probed identical asm annotated vs not).
if (src != nil && src.kind == nkind.N_TSTRUCT
&& n.rhs.kind == nkind.N_STRUCTLIT
&& n.rhs.lhs != nil && n.rhs.lhs.kind == nkind.N_IDENT) {
let ltn: *node = mktname(c, n.rhs.lhs.str);
ltn.type_ = tinfofornode(c, ltn): *void;
n.lhs = ltn;
return;
};
if (src != nil) { n.lhs = src; };
return;
};

View File

@@ -4363,6 +4363,24 @@ fn checkletassign(c: *checker, n: *node) void = {
// Defaulting (untyped_int → i32, etc.) is exprtype's job at use
// sites, not the binding site.
if (n.lhs == nil) {
// #24 (fold-5 prereq): a struct-lit init's exprtype returns the
// decl's BODY node (N_TSTRUCT, L3103 per #66) — but every cgen
// local-arm dispatch (cgdot read, cgassign tagged-field store,
// cgun addr-of) is N_TNAME-keyed, so planting the body dropped
// `p.f` to the module-qualified `MOVQ f(SB)` fallback (link-fail
// name-leak; #211 family). Normalize to the synthesized TNAME so
// the inferred binding is indistinguishable from the annotated
// one downstream. cstage needs no twin: check.c:1477 clet carries
// Sym.type (tinfo), and its emission is annotation-invariant
// (probed identical asm annotated vs not).
if (src != nil && src.kind == nkind.N_TSTRUCT
&& n.rhs.kind == nkind.N_STRUCTLIT
&& n.rhs.lhs != nil && n.rhs.lhs.kind == nkind.N_IDENT) {
let ltn: *node = mktname(c, n.rhs.lhs.str);
ltn.type_ = tinfofornode(c, ltn): *void;
n.lhs = ltn;
return;
};
if (src != nil) { n.lhs = src; };
return;
};

View File

@@ -14627,6 +14627,24 @@ fn checkletassign(c: *checker, n: *node) void = {
// Defaulting (untyped_int → i32, etc.) is exprtype's job at use
// sites, not the binding site.
if (n.lhs == nil) {
// #24 (fold-5 prereq): a struct-lit init's exprtype returns the
// decl's BODY node (N_TSTRUCT, L3103 per #66) — but every cgen
// local-arm dispatch (cgdot read, cgassign tagged-field store,
// cgun addr-of) is N_TNAME-keyed, so planting the body dropped
// `p.f` to the module-qualified `MOVQ f(SB)` fallback (link-fail
// name-leak; #211 family). Normalize to the synthesized TNAME so
// the inferred binding is indistinguishable from the annotated
// one downstream. cstage needs no twin: check.c:1477 clet carries
// Sym.type (tinfo), and its emission is annotation-invariant
// (probed identical asm annotated vs not).
if (src != nil && src.kind == nkind.N_TSTRUCT
&& n.rhs.kind == nkind.N_STRUCTLIT
&& n.rhs.lhs != nil && n.rhs.lhs.kind == nkind.N_IDENT) {
let ltn: *node = mktname(c, n.rhs.lhs.str);
ltn.type_ = tinfofornode(c, ltn): *void;
n.lhs = ltn;
return;
};
if (src != nil) { n.lhs = src; };
return;
};