selfhost/cmd/wcc: stamp e.type_ for N_CALL + drop fold hook (A.6.1.2)

Six N_CALL stamp sites in exprtype:
- alloc slice form: tt = ([]u8 | nomem)
- alloc value form: tt = (*T | nomem)
- size/align/offset fold sites: untyped_int (mirrors cstage
  cmd/wcc/check.c:926/958 which sets n->type = ty_untyped_int
  after the fold; the returned mktname("i32") is the assignability
  target for callers, not the constant's own type)
- regular call: s.decl.lhs (mirrors cstage's build_fn_type ret)

Five error-path nil returns deliberately don't stamp.

Drop the size/align/offset trigger hook in resolvewalk: the A.6.0
end-of-fn general N_CALL dispatch already fires exprtype on every
N_CALL, making the targeted hook redundant. Pre-edit relied on
double-dispatch (hook → fold → re-dispatch → N_INTLIT stamp);
post-edit folds and stamps in one pass. Final n.type_ identical.

Hook removal + stamps bundled per CLAUDE.md rule 11: same-concern
(N_CALL handling) and the removal is what justifies the inline
fold-site stamps replacing the double-dispatch path.

Verified 132/132 incl. 995_self_rebuild byte-identity.
This commit is contained in:
2026-05-21 14:54:29 +09:00
parent c0fe38c101
commit 19095e4b50
3 changed files with 63 additions and 54 deletions

View File

@@ -7366,23 +7366,9 @@ fn resolvewalk(c: *checker, n: *node) void = {
};
};
// #42: trigger the size/align/offset fold here so the mutation
// fires regardless of context (if-conditions, expression statements,
// etc.) — wwstage's exprtype is otherwise called only from
// checkletassign / checkretassign / TRYPROP, and an unwrapped
// `if (size(str) != 16)` would otherwise leave the N_CALL alone
// and cgen would emit a stray `CALL size(SB)`. Mirrors cstage
// cstmt's recursive cexpr discipline.
if (k == nkind.N_CALL) {
if (n.lhs != nil) {
if (n.lhs.kind == nkind.N_IDENT) {
let nm: str = n.lhs.str;
if (streq(nm, "size") || streq(nm, "align") || streq(nm, "offset")) {
let _t: *node = exprtype(c, n, nil);
};
};
};
};
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
// every N_CALL — same context-free coverage, one dispatch site.
// After walking children: a local `let X: T = init;` registers
// `X` so subsequent statements can resolve it. Top-level lets
@@ -8291,6 +8277,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
sl.next = nome;
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = sl;
e.type_ = tinfofornode(c, tt): *void;
return tt;
};
};
@@ -8305,6 +8292,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
ptr.next = nome;
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = ptr;
e.type_ = tinfofornode(c, tt): *void;
return tt;
};
};
@@ -8331,14 +8319,24 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
};
if (!shadowed) {
if (e.list != nil) {
// Post-fold the node IS an N_INTLIT-shaped
// untyped_int constant. Mirrors cstage
// cmd/wcc/check.c:926/958 which stamps
// ty_untyped_int after the fold. The return
// tnode mktname("i32") is the assignability
// target for callers, not the constant's
// own type.
let utn: *node = mktname(c, "untyped_int");
if (issize) {
let v: i64 = astsize(c, e.list);
foldtointlit(c, e, v);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
if (isalign) {
let v: i64 = astalign(c, e.list);
foldtointlit(c, e, v);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
// offset(e.f): the arg is a value expression
@@ -8355,6 +8353,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
off = 0i64;
};
foldtointlit(c, e, off);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
};
@@ -8384,7 +8383,11 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
if (s == nil) { return nil; };
if (s.skind != skind.SK_FN) { return nil; };
if (s.decl == nil) { return nil; };
return s.decl.lhs; // fn-decl's lhs is the return type
// fn-decl's lhs is the return-type AST node. Mirrors cstage
// cmd/wcc/check.c:984+ regular-CALL `n->type = build_fn_type(c,
// s->decl)->ret` shape.
e.type_ = tinfofornode(c, s.decl.lhs): *void;
return s.decl.lhs;
};
if (k == nkind.N_TRYPROP) {
// success unwrap: the success-variant type of operand's

View File

@@ -434,23 +434,9 @@ fn resolvewalk(c: *checker, n: *node) void = {
};
};
// #42: trigger the size/align/offset fold here so the mutation
// fires regardless of context (if-conditions, expression statements,
// etc.) — wwstage's exprtype is otherwise called only from
// checkletassign / checkretassign / TRYPROP, and an unwrapped
// `if (size(str) != 16)` would otherwise leave the N_CALL alone
// and cgen would emit a stray `CALL size(SB)`. Mirrors cstage
// cstmt's recursive cexpr discipline.
if (k == nkind.N_CALL) {
if (n.lhs != nil) {
if (n.lhs.kind == nkind.N_IDENT) {
let nm: str = n.lhs.str;
if (streq(nm, "size") || streq(nm, "align") || streq(nm, "offset")) {
let _t: *node = exprtype(c, n, nil);
};
};
};
};
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
// every N_CALL — same context-free coverage, one dispatch site.
// After walking children: a local `let X: T = init;` registers
// `X` so subsequent statements can resolve it. Top-level lets
@@ -1359,6 +1345,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
sl.next = nome;
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = sl;
e.type_ = tinfofornode(c, tt): *void;
return tt;
};
};
@@ -1373,6 +1360,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
ptr.next = nome;
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = ptr;
e.type_ = tinfofornode(c, tt): *void;
return tt;
};
};
@@ -1399,14 +1387,24 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
};
if (!shadowed) {
if (e.list != nil) {
// Post-fold the node IS an N_INTLIT-shaped
// untyped_int constant. Mirrors cstage
// cmd/wcc/check.c:926/958 which stamps
// ty_untyped_int after the fold. The return
// tnode mktname("i32") is the assignability
// target for callers, not the constant's
// own type.
let utn: *node = mktname(c, "untyped_int");
if (issize) {
let v: i64 = astsize(c, e.list);
foldtointlit(c, e, v);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
if (isalign) {
let v: i64 = astalign(c, e.list);
foldtointlit(c, e, v);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
// offset(e.f): the arg is a value expression
@@ -1423,6 +1421,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
off = 0i64;
};
foldtointlit(c, e, off);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
};
@@ -1452,7 +1451,11 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
if (s == nil) { return nil; };
if (s.skind != skind.SK_FN) { return nil; };
if (s.decl == nil) { return nil; };
return s.decl.lhs; // fn-decl's lhs is the return type
// fn-decl's lhs is the return-type AST node. Mirrors cstage
// cmd/wcc/check.c:984+ regular-CALL `n->type = build_fn_type(c,
// s->decl)->ret` shape.
e.type_ = tinfofornode(c, s.decl.lhs): *void;
return s.decl.lhs;
};
if (k == nkind.N_TRYPROP) {
// success unwrap: the success-variant type of operand's

View File

@@ -7366,23 +7366,9 @@ fn resolvewalk(c: *checker, n: *node) void = {
};
};
// #42: trigger the size/align/offset fold here so the mutation
// fires regardless of context (if-conditions, expression statements,
// etc.) — wwstage's exprtype is otherwise called only from
// checkletassign / checkretassign / TRYPROP, and an unwrapped
// `if (size(str) != 16)` would otherwise leave the N_CALL alone
// and cgen would emit a stray `CALL size(SB)`. Mirrors cstage
// cstmt's recursive cexpr discipline.
if (k == nkind.N_CALL) {
if (n.lhs != nil) {
if (n.lhs.kind == nkind.N_IDENT) {
let nm: str = n.lhs.str;
if (streq(nm, "size") || streq(nm, "align") || streq(nm, "offset")) {
let _t: *node = exprtype(c, n, nil);
};
};
};
};
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
// every N_CALL — same context-free coverage, one dispatch site.
// After walking children: a local `let X: T = init;` registers
// `X` so subsequent statements can resolve it. Top-level lets
@@ -8291,6 +8277,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
sl.next = nome;
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = sl;
e.type_ = tinfofornode(c, tt): *void;
return tt;
};
};
@@ -8305,6 +8292,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
ptr.next = nome;
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = ptr;
e.type_ = tinfofornode(c, tt): *void;
return tt;
};
};
@@ -8331,14 +8319,24 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
};
if (!shadowed) {
if (e.list != nil) {
// Post-fold the node IS an N_INTLIT-shaped
// untyped_int constant. Mirrors cstage
// cmd/wcc/check.c:926/958 which stamps
// ty_untyped_int after the fold. The return
// tnode mktname("i32") is the assignability
// target for callers, not the constant's
// own type.
let utn: *node = mktname(c, "untyped_int");
if (issize) {
let v: i64 = astsize(c, e.list);
foldtointlit(c, e, v);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
if (isalign) {
let v: i64 = astalign(c, e.list);
foldtointlit(c, e, v);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
// offset(e.f): the arg is a value expression
@@ -8355,6 +8353,7 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
off = 0i64;
};
foldtointlit(c, e, off);
e.type_ = tinfofornode(c, utn): *void;
return mktname(c, "i32");
};
};
@@ -8384,7 +8383,11 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
if (s == nil) { return nil; };
if (s.skind != skind.SK_FN) { return nil; };
if (s.decl == nil) { return nil; };
return s.decl.lhs; // fn-decl's lhs is the return type
// fn-decl's lhs is the return-type AST node. Mirrors cstage
// cmd/wcc/check.c:984+ regular-CALL `n->type = build_fn_type(c,
// s->decl)->ret` shape.
e.type_ = tinfofornode(c, s.decl.lhs): *void;
return s.decl.lhs;
};
if (k == nkind.N_TRYPROP) {
// success unwrap: the success-variant type of operand's