selfhost: parser tsuffix plumb + cgen N_UN peel for tagged-store variant index (closes #32)
Typed-int literal assigned into a tagged-union slot (`h.e = 42i64;` where
e: (i32 | i64)) wrote tag = 0 (the i32 slot) instead of tag = 1 (the i64
slot). Cstage was correct: parse.c parseprimary copies tok.tsuffix onto
N_INTLIT, check.c stamps node.type = ty_i64, and cg_widen_tagged_store →
cg_tag_for_variant walks variants matching by structural type_eq —
ty_i64 lands at index 1. Wwstage had two gaps:
1. The parser (lib/ww/parse/expr.ww parseprimary) read p.curuval and
p.curtext from the current token but never the tsuffix field. Token-
side capture has been in place since the lexer's `i8/i16/.../u64/f32/
f64` glue suffix landed (lib/ww/lex/lex.ww sets out.tsuffix); the
parser side was missed. So an N_INTLIT for `42i64` carried tsuffix=""
into cgen. Mirror of cmd/wcc/parse.c parseprimary's `n->tsuffix =
t.tsuffix` line. Same plumb for N_FLOATLIT.
2. Wwstage has no checker stage to stamp N_UN's type from its inner
expression's type. `-42i64` parses as N_UN(MINUS, N_INTLIT(42,
tsuffix="i64")) and rhstargetname stopped at N_UN, returning "" and
falling through to taggedvariantindex's "first non-str variant"
fallback — which picked tag 0 (i32) for any numeric rhs in an
(i32|i64) union. Cstage's cunop returns the inner type for
TK_MINUS / TK_PLUS / TK_TILDE so the N_UN gets ty_i64 stamped
naturally; wwstage gets the equivalent via an explicit peel in
rhstargetname, recursing into rhs.lhs for these three ops. The
recursion also covers nested unary (`- -42i64`), which parseunary
builds as N_UN over N_UN over N_INTLIT.
The lib/ww/parse change is mirrored in selfhost/cmd/{w6c,wwdump}/
main.combined.ww so the bootstrap snapshot stays consistent with the
working frontend source. parser.curtsuffix is a new str field; refill
copies t.tsuffix into it; parseprimary TK_INT / TK_FLOAT copy it onto
the new node before advance.
Cstage handled both `42i64` and `-42i64` correctly already; no cstage
mirror needed.
Test 694_tagged_store_intlit — eleven rows running on both stages: i64
lit in (i32|i64); i32 lit (existing-working pin); i64 lit in
(i32|i64|str) with the str fallback at tail; u8 lit at head of
(u8|i32|i64); i64 lit at tail of (u8|i32|i64) with a +100 marker so
mis-binding into u8 can't masquerade as success; negative-i64 lit
(N_UN MINUS peel + sign extension through match-arm bind);
unary-plus i64 lit (N_UN PLUS peel); bitwise-not i64 lit (N_UN TILDE
peel; `~0i64 == -1i64`); nested unary `- -42i64` (recursion through
two N_UN levels); direct `let x: ev = 42i64;` (cglet's tagged-init
code path, separate write site from cgassign's field-write);
negative-control str field (pins the existing str-fallback path
through rhstargetname).
Pre-fix run on wwstage: 8/11 rows fail (every typed-i64 case including
all three unary operators, nested unary, and the direct let-init);
cstage 11/11 pass. Post-fix: 22/22 across both stages. make test
41/41. Bootstrap ww2 == ww3 == ww4 byte-identical.
This commit is contained in:
@@ -25,6 +25,13 @@ fn parseprimary(p: *parser) *node = {
|
||||
let n: *node = newnode(p.a, nkind.N_INTLIT, pf, pl, pc);
|
||||
n.uval = p.curuval;
|
||||
n.str = p.curtext;
|
||||
// Plumb the typed-int suffix (`42i64`, `3u8`) through to
|
||||
// the node. Cgen's rhstargetname reads tsuffix to pick the
|
||||
// matching tagged-union variant; without this, typed-int
|
||||
// rhs of `h.e = 42i64;` falls through to the "first non-str
|
||||
// variant" fallback and writes tag 0. Mirror of cmd/wcc/
|
||||
// parse.c parseprimary TK_INT.
|
||||
n.tsuffix = p.curtsuffix;
|
||||
advance(p);
|
||||
return n;
|
||||
};
|
||||
@@ -36,6 +43,7 @@ fn parseprimary(p: *parser) *node = {
|
||||
// don't need a float ABI to materialise the constant.
|
||||
n.uval = p.curuval;
|
||||
n.str = p.curtext;
|
||||
n.tsuffix = p.curtsuffix;
|
||||
advance(p);
|
||||
return n;
|
||||
};
|
||||
|
||||
@@ -31,6 +31,13 @@ type parser = struct {
|
||||
curtext: str,
|
||||
curuval: u64,
|
||||
curfval: f64,
|
||||
// curtsuffix: typed numeric literal suffix ("i32", "u64", ...) on
|
||||
// the current TK_INT / TK_FLOAT token, or empty. Parseprimary
|
||||
// copies this onto the N_INTLIT / N_FLOATLIT node so cgen's
|
||||
// rhstargetname can map `42i64` to the i64 variant of a tagged
|
||||
// union without falling back to "first non-str variant" (which
|
||||
// silently picked tag 0 for typed-int literals; see #10).
|
||||
curtsuffix: str,
|
||||
};
|
||||
|
||||
fn refill(p: *parser) void = {
|
||||
@@ -43,6 +50,7 @@ fn refill(p: *parser) void = {
|
||||
p.curtext = t.text;
|
||||
p.curuval = t.uval;
|
||||
p.curfval = t.fval;
|
||||
p.curtsuffix = t.tsuffix;
|
||||
};
|
||||
|
||||
export fn parserinit(p: *parser, a: *arena, l: *lex) void = {
|
||||
|
||||
Reference in New Issue
Block a user