wcc: stamp computed enum-member value-exprs
A computed enum member — B = A + 4, RW = R | W, sibling/chained backref — left its value-expr node nil-typed: enumvalfold folds the constant but never stamps the expr, and since enum members are not installed as scope idents the sibling backref resolves to nothing, so BOTH the N_BIN/N_UN wrapper and the backref N_IDENT go nil (literal members are fine). Stamp the value-expr subtree (only-nil) to the enum's underlying storage type via a new stampenumvals/stampnilexpr pass on the N_TENUM branch. Mirrors harec checking each member value-expr at the underlying type (ref/harec/src/check.c:4419). A prerequisite for arming the wwstage asserttyped bail. Checker-only — the value folds to a constant at every use site and in cgen, so the node's type_ is never read by codegen; 990-997 byte-id hold. Extends the 901 gap-corpus with 901_enum_corpus.ww.
This commit is contained in:
@@ -10522,6 +10522,8 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (k == nkind.N_TENUM) { stampenumvals(c, n); };
|
||||||
|
|
||||||
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
|
// #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
|
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
|
||||||
// every N_CALL — same context-free coverage, one dispatch site.
|
// every N_CALL — same context-free coverage, one dispatch site.
|
||||||
@@ -11334,6 +11336,43 @@ fn enumvalfold(body: *node, until: *node, e: *node, out: *u64) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stampenumvals — give every node in each enum-member value-expr a
|
||||||
|
// non-nil type_. resolvewalk's post-order exprtype (L543) stamps the
|
||||||
|
// literal leaves, but a sibling backref (`B = A + 4`) resolves to
|
||||||
|
// nothing — enum members aren't installed as scope idents — so the
|
||||||
|
// backref N_IDENT and the N_BIN/N_UN wrapping it stay nil. asserttyped
|
||||||
|
// walks the enum DEFINITION (whether or not a member is `.`-accessed)
|
||||||
|
// and its value-node invariant then fires on those. harec checks each
|
||||||
|
// member's value-expr at the enum's underlying type
|
||||||
|
// (ref/harec/src/check.c:4419 — check_expression with type->alias.type),
|
||||||
|
// so the whole constant subtree carries the underlying integer type;
|
||||||
|
// mirror that. The value itself is folded to a constant at every use
|
||||||
|
// site (enumvalfold) and at codegen (cgen.ww enumevalmember), so cgen
|
||||||
|
// never reads these node types — this stamp is checker metadata only.
|
||||||
|
fn stampenumvals(c: *checker, n: *node) void = {
|
||||||
|
let under: *tinfo = c.tc.tyi32;
|
||||||
|
if (n.lhs != nil) {
|
||||||
|
let s: *tinfo = tinfofornode(c, n.lhs);
|
||||||
|
if (s != nil) { under = s; };
|
||||||
|
};
|
||||||
|
let m: *node = n.list;
|
||||||
|
for (m != nil) {
|
||||||
|
stampnilexpr(m.lhs, under);
|
||||||
|
m = m.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// stampnilexpr — stamp nil-typed nodes in a constant expr subtree to
|
||||||
|
// `ti`. lhs/rhs cover the enum constexpr grammar enumvalfold accepts
|
||||||
|
// (literals, unary, binary, sibling backref); non-nil nodes keep the
|
||||||
|
// type exprtype already derived.
|
||||||
|
fn stampnilexpr(n: *node, ti: *tinfo) void = {
|
||||||
|
if (n == nil) { return; };
|
||||||
|
if (n.type_ == nil) { n.type_ = ti: *void; };
|
||||||
|
stampnilexpr(n.lhs, ti);
|
||||||
|
stampnilexpr(n.rhs, ti);
|
||||||
|
};
|
||||||
|
|
||||||
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
||||||
// tuple. Mirrors cgenutil.ww slotsize TTUPLE — cstage's tuple ABI
|
// tuple. Mirrors cgenutil.ww slotsize TTUPLE — cstage's tuple ABI
|
||||||
// spills each element into its own register / 8B eightbyte, so narrow
|
// spills each element into its own register / 8B eightbyte, so narrow
|
||||||
|
|||||||
@@ -492,6 +492,8 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (k == nkind.N_TENUM) { stampenumvals(c, n); };
|
||||||
|
|
||||||
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
|
// #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
|
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
|
||||||
// every N_CALL — same context-free coverage, one dispatch site.
|
// every N_CALL — same context-free coverage, one dispatch site.
|
||||||
@@ -1304,6 +1306,43 @@ fn enumvalfold(body: *node, until: *node, e: *node, out: *u64) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stampenumvals — give every node in each enum-member value-expr a
|
||||||
|
// non-nil type_. resolvewalk's post-order exprtype (L543) stamps the
|
||||||
|
// literal leaves, but a sibling backref (`B = A + 4`) resolves to
|
||||||
|
// nothing — enum members aren't installed as scope idents — so the
|
||||||
|
// backref N_IDENT and the N_BIN/N_UN wrapping it stay nil. asserttyped
|
||||||
|
// walks the enum DEFINITION (whether or not a member is `.`-accessed)
|
||||||
|
// and its value-node invariant then fires on those. harec checks each
|
||||||
|
// member's value-expr at the enum's underlying type
|
||||||
|
// (ref/harec/src/check.c:4419 — check_expression with type->alias.type),
|
||||||
|
// so the whole constant subtree carries the underlying integer type;
|
||||||
|
// mirror that. The value itself is folded to a constant at every use
|
||||||
|
// site (enumvalfold) and at codegen (cgen.ww enumevalmember), so cgen
|
||||||
|
// never reads these node types — this stamp is checker metadata only.
|
||||||
|
fn stampenumvals(c: *checker, n: *node) void = {
|
||||||
|
let under: *tinfo = c.tc.tyi32;
|
||||||
|
if (n.lhs != nil) {
|
||||||
|
let s: *tinfo = tinfofornode(c, n.lhs);
|
||||||
|
if (s != nil) { under = s; };
|
||||||
|
};
|
||||||
|
let m: *node = n.list;
|
||||||
|
for (m != nil) {
|
||||||
|
stampnilexpr(m.lhs, under);
|
||||||
|
m = m.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// stampnilexpr — stamp nil-typed nodes in a constant expr subtree to
|
||||||
|
// `ti`. lhs/rhs cover the enum constexpr grammar enumvalfold accepts
|
||||||
|
// (literals, unary, binary, sibling backref); non-nil nodes keep the
|
||||||
|
// type exprtype already derived.
|
||||||
|
fn stampnilexpr(n: *node, ti: *tinfo) void = {
|
||||||
|
if (n == nil) { return; };
|
||||||
|
if (n.type_ == nil) { n.type_ = ti: *void; };
|
||||||
|
stampnilexpr(n.lhs, ti);
|
||||||
|
stampnilexpr(n.rhs, ti);
|
||||||
|
};
|
||||||
|
|
||||||
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
||||||
// tuple. Mirrors cgenutil.ww slotsize TTUPLE — cstage's tuple ABI
|
// tuple. Mirrors cgenutil.ww slotsize TTUPLE — cstage's tuple ABI
|
||||||
// spills each element into its own register / 8B eightbyte, so narrow
|
// spills each element into its own register / 8B eightbyte, so narrow
|
||||||
|
|||||||
@@ -10522,6 +10522,8 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (k == nkind.N_TENUM) { stampenumvals(c, n); };
|
||||||
|
|
||||||
// #42's size/align/offset fold trigger lived here pre-A.6.0; the
|
// #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
|
// A.6.0 end-of-fn general dispatch (below) now fires exprtype on
|
||||||
// every N_CALL — same context-free coverage, one dispatch site.
|
// every N_CALL — same context-free coverage, one dispatch site.
|
||||||
@@ -11334,6 +11336,43 @@ fn enumvalfold(body: *node, until: *node, e: *node, out: *u64) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stampenumvals — give every node in each enum-member value-expr a
|
||||||
|
// non-nil type_. resolvewalk's post-order exprtype (L543) stamps the
|
||||||
|
// literal leaves, but a sibling backref (`B = A + 4`) resolves to
|
||||||
|
// nothing — enum members aren't installed as scope idents — so the
|
||||||
|
// backref N_IDENT and the N_BIN/N_UN wrapping it stay nil. asserttyped
|
||||||
|
// walks the enum DEFINITION (whether or not a member is `.`-accessed)
|
||||||
|
// and its value-node invariant then fires on those. harec checks each
|
||||||
|
// member's value-expr at the enum's underlying type
|
||||||
|
// (ref/harec/src/check.c:4419 — check_expression with type->alias.type),
|
||||||
|
// so the whole constant subtree carries the underlying integer type;
|
||||||
|
// mirror that. The value itself is folded to a constant at every use
|
||||||
|
// site (enumvalfold) and at codegen (cgen.ww enumevalmember), so cgen
|
||||||
|
// never reads these node types — this stamp is checker metadata only.
|
||||||
|
fn stampenumvals(c: *checker, n: *node) void = {
|
||||||
|
let under: *tinfo = c.tc.tyi32;
|
||||||
|
if (n.lhs != nil) {
|
||||||
|
let s: *tinfo = tinfofornode(c, n.lhs);
|
||||||
|
if (s != nil) { under = s; };
|
||||||
|
};
|
||||||
|
let m: *node = n.list;
|
||||||
|
for (m != nil) {
|
||||||
|
stampnilexpr(m.lhs, under);
|
||||||
|
m = m.next;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// stampnilexpr — stamp nil-typed nodes in a constant expr subtree to
|
||||||
|
// `ti`. lhs/rhs cover the enum constexpr grammar enumvalfold accepts
|
||||||
|
// (literals, unary, binary, sibling backref); non-nil nodes keep the
|
||||||
|
// type exprtype already derived.
|
||||||
|
fn stampnilexpr(n: *node, ti: *tinfo) void = {
|
||||||
|
if (n == nil) { return; };
|
||||||
|
if (n.type_ == nil) { n.type_ = ti: *void; };
|
||||||
|
stampnilexpr(n.lhs, ti);
|
||||||
|
stampnilexpr(n.rhs, ti);
|
||||||
|
};
|
||||||
|
|
||||||
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
// #61 A.5 helper: per-element slot size when `pt` appears inside a
|
||||||
// tuple. Mirrors cgenutil.ww slotsize TTUPLE — cstage's tuple ABI
|
// tuple. Mirrors cgenutil.ww slotsize TTUPLE — cstage's tuple ABI
|
||||||
// spills each element into its own register / 8B eightbyte, so narrow
|
// spills each element into its own register / 8B eightbyte, so narrow
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
* C abort intrinsic callee utf8 8
|
* C abort intrinsic callee utf8 8
|
||||||
* D module-leaf == type/fn name fnmatch 0 (closed)
|
* D module-leaf == type/fn name fnmatch 0 (closed)
|
||||||
* D module-leaf == type/fn name random 0 (closed)
|
* D module-leaf == type/fn name random 0 (closed)
|
||||||
|
* E computed enum-member value-expr enum_corpus 0 (closed)
|
||||||
*
|
*
|
||||||
* Exit code of wwdump_ww is intentionally not gated: the diagnostics
|
* Exit code of wwdump_ww is intentionally not gated: the diagnostics
|
||||||
* land on stderr regardless of the run's success, and arming the bail
|
* land on stderr regardless of the run's success, and arming the bail
|
||||||
@@ -116,6 +117,8 @@ main(void)
|
|||||||
"D module-leaf == type/fn name", 0 },
|
"D module-leaf == type/fn name", 0 },
|
||||||
{ "lib/math/random/random_test.combined.ww",
|
{ "lib/math/random/random_test.combined.ww",
|
||||||
"D module-leaf == type/fn name", 0 },
|
"D module-leaf == type/fn name", 0 },
|
||||||
|
{ "test/wcc/901_enum_corpus.ww",
|
||||||
|
"E computed enum-member value-expr", 0 },
|
||||||
{ NULL, NULL, 0 },
|
{ NULL, NULL, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,6 +154,6 @@ main(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
||||||
"on %d gap-corpus fixtures (C pinned, A+B+D closed)\n", n);
|
"on %d gap-corpus fixtures (C pinned, A+B+D+E closed)\n", n);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
37
test/wcc/901_enum_corpus.ww
Normal file
37
test/wcc/901_enum_corpus.ww
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// 901_enum_corpus — computed enum-member value-expr gap fixture for
|
||||||
|
// the 901 asserttyped net (class E). Each computed member leaves its
|
||||||
|
// value-expr (N_BIN / N_UN / sibling-backref N_IDENT) type_=nil until
|
||||||
|
// the checker's stampenumvals fills the subtree at the enum's
|
||||||
|
// underlying type. Mirrors the 759_check_enum_fold rows plus the 994
|
||||||
|
// in-source `RW = R | W` shape (enum u8). Literal + auto-increment
|
||||||
|
// members never warned (resolvewalk's post-order exprtype stamps the
|
||||||
|
// leaf), so the literal-only enum is a control: it must stay 0.
|
||||||
|
package main;
|
||||||
|
|
||||||
|
type sib = enum i32 { A = 7, B = A };
|
||||||
|
type un = enum i32 { NEG = -7, R = NEG + 14 };
|
||||||
|
type unl = enum i32 { TIL = ~(-8), PLU = +7 };
|
||||||
|
type arith = enum i32 {
|
||||||
|
A = 3,
|
||||||
|
ADD = A + 4,
|
||||||
|
SUB = A - 1,
|
||||||
|
MUL = A * 2,
|
||||||
|
DIV = A / 3,
|
||||||
|
MOD = A % 2,
|
||||||
|
};
|
||||||
|
type bits = enum i32 {
|
||||||
|
A = 5,
|
||||||
|
AND = A & 3,
|
||||||
|
IOR = A | 2,
|
||||||
|
XOR = A ^ 1,
|
||||||
|
SHL = A << 2,
|
||||||
|
SHR = A >> 1,
|
||||||
|
};
|
||||||
|
type chain = enum i32 { A = 1, B = A + 1, C = B + 1, D = C + 4 };
|
||||||
|
type mode = enum u8 { RD = 1u8, WR = 2u8, RW = RD | WR };
|
||||||
|
|
||||||
|
type lit = enum i32 { X = 7, Y = 9, Z = 11 };
|
||||||
|
|
||||||
|
fn main() i32 = {
|
||||||
|
return chain.D: i32 + mode.RW: i32;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user