wcc/check: reject untyped-float + nil into a tagged dst with no matching variant (wwstage align to cstage)

isassignable's untyped-float (A4) and nil (A5) arms over-accepted a source
into a tagged dst where no variant accepts it -> silent over-accept then
tag-0 miscompile; cstage louds. Reject (cerr + c.errs+=1). Part of the
isassignable tagged-dst over-accept class (#23 fixed arm3; A6/A7 deferred
to the B-full nominal arc). test/wcc/836.
This commit is contained in:
2026-06-09 19:07:40 +09:00
parent 7e19d282f4
commit 08ccb734b2
5 changed files with 385 additions and 6 deletions

View File

@@ -14123,6 +14123,30 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
if (streq(du.str, "void")) { return false; };
if (streq(du.str, "str")) { return false; };
};
// A4 (#23 float-twin): (T | ...) tagged dst — accept iff a DIRECT
// variant is a float type. cstage type_assignable for an
// untyped_float src uses type_isfloat (type.c:376 — f32/f64 ONLY,
// NOT type_isnum), so an int/enum variant does NOT accept an
// untyped float. No direct float variant -> confident reject
// (cstage type.c:316 loop returns 0); ww does NOT flatten a
// nested-union float variant (#199-alpha). *confident is already
// true (:3777). Without it the catch-all (:3972) over-accepts an
// untyped float into ANY tagged (silent tag=0). Faithful
// flatten+rebox deferred post-CSP (nominal id, #23/#40).
if (du.kind == nkind.N_TTAGGED) {
let v: *node = du.list;
for (v != nil) {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (vu.kind == nkind.N_TNAME) {
if (streq(vu.str, "f32")) { return true; };
if (streq(vu.str, "f64")) { return true; };
};
};
v = v.next;
};
return false;
};
*confident = false;
return true;
};
@@ -14137,10 +14161,20 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
if (du.kind == nkind.N_TTAGGED) {
let v: *node = du.list;
for (v != nil) {
if (v.kind == nkind.N_TPTR) { return true; };
if (v.kind == nkind.N_TSLICE){ return true; };
if (v.kind == nkind.N_TPTR) { return true; };
if (v.kind == nkind.N_TSLICE) { return true; };
if (v.kind == nkind.N_TCHAN) { return true; };
if (v.kind == nkind.N_TFN) { return true; };
v = v.next;
};
// A5: no nullable (ptr/slice/chan/fn) variant -> confident
// reject (cstage type.c:316 loop returns 0; nil accepts only
// into ptr/slice/chan/fn per type.c:382-385). *confident is
// already true (:3777). Without it the catch-all (:3972)
// over-accepts nil into ANY tagged (silent). The trailing
// fallthrough below stays for a NON-tagged du (nil into a bare
// scalar — a separate non-family over-accept, SIBLINGS).
return false;
};
*confident = false;
return true;

View File

@@ -3842,6 +3842,30 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
if (streq(du.str, "void")) { return false; };
if (streq(du.str, "str")) { return false; };
};
// A4 (#23 float-twin): (T | ...) tagged dst — accept iff a DIRECT
// variant is a float type. cstage type_assignable for an
// untyped_float src uses type_isfloat (type.c:376 — f32/f64 ONLY,
// NOT type_isnum), so an int/enum variant does NOT accept an
// untyped float. No direct float variant -> confident reject
// (cstage type.c:316 loop returns 0); ww does NOT flatten a
// nested-union float variant (#199-alpha). *confident is already
// true (:3777). Without it the catch-all (:3972) over-accepts an
// untyped float into ANY tagged (silent tag=0). Faithful
// flatten+rebox deferred post-CSP (nominal id, #23/#40).
if (du.kind == nkind.N_TTAGGED) {
let v: *node = du.list;
for (v != nil) {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (vu.kind == nkind.N_TNAME) {
if (streq(vu.str, "f32")) { return true; };
if (streq(vu.str, "f64")) { return true; };
};
};
v = v.next;
};
return false;
};
*confident = false;
return true;
};
@@ -3856,10 +3880,20 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
if (du.kind == nkind.N_TTAGGED) {
let v: *node = du.list;
for (v != nil) {
if (v.kind == nkind.N_TPTR) { return true; };
if (v.kind == nkind.N_TSLICE){ return true; };
if (v.kind == nkind.N_TPTR) { return true; };
if (v.kind == nkind.N_TSLICE) { return true; };
if (v.kind == nkind.N_TCHAN) { return true; };
if (v.kind == nkind.N_TFN) { return true; };
v = v.next;
};
// A5: no nullable (ptr/slice/chan/fn) variant -> confident
// reject (cstage type.c:316 loop returns 0; nil accepts only
// into ptr/slice/chan/fn per type.c:382-385). *confident is
// already true (:3777). Without it the catch-all (:3972)
// over-accepts nil into ANY tagged (silent). The trailing
// fallthrough below stays for a NON-tagged du (nil into a bare
// scalar — a separate non-family over-accept, SIBLINGS).
return false;
};
*confident = false;
return true;

View File

@@ -14123,6 +14123,30 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
if (streq(du.str, "void")) { return false; };
if (streq(du.str, "str")) { return false; };
};
// A4 (#23 float-twin): (T | ...) tagged dst — accept iff a DIRECT
// variant is a float type. cstage type_assignable for an
// untyped_float src uses type_isfloat (type.c:376 — f32/f64 ONLY,
// NOT type_isnum), so an int/enum variant does NOT accept an
// untyped float. No direct float variant -> confident reject
// (cstage type.c:316 loop returns 0); ww does NOT flatten a
// nested-union float variant (#199-alpha). *confident is already
// true (:3777). Without it the catch-all (:3972) over-accepts an
// untyped float into ANY tagged (silent tag=0). Faithful
// flatten+rebox deferred post-CSP (nominal id, #23/#40).
if (du.kind == nkind.N_TTAGGED) {
let v: *node = du.list;
for (v != nil) {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (vu.kind == nkind.N_TNAME) {
if (streq(vu.str, "f32")) { return true; };
if (streq(vu.str, "f64")) { return true; };
};
};
v = v.next;
};
return false;
};
*confident = false;
return true;
};
@@ -14137,10 +14161,20 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
if (du.kind == nkind.N_TTAGGED) {
let v: *node = du.list;
for (v != nil) {
if (v.kind == nkind.N_TPTR) { return true; };
if (v.kind == nkind.N_TSLICE){ return true; };
if (v.kind == nkind.N_TPTR) { return true; };
if (v.kind == nkind.N_TSLICE) { return true; };
if (v.kind == nkind.N_TCHAN) { return true; };
if (v.kind == nkind.N_TFN) { return true; };
v = v.next;
};
// A5: no nullable (ptr/slice/chan/fn) variant -> confident
// reject (cstage type.c:316 loop returns 0; nil accepts only
// into ptr/slice/chan/fn per type.c:382-385). *confident is
// already true (:3777). Without it the catch-all (:3972)
// over-accepts nil into ANY tagged (silent). The trailing
// fallthrough below stays for a NON-tagged du (nil into a bare
// scalar — a separate non-family over-accept, SIBLINGS).
return false;
};
*confident = false;
return true;