wcc: nodeisunsigned N_INDEX reads stamped type_ (#134)

wwstage cgenutil nodeisunsigned N_INDEX arm now reads
typeisunsigned(n.type_) directly, mirroring the N_DOT arm at line
1007 and cstage cgen.c:2541 which reads type_isunsigned on the
stamped operand. Embodies the #121 principle (collapse structural
onto stamp). Byte-id-neutral at master (no current N_INDEX-of-non-
IDENT-base unsigned compare sites in bootstrap); fix is for forward
consumers in strconv decimal.ha (>= 5u8 on d.digits[nd] with N_DOT
base) and similar Hare idioms. Closes #134.
This commit is contained in:
2026-05-26 21:28:14 +09:00
parent 4acab6e0bb
commit 36bf60350c
3 changed files with 33 additions and 72 deletions

View File

@@ -12028,31 +12028,18 @@ fn nodeisunsigned(c: *cgen, n: *node) bool = {
return nodeisunsigned(c, n.rhs); return nodeisunsigned(c, n.rhs);
}; };
if (k == nkind.N_UN) { return nodeisunsigned(c, n.lhs); }; if (k == nkind.N_UN) { return nodeisunsigned(c, n.lhs); };
// nkind.N_INDEX: `p[i]` is unsigned iff p's element type is unsigned. // nkind.N_INDEX: `p[i]` is unsigned iff its element type is
// Walks the base local's declared type and pulls the element // unsigned. Read the checker-stamped result type directly,
// out — *u8 → u8, [N]u32 → u32, []u64 → u64. Without this the // mirroring the N_DOT arm above and cstage cgen.c:2541
// compare-codegen for `p[i] >= 48u8` falls back to signed JGE // (`type_isunsigned(n->lhs->type)` on operand's stamped tinfo).
// instead of JAE, diverging from C w6c on byte indexing. // Replaces the prior structural base-walk that only fired for
// N_IDENT base — fell through to `return false` for N_DOT base
// (e.g. `d.digits[nd]` where d is a *struct), making
// `d.digits[nd] >= 5u8` pick signed JGE instead of unsigned JAE.
// Embodies the #121 principle (collapse structural onto stamp).
// #134.
if (k == nkind.N_INDEX) { if (k == nkind.N_INDEX) {
let base: *node = n.lhs; return typeisunsigned(n.type_: *tinfo);
if (base != nil) {
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
if (tn != nil) {
let elem: *node = nil;
if (tn.kind == nkind.N_TPTR) { elem = tn.lhs; };
if (tn.kind == nkind.N_TARRAY) { elem = tn.lhs; };
if (tn.kind == nkind.N_TSLICE) { elem = tn.lhs; };
if (elem != nil) {
return typeisunsigned(elem.type_: *tinfo);
};
};
};
};
};
return false;
}; };
return false; return false;
}; };

View File

@@ -1015,31 +1015,18 @@ fn nodeisunsigned(c: *cgen, n: *node) bool = {
return nodeisunsigned(c, n.rhs); return nodeisunsigned(c, n.rhs);
}; };
if (k == nkind.N_UN) { return nodeisunsigned(c, n.lhs); }; if (k == nkind.N_UN) { return nodeisunsigned(c, n.lhs); };
// nkind.N_INDEX: `p[i]` is unsigned iff p's element type is unsigned. // nkind.N_INDEX: `p[i]` is unsigned iff its element type is
// Walks the base local's declared type and pulls the element // unsigned. Read the checker-stamped result type directly,
// out — *u8 → u8, [N]u32 → u32, []u64 → u64. Without this the // mirroring the N_DOT arm above and cstage cgen.c:2541
// compare-codegen for `p[i] >= 48u8` falls back to signed JGE // (`type_isunsigned(n->lhs->type)` on operand's stamped tinfo).
// instead of JAE, diverging from C w6c on byte indexing. // Replaces the prior structural base-walk that only fired for
// N_IDENT base — fell through to `return false` for N_DOT base
// (e.g. `d.digits[nd]` where d is a *struct), making
// `d.digits[nd] >= 5u8` pick signed JGE instead of unsigned JAE.
// Embodies the #121 principle (collapse structural onto stamp).
// #134.
if (k == nkind.N_INDEX) { if (k == nkind.N_INDEX) {
let base: *node = n.lhs; return typeisunsigned(n.type_: *tinfo);
if (base != nil) {
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
if (tn != nil) {
let elem: *node = nil;
if (tn.kind == nkind.N_TPTR) { elem = tn.lhs; };
if (tn.kind == nkind.N_TARRAY) { elem = tn.lhs; };
if (tn.kind == nkind.N_TSLICE) { elem = tn.lhs; };
if (elem != nil) {
return typeisunsigned(elem.type_: *tinfo);
};
};
};
};
};
return false;
}; };
return false; return false;
}; };

View File

@@ -12028,31 +12028,18 @@ fn nodeisunsigned(c: *cgen, n: *node) bool = {
return nodeisunsigned(c, n.rhs); return nodeisunsigned(c, n.rhs);
}; };
if (k == nkind.N_UN) { return nodeisunsigned(c, n.lhs); }; if (k == nkind.N_UN) { return nodeisunsigned(c, n.lhs); };
// nkind.N_INDEX: `p[i]` is unsigned iff p's element type is unsigned. // nkind.N_INDEX: `p[i]` is unsigned iff its element type is
// Walks the base local's declared type and pulls the element // unsigned. Read the checker-stamped result type directly,
// out — *u8 → u8, [N]u32 → u32, []u64 → u64. Without this the // mirroring the N_DOT arm above and cstage cgen.c:2541
// compare-codegen for `p[i] >= 48u8` falls back to signed JGE // (`type_isunsigned(n->lhs->type)` on operand's stamped tinfo).
// instead of JAE, diverging from C w6c on byte indexing. // Replaces the prior structural base-walk that only fired for
// N_IDENT base — fell through to `return false` for N_DOT base
// (e.g. `d.digits[nd]` where d is a *struct), making
// `d.digits[nd] >= 5u8` pick signed JGE instead of unsigned JAE.
// Embodies the #121 principle (collapse structural onto stamp).
// #134.
if (k == nkind.N_INDEX) { if (k == nkind.N_INDEX) {
let base: *node = n.lhs; return typeisunsigned(n.type_: *tinfo);
if (base != nil) {
if (base.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, base.str);
if (lc != nil) {
let tn: *node = lc.tnode;
if (tn != nil) {
let elem: *node = nil;
if (tn.kind == nkind.N_TPTR) { elem = tn.lhs; };
if (tn.kind == nkind.N_TARRAY) { elem = tn.lhs; };
if (tn.kind == nkind.N_TSLICE) { elem = tn.lhs; };
if (elem != nil) {
return typeisunsigned(elem.type_: *tinfo);
};
};
};
};
};
return false;
}; };
return false; return false;
}; };