wcc/check: #103/#108 inferred untyped-int defaults to int (8B), both stages

cstage type_default(TY_UNTYPED_INT) returned ty_i32 (4B): an unannotated
`let x = <v>` / `let a = [<v>,..]` silently TRUNCATED any value > 2^31
(5000000000 -> 705032704) and strode inferred arrays at 4. wwstage kept
the element raw untyped_int (size 0), which sized INCONSISTENTLY across
cgen — the array STORE strode the 8 sentinel but letslotsize under-
allocated the frame (SEGV) and cgindex strode the READ at 1. The two
stages were each wrong differently; #263 polarity: cstage was the
truncating side. int = machine word = 8B (Go-style, MEMORY
project_int_machine_word_derived_limits); Hare lowers a flexible iconst
to `int`, never a fixed i32 (ref/harec/src/types.c:835).

Fix, one root, both stages (FUSE — the cs default + the ww concrete
element must land together, else the inferred array is transient cs!=ww):
- cmd/wcc/type.c type_default(TY_UNTYPED_INT) ty_i32 -> ty_int. The
  root; stops scalar AND array truncation at source.
- cmd/wcc/check.c N_ARRLIT empty-elt fallback ty_i32 -> ty_int. Symmetric
  pair; count-0 array emits no stores, so byte-id-neutral.
- selfhost/cmd/wcc/check.ww exprtype N_ARRLIT: default the inferred
  element's untyped flavor to concrete (untyped_int->int, _float->f64,
  _str->str, _rune->rune, _bool->bool, mirror cstage type_default),
  empty-elt "i32"->"int", and stamp the synthesized N_TARRAY's .type_ so
  slotsize / elemsizeofc / letslotsize read its real [N]int size via the
  type table (rule-13) — no letslotsize special-case (SSoT).
combined.ww regen (check.ww embed): w6c + wwdump.

ken v2 corpus re-census (160 files): EXACTLY 5 rows move, ALL CONVERGE
(byte-id YES + run exit 0, none both-wrong, zero regression):
  m2_while   #108 scalar via alias-bool loop
  m8_range1  #104 for-range elem over alias [4]int
  m8_range2  #104 over 2-level alias
  m8_slice1  #103 inferred array + alias-slice init
  m8_slice2  #103 + 2-level-alias slice + re-slice
Bootstrap byte-id neutral (5 combined units w6c==w6c_ww; 0 bare inferred
arrays in selfhost). Annotated controls untouched ([4]i32 stride-4,
[4]int stride-8, byte-id). Pinned in test/wcc/813_arrlit_infer_elem_run
(the 2 direct repros incl the >2^31 truncation teeth + all 5 movers +
controls; test-unit 296).

Closes #103 (inferred-array SEGV + truncation), #108 (cstage scalar
untyped-int truncation), #104 (for-range elem alias i32-stamp), and the
m8_slice []int-init acceptance divergence.
This commit is contained in:
2026-06-06 09:23:24 +09:00
parent fc50a27f3e
commit c9cfa52624
7 changed files with 473 additions and 5 deletions

View File

@@ -13514,13 +13514,45 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
};
it = it.next;
};
if (elt == nil) { elt = mktname(c, "i32"); };
// #103/#108: default the inferred element's UNTYPED flavor to its
// concrete type, mirroring cstage cmd/wcc/check.c:1856
// `elt = type_default(t)` (type.c:237 untyped_int→int after the
// #108 polarity flip). Keeping the raw untyped_int (the prior
// documented divergence) sized the synthesized [N]untyped_int
// INCONSISTENTLY — untyped_int.size is 0, so the cgarrlitfillbp
// STORE strode the 8 sentinel while slotsize (letslotsize slot)
// and elemsizeofc (cgindex READ stride) read the 0-size element
// → frame under-alloc SEGV + stride-1 index reads, cs≠ww. drew
// Hare-fidelity: harec lower_flexible defaults a flexible iconst
// to `int` (ref/harec/src/types.c:835). int = machine word (8B);
// the old i32 truncated values >2^31 (#263 trap).
if (elt != nil && elt.kind == nkind.N_TNAME) {
if (streq(elt.str, "untyped_int")) {
elt = mktname(c, "int");
} else { if (streq(elt.str, "untyped_float")) {
elt = mktname(c, "f64");
} else { if (streq(elt.str, "untyped_str")) {
elt = mktname(c, "str");
} else { if (streq(elt.str, "untyped_rune")) {
elt = mktname(c, "rune");
} else { if (streq(elt.str, "untyped_bool")) {
elt = mktname(c, "bool");
}; }; }; }; };
};
// Empty inferred arrlit: default to int, symmetric with cstage
// check.c:1859 empty-fallback ty_int (#103). Was "i32".
if (elt == nil) { elt = mktname(c, "int"); };
let arr: *node = newnode(nkind.N_TARRAY, "", 0, 0);
arr.lhs = elt;
let cn: *node = newnode(nkind.N_INTLIT, "", 0, 0);
cn.uval = count;
arr.rhs = cn;
e.type_ = tinfofornode(c, arr): *void;
// #103: stamp the synthesized array NODE too. checkletassign
// plants this node on the inferred let's n.lhs; slotsize /
// elemsizeofc / letslotsize all read n.lhs.type_, which was nil
// here (only e.type_ was set) — falling to the 8 / 0 sentinels.
arr.type_ = e.type_;
return arr;
};
if (k == nkind.N_SLICE) {