wwstage: reject error-type vs int comparison (#246)

cstage cbinop routes every comparison through unify_arith
(cmd/wcc/check.c:952), which loud-rejects an error-typed operand
paired with a differing type (e.g. strconv.invalid != i32). wwstage
binoptype returned bool for comparisons without any unify step, so it
silently accepted a program cstage rejects -- a rule-10 break (align
the leaner-but-leniner wwstage DOWN to cstage).

Scope the rejection to an error operand (varianterr) mismatched with
the other (typeeqast) so the broad differing-types diagnostic -- whose
typeeqast-vs-cstage-type_eq asymmetry risk could reject valid bootstrap
code -- stays out of wwstage. Covers the whole comparison family
(EQ/NEQ/LT/LE/GT/GE), all of which cstage routes through unify_arith.

Found by impl-strconv3 writing the strconv test. Gate-blind: the
bootstrap never compares an error type to an int, so byte-id stayed
green while the stages disagreed on what's a valid program.

test/wcc/949_errtype_compare.c: both drivers reject invalid !=/==/< i32
(K_BUILDERR); same-error-type and plain-int compares still accept on
both stages + cs==ww byte-id (K_RUN). 12/12.
This commit is contained in:
2026-06-01 23:10:46 +09:00
parent a11785273a
commit dbc169a025
5 changed files with 333 additions and 6 deletions

View File

@@ -12384,8 +12384,23 @@ fn binoptype(c: *checker, e: *node) *node = {
};
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
op == tkind.TK_LT || op == tkind.TK_LE ||
op == tkind.TK_GT || op == tkind.TK_GE ||
op == tkind.TK_AND || op == tkind.TK_OR) {
op == tkind.TK_GT || op == tkind.TK_GE) {
// cstage routes every comparison through unify_arith (check.c:952/
// 955 cbinop), which loud-rejects an error-typed operand paired
// with a differing type, e.g. strconv.invalid != i32. wwstage's
// unifyarith stays silent on generic typed mismatches (5-lite-b),
// but the error-type case is a real cs!=ww edge and must reject to
// match cstage (#246, rule 10). Scoped to error operands so the
// broad differing-types flip (typeeqast vs cstage type_eq
// asymmetry risk) stays out.
if (varianterr(c, ltn) || varianterr(c, rtn)) {
if (!typeeqast(ltn, rtn)) {
deffolderr(c, e, "operands have differing types");
};
};
return mktname(c, "bool");
};
if (op == tkind.TK_AND || op == tkind.TK_OR) {
return mktname(c, "bool");
};
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/