wcc/ww: binoptype/unoptype operand-kind gates (arith/bitwise/ordered/logical)
wwstage ran no operand-kind check on binary/unary operators: str+str compiled to integer ADD on the 24B header (silent garbage). Gate each operator class on the operand kind, mirroring cstage check.c:1186-1238 wording; the pre-existing ptr-arith arm aligns to intkindast so ptr+untyped_int keeps compiling (byte-id-neutral, the over-reject the self-compile gate caught). New intkindast/numkindast/boolkindast predicates.
This commit is contained in:
@@ -12879,13 +12879,19 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let ltn: *node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *node = exprtype(c, e.rhs, nil);
|
||||
// #38/F2: ptr ± int / int + ptr use intkindast (the type_isint mirror,
|
||||
// incl untyped_int / untyped_rune / alias-chased) — NOT isinttypeast,
|
||||
// which misses untyped_int. cstage's ptr-arith arm gates on type_isint
|
||||
// (check.c:1179/1181), so `p + 1` (untyped_int) returns the ptr there
|
||||
// and never reaches the isnum gate; aligning these arms keeps the new
|
||||
// arithmetic gate below from rejecting valid pointer arithmetic.
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && isinttypeast(rtn)) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && intkindast(c, rtn)) {
|
||||
return ltn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS) {
|
||||
if (isinttypeast(ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
if (intkindast(c, ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
return rtn;
|
||||
};
|
||||
};
|
||||
@@ -12895,11 +12901,28 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
return mktname(c, "i64");
|
||||
};
|
||||
};
|
||||
// #38/F2 (review item 5): operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cbinop (cmd/wcc/check.c:1186-1199): arithmetic
|
||||
// wants numeric operands, bitwise wants integer operands. Without these
|
||||
// `let c: str = a + b;` (str+str) compiled to an integer ADD of the two
|
||||
// header pointers, silent garbage. The ptr-arithmetic special cases
|
||||
// above already returned, so a survivor here is plain value arithmetic.
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS ||
|
||||
op == tkind.TK_STAR || op == tkind.TK_SLASH ||
|
||||
op == tkind.TK_PERCENT || op == tkind.TK_AMP ||
|
||||
op == tkind.TK_PIPE || op == tkind.TK_CARET ||
|
||||
op == tkind.TK_LSHIFT || op == tkind.TK_RSHIFT) {
|
||||
op == tkind.TK_PERCENT) {
|
||||
if ((ltn != nil && !numkindast(c, ltn)) ||
|
||||
(rtn != nil && !numkindast(c, rtn))) {
|
||||
deffolderr(c, e, "arithmetic on non-numeric type");
|
||||
};
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_AMP || op == tkind.TK_PIPE ||
|
||||
op == tkind.TK_CARET || op == tkind.TK_LSHIFT ||
|
||||
op == tkind.TK_RSHIFT) {
|
||||
if ((ltn != nil && !intkindast(c, ltn)) ||
|
||||
(rtn != nil && !intkindast(c, rtn))) {
|
||||
deffolderr(c, e, "bitwise on non-integer type");
|
||||
};
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
|
||||
@@ -12918,9 +12941,30 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
deffolderr(c, e, "operands have differing types");
|
||||
};
|
||||
};
|
||||
// #38/F2 (review item 5): ordered comparisons want numeric
|
||||
// operands (cstage check.c:1198-1199). EQ/NEQ stay UNGATED —
|
||||
// cstage's cbinop equality arm (check.c:1194-1196) gates nothing,
|
||||
// so str==str / ptr==ptr remain valid; aligning those down would
|
||||
// over-reject what cstage accepts.
|
||||
if (op == tkind.TK_LT || op == tkind.TK_LE ||
|
||||
op == tkind.TK_GT || op == tkind.TK_GE) {
|
||||
if ((ltn != nil && !numkindast(c, ltn)) ||
|
||||
(rtn != nil && !numkindast(c, rtn))) {
|
||||
deffolderr(c, e, "ordered comparison on non-numeric");
|
||||
};
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
if (op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
// #38/F2 (review item 5): logical operands must be bool (cstage
|
||||
// check.c:1208-1211 gates each side via type_chase_named). cstage
|
||||
// formats per-side with tokname; ww's piecewise cerr can't splice
|
||||
// the op token, so one combined wording — the build-based reject
|
||||
// test gates on rc, not on exact text.
|
||||
if ((ltn != nil && !boolkindast(c, ltn)) ||
|
||||
(rtn != nil && !boolkindast(c, rtn))) {
|
||||
deffolderr(c, e, "logical operand is not bool");
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/
|
||||
@@ -12938,9 +12982,32 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) { return opt; };
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
// #38/F2 (review item 5): unop operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cunop (cmd/wcc/check.c:1224-1238): unary +/-
|
||||
// want a numeric operand, ~ wants an integer, ! wants a bool. A nil
|
||||
// opt is an already-errored / inherent-IDENT bail — not re-rejected.
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) {
|
||||
if (opt != nil && !numkindast(c, opt)) {
|
||||
if (op == tkind.TK_MINUS) {
|
||||
deffolderr(c, e, "- on non-numeric");
|
||||
} else {
|
||||
deffolderr(c, e, "+ on non-numeric");
|
||||
};
|
||||
};
|
||||
return opt;
|
||||
};
|
||||
if (op == tkind.TK_NOT) {
|
||||
if (opt != nil && !boolkindast(c, opt)) {
|
||||
deffolderr(c, e, "! on non-bool");
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
if (op == tkind.TK_TILDE) {
|
||||
if (opt != nil && !intkindast(c, opt)) {
|
||||
deffolderr(c, e, "~ on non-integer");
|
||||
};
|
||||
return opt;
|
||||
};
|
||||
if (op == tkind.TK_STAR) {
|
||||
// opt nil here means the operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE / pseudo-builtin
|
||||
@@ -14328,6 +14395,48 @@ fn isinttypeast(t: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// intkindast / numkindast / boolkindast — operand-kind classifiers for
|
||||
// binoptype/unoptype's cstage-mirrored gates (#38/F2 review item 5).
|
||||
// They resolvealias + unwrapbang to chase the alias (TY_NAMED) wrapper
|
||||
// before classifying — exactly as cstage's type_isint/type_isnum recurse
|
||||
// through TY_NAMED.under (cmd/wcc/type.c:178-180/192-193) and its AND/OR
|
||||
// arm chases via type_chase_named (check.c:1206-1207). Without the chase,
|
||||
// a `type myint = i32` operand would spuriously fail the gate that cstage
|
||||
// (chasing) accepts. nil operands are treated as already-errored upstream
|
||||
// (the unifyarith nil-bail shapes) and NOT re-rejected — the cstage twin's
|
||||
// `l == ty_err` escape.
|
||||
fn intkindast(c: *checker, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (isinttypeast(u)) { return true; }; // int family + enum + rune
|
||||
if (isuntypedint(u)) { return true; };
|
||||
if (u != nil && u.kind == nkind.N_TNAME && streq(u.str, "untyped_rune")) {
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn numkindast(c: *checker, t: *node) bool = {
|
||||
if (intkindast(c, t)) { return true; };
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != nkind.N_TNAME) { return false; };
|
||||
let s: str = u.str;
|
||||
if (streq(s, "f32")) { return true; };
|
||||
if (streq(s, "f64")) { return true; };
|
||||
if (streq(s, "untyped_float")) { return true; };
|
||||
return false;
|
||||
};
|
||||
|
||||
fn boolkindast(c: *checker, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(u.str, "bool") || streq(u.str, "untyped_bool");
|
||||
};
|
||||
|
||||
fn isnumerictname(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
|
||||
@@ -2459,13 +2459,19 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let ltn: *node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *node = exprtype(c, e.rhs, nil);
|
||||
// #38/F2: ptr ± int / int + ptr use intkindast (the type_isint mirror,
|
||||
// incl untyped_int / untyped_rune / alias-chased) — NOT isinttypeast,
|
||||
// which misses untyped_int. cstage's ptr-arith arm gates on type_isint
|
||||
// (check.c:1179/1181), so `p + 1` (untyped_int) returns the ptr there
|
||||
// and never reaches the isnum gate; aligning these arms keeps the new
|
||||
// arithmetic gate below from rejecting valid pointer arithmetic.
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && isinttypeast(rtn)) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && intkindast(c, rtn)) {
|
||||
return ltn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS) {
|
||||
if (isinttypeast(ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
if (intkindast(c, ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
return rtn;
|
||||
};
|
||||
};
|
||||
@@ -2475,11 +2481,28 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
return mktname(c, "i64");
|
||||
};
|
||||
};
|
||||
// #38/F2 (review item 5): operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cbinop (cmd/wcc/check.c:1186-1199): arithmetic
|
||||
// wants numeric operands, bitwise wants integer operands. Without these
|
||||
// `let c: str = a + b;` (str+str) compiled to an integer ADD of the two
|
||||
// header pointers, silent garbage. The ptr-arithmetic special cases
|
||||
// above already returned, so a survivor here is plain value arithmetic.
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS ||
|
||||
op == tkind.TK_STAR || op == tkind.TK_SLASH ||
|
||||
op == tkind.TK_PERCENT || op == tkind.TK_AMP ||
|
||||
op == tkind.TK_PIPE || op == tkind.TK_CARET ||
|
||||
op == tkind.TK_LSHIFT || op == tkind.TK_RSHIFT) {
|
||||
op == tkind.TK_PERCENT) {
|
||||
if ((ltn != nil && !numkindast(c, ltn)) ||
|
||||
(rtn != nil && !numkindast(c, rtn))) {
|
||||
deffolderr(c, e, "arithmetic on non-numeric type");
|
||||
};
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_AMP || op == tkind.TK_PIPE ||
|
||||
op == tkind.TK_CARET || op == tkind.TK_LSHIFT ||
|
||||
op == tkind.TK_RSHIFT) {
|
||||
if ((ltn != nil && !intkindast(c, ltn)) ||
|
||||
(rtn != nil && !intkindast(c, rtn))) {
|
||||
deffolderr(c, e, "bitwise on non-integer type");
|
||||
};
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
|
||||
@@ -2498,9 +2521,30 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
deffolderr(c, e, "operands have differing types");
|
||||
};
|
||||
};
|
||||
// #38/F2 (review item 5): ordered comparisons want numeric
|
||||
// operands (cstage check.c:1198-1199). EQ/NEQ stay UNGATED —
|
||||
// cstage's cbinop equality arm (check.c:1194-1196) gates nothing,
|
||||
// so str==str / ptr==ptr remain valid; aligning those down would
|
||||
// over-reject what cstage accepts.
|
||||
if (op == tkind.TK_LT || op == tkind.TK_LE ||
|
||||
op == tkind.TK_GT || op == tkind.TK_GE) {
|
||||
if ((ltn != nil && !numkindast(c, ltn)) ||
|
||||
(rtn != nil && !numkindast(c, rtn))) {
|
||||
deffolderr(c, e, "ordered comparison on non-numeric");
|
||||
};
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
if (op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
// #38/F2 (review item 5): logical operands must be bool (cstage
|
||||
// check.c:1208-1211 gates each side via type_chase_named). cstage
|
||||
// formats per-side with tokname; ww's piecewise cerr can't splice
|
||||
// the op token, so one combined wording — the build-based reject
|
||||
// test gates on rc, not on exact text.
|
||||
if ((ltn != nil && !boolkindast(c, ltn)) ||
|
||||
(rtn != nil && !boolkindast(c, rtn))) {
|
||||
deffolderr(c, e, "logical operand is not bool");
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/
|
||||
@@ -2518,9 +2562,32 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) { return opt; };
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
// #38/F2 (review item 5): unop operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cunop (cmd/wcc/check.c:1224-1238): unary +/-
|
||||
// want a numeric operand, ~ wants an integer, ! wants a bool. A nil
|
||||
// opt is an already-errored / inherent-IDENT bail — not re-rejected.
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) {
|
||||
if (opt != nil && !numkindast(c, opt)) {
|
||||
if (op == tkind.TK_MINUS) {
|
||||
deffolderr(c, e, "- on non-numeric");
|
||||
} else {
|
||||
deffolderr(c, e, "+ on non-numeric");
|
||||
};
|
||||
};
|
||||
return opt;
|
||||
};
|
||||
if (op == tkind.TK_NOT) {
|
||||
if (opt != nil && !boolkindast(c, opt)) {
|
||||
deffolderr(c, e, "! on non-bool");
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
if (op == tkind.TK_TILDE) {
|
||||
if (opt != nil && !intkindast(c, opt)) {
|
||||
deffolderr(c, e, "~ on non-integer");
|
||||
};
|
||||
return opt;
|
||||
};
|
||||
if (op == tkind.TK_STAR) {
|
||||
// opt nil here means the operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE / pseudo-builtin
|
||||
@@ -3908,6 +3975,48 @@ fn isinttypeast(t: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// intkindast / numkindast / boolkindast — operand-kind classifiers for
|
||||
// binoptype/unoptype's cstage-mirrored gates (#38/F2 review item 5).
|
||||
// They resolvealias + unwrapbang to chase the alias (TY_NAMED) wrapper
|
||||
// before classifying — exactly as cstage's type_isint/type_isnum recurse
|
||||
// through TY_NAMED.under (cmd/wcc/type.c:178-180/192-193) and its AND/OR
|
||||
// arm chases via type_chase_named (check.c:1206-1207). Without the chase,
|
||||
// a `type myint = i32` operand would spuriously fail the gate that cstage
|
||||
// (chasing) accepts. nil operands are treated as already-errored upstream
|
||||
// (the unifyarith nil-bail shapes) and NOT re-rejected — the cstage twin's
|
||||
// `l == ty_err` escape.
|
||||
fn intkindast(c: *checker, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (isinttypeast(u)) { return true; }; // int family + enum + rune
|
||||
if (isuntypedint(u)) { return true; };
|
||||
if (u != nil && u.kind == nkind.N_TNAME && streq(u.str, "untyped_rune")) {
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn numkindast(c: *checker, t: *node) bool = {
|
||||
if (intkindast(c, t)) { return true; };
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != nkind.N_TNAME) { return false; };
|
||||
let s: str = u.str;
|
||||
if (streq(s, "f32")) { return true; };
|
||||
if (streq(s, "f64")) { return true; };
|
||||
if (streq(s, "untyped_float")) { return true; };
|
||||
return false;
|
||||
};
|
||||
|
||||
fn boolkindast(c: *checker, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(u.str, "bool") || streq(u.str, "untyped_bool");
|
||||
};
|
||||
|
||||
fn isnumerictname(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
|
||||
@@ -12879,13 +12879,19 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let ltn: *node = exprtype(c, e.lhs, nil);
|
||||
let rtn: *node = exprtype(c, e.rhs, nil);
|
||||
// #38/F2: ptr ± int / int + ptr use intkindast (the type_isint mirror,
|
||||
// incl untyped_int / untyped_rune / alias-chased) — NOT isinttypeast,
|
||||
// which misses untyped_int. cstage's ptr-arith arm gates on type_isint
|
||||
// (check.c:1179/1181), so `p + 1` (untyped_int) returns the ptr there
|
||||
// and never reaches the isnum gate; aligning these arms keeps the new
|
||||
// arithmetic gate below from rejecting valid pointer arithmetic.
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && isinttypeast(rtn)) {
|
||||
if (ltn != nil && ltn.kind == nkind.N_TPTR && intkindast(c, rtn)) {
|
||||
return ltn;
|
||||
};
|
||||
};
|
||||
if (op == tkind.TK_PLUS) {
|
||||
if (isinttypeast(ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
if (intkindast(c, ltn) && rtn != nil && rtn.kind == nkind.N_TPTR) {
|
||||
return rtn;
|
||||
};
|
||||
};
|
||||
@@ -12895,11 +12901,28 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
return mktname(c, "i64");
|
||||
};
|
||||
};
|
||||
// #38/F2 (review item 5): operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cbinop (cmd/wcc/check.c:1186-1199): arithmetic
|
||||
// wants numeric operands, bitwise wants integer operands. Without these
|
||||
// `let c: str = a + b;` (str+str) compiled to an integer ADD of the two
|
||||
// header pointers, silent garbage. The ptr-arithmetic special cases
|
||||
// above already returned, so a survivor here is plain value arithmetic.
|
||||
if (op == tkind.TK_PLUS || op == tkind.TK_MINUS ||
|
||||
op == tkind.TK_STAR || op == tkind.TK_SLASH ||
|
||||
op == tkind.TK_PERCENT || op == tkind.TK_AMP ||
|
||||
op == tkind.TK_PIPE || op == tkind.TK_CARET ||
|
||||
op == tkind.TK_LSHIFT || op == tkind.TK_RSHIFT) {
|
||||
op == tkind.TK_PERCENT) {
|
||||
if ((ltn != nil && !numkindast(c, ltn)) ||
|
||||
(rtn != nil && !numkindast(c, rtn))) {
|
||||
deffolderr(c, e, "arithmetic on non-numeric type");
|
||||
};
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_AMP || op == tkind.TK_PIPE ||
|
||||
op == tkind.TK_CARET || op == tkind.TK_LSHIFT ||
|
||||
op == tkind.TK_RSHIFT) {
|
||||
if ((ltn != nil && !intkindast(c, ltn)) ||
|
||||
(rtn != nil && !intkindast(c, rtn))) {
|
||||
deffolderr(c, e, "bitwise on non-integer type");
|
||||
};
|
||||
return unifyarith(c, ltn, rtn);
|
||||
};
|
||||
if (op == tkind.TK_EQ || op == tkind.TK_NEQ ||
|
||||
@@ -12918,9 +12941,30 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
deffolderr(c, e, "operands have differing types");
|
||||
};
|
||||
};
|
||||
// #38/F2 (review item 5): ordered comparisons want numeric
|
||||
// operands (cstage check.c:1198-1199). EQ/NEQ stay UNGATED —
|
||||
// cstage's cbinop equality arm (check.c:1194-1196) gates nothing,
|
||||
// so str==str / ptr==ptr remain valid; aligning those down would
|
||||
// over-reject what cstage accepts.
|
||||
if (op == tkind.TK_LT || op == tkind.TK_LE ||
|
||||
op == tkind.TK_GT || op == tkind.TK_GE) {
|
||||
if ((ltn != nil && !numkindast(c, ltn)) ||
|
||||
(rtn != nil && !numkindast(c, rtn))) {
|
||||
deffolderr(c, e, "ordered comparison on non-numeric");
|
||||
};
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
if (op == tkind.TK_AND || op == tkind.TK_OR) {
|
||||
// #38/F2 (review item 5): logical operands must be bool (cstage
|
||||
// check.c:1208-1211 gates each side via type_chase_named). cstage
|
||||
// formats per-side with tokname; ww's piecewise cerr can't splice
|
||||
// the op token, so one combined wording — the build-based reject
|
||||
// test gates on rc, not on exact text.
|
||||
if ((ltn != nil && !boolkindast(c, ltn)) ||
|
||||
(rtn != nil && !boolkindast(c, rtn))) {
|
||||
deffolderr(c, e, "logical operand is not bool");
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
// Unreachable for valid input: op is one of TK_PLUS/MINUS/STAR/
|
||||
@@ -12938,9 +12982,32 @@ fn binoptype(c: *checker, e: *node) *node = {
|
||||
fn unoptype(c: *checker, e: *node) *node = {
|
||||
let op: tkind = e.op;
|
||||
let opt: *node = exprtype(c, e.lhs, nil);
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) { return opt; };
|
||||
if (op == tkind.TK_NOT) { return mktname(c, "bool"); };
|
||||
if (op == tkind.TK_TILDE) { return opt; };
|
||||
// #38/F2 (review item 5): unop operand-kind gates the wwstage checker
|
||||
// elided. Mirror cstage cunop (cmd/wcc/check.c:1224-1238): unary +/-
|
||||
// want a numeric operand, ~ wants an integer, ! wants a bool. A nil
|
||||
// opt is an already-errored / inherent-IDENT bail — not re-rejected.
|
||||
if (op == tkind.TK_MINUS || op == tkind.TK_PLUS) {
|
||||
if (opt != nil && !numkindast(c, opt)) {
|
||||
if (op == tkind.TK_MINUS) {
|
||||
deffolderr(c, e, "- on non-numeric");
|
||||
} else {
|
||||
deffolderr(c, e, "+ on non-numeric");
|
||||
};
|
||||
};
|
||||
return opt;
|
||||
};
|
||||
if (op == tkind.TK_NOT) {
|
||||
if (opt != nil && !boolkindast(c, opt)) {
|
||||
deffolderr(c, e, "! on non-bool");
|
||||
};
|
||||
return mktname(c, "bool");
|
||||
};
|
||||
if (op == tkind.TK_TILDE) {
|
||||
if (opt != nil && !intkindast(c, opt)) {
|
||||
deffolderr(c, e, "~ on non-integer");
|
||||
};
|
||||
return opt;
|
||||
};
|
||||
if (op == tkind.TK_STAR) {
|
||||
// opt nil here means the operand was an inherent-IDENT bail
|
||||
// (exprtype N_IDENT arm L1596-1599 — SK_USE / pseudo-builtin
|
||||
@@ -14328,6 +14395,48 @@ fn isinttypeast(t: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// intkindast / numkindast / boolkindast — operand-kind classifiers for
|
||||
// binoptype/unoptype's cstage-mirrored gates (#38/F2 review item 5).
|
||||
// They resolvealias + unwrapbang to chase the alias (TY_NAMED) wrapper
|
||||
// before classifying — exactly as cstage's type_isint/type_isnum recurse
|
||||
// through TY_NAMED.under (cmd/wcc/type.c:178-180/192-193) and its AND/OR
|
||||
// arm chases via type_chase_named (check.c:1206-1207). Without the chase,
|
||||
// a `type myint = i32` operand would spuriously fail the gate that cstage
|
||||
// (chasing) accepts. nil operands are treated as already-errored upstream
|
||||
// (the unifyarith nil-bail shapes) and NOT re-rejected — the cstage twin's
|
||||
// `l == ty_err` escape.
|
||||
fn intkindast(c: *checker, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (isinttypeast(u)) { return true; }; // int family + enum + rune
|
||||
if (isuntypedint(u)) { return true; };
|
||||
if (u != nil && u.kind == nkind.N_TNAME && streq(u.str, "untyped_rune")) {
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn numkindast(c: *checker, t: *node) bool = {
|
||||
if (intkindast(c, t)) { return true; };
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != nkind.N_TNAME) { return false; };
|
||||
let s: str = u.str;
|
||||
if (streq(s, "f32")) { return true; };
|
||||
if (streq(s, "f64")) { return true; };
|
||||
if (streq(s, "untyped_float")) { return true; };
|
||||
return false;
|
||||
};
|
||||
|
||||
fn boolkindast(c: *checker, t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let u: *node = resolvealias(c, unwrapbang(t));
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != nkind.N_TNAME) { return false; };
|
||||
return streq(u.str, "bool") || streq(u.str, "untyped_bool");
|
||||
};
|
||||
|
||||
fn isnumerictname(t: *node) bool = {
|
||||
if (t == nil) { return false; };
|
||||
if (t.kind != nkind.N_TNAME) { return false; };
|
||||
|
||||
Reference in New Issue
Block a user