wcc/check: #23 reject untyped-int into nested-union variant (wwstage align to cstage)

isassignable's untyped-int arm fell through *confident=false/return true,
silently accepting an untyped int into a union whose variant is itself a
nested (non-flattened) union; ww emitted tag=0 (wrong arm) where cstage
louds. Reject unless a direct variant is numeric-or-enum (N_TENUM accept
mirrors cstage type_isnum). Faithful flatten+rebox deferred (nominal
identity, post-CSP). test/wcc/835 (new) + Makefile.
This commit is contained in:
2026-06-09 17:31:34 +09:00
parent d1ac836fb9
commit 64606de8af
5 changed files with 324 additions and 6 deletions

View File

@@ -3803,11 +3803,27 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (isnumerictname(vu)) { return true; };
// mirror cstage type_isnum(enum)=true (type.c:201 ->
// type_isint -> :178 TY_ENUM); an enum variant DOES
// accept an untyped int. Without this the #23 fix would
// flip an enum-variant union to reject while cstage
// accepts = a NEW divergence (A3 trap).
if (vu.kind == nkind.N_TENUM) { return true; };
};
v = v.next;
};
*confident = false;
return true;
// #23: no DIRECT variant accepts an untyped int -> confident
// reject (mirror cstage type.c:343 `return 0`). ww does NOT
// flatten a nested union variant (#199-alpha non-drill); an int
// reachable only via a nested union (e.g. (inner|str),
// inner=(int|bool)) would otherwise silently build tag=0/payload
// with no inner-tag wrapper = malformed box. *confident is
// already true (:3777, untouched on this path) so the caller
// sees ok=false,conf=true -> loud errnotassign. DEFERRED
// divergence (task #23 / #199b): both stages then over-reject
// valid Hare (expand_tagged flattens); faithful flatten+rebox
// is post-CSP nominal-identity work.
return false;
};
// Known non-numeric primitive: confidently wrong.
if (du.kind == nkind.N_TNAME) {